Merge commit '16d7853b4efbf173967166881aabbe0ee14ae74b'

This commit is contained in:
Peter 2019-06-26 16:20:56 +03:00
commit 2a0e482bd3
84 changed files with 2970 additions and 329 deletions

View File

@ -173,6 +173,18 @@
09C5732D2172953900BDF00F /* TGBridgeLocationVenue+TGTableItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 09C572FF2172953600BDF00F /* TGBridgeLocationVenue+TGTableItem.m */; };
09CFB212217299E80083F7A3 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09CFB211217299E80083F7A3 /* CoreLocation.framework */; };
09D30420217418EC00C00567 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D09DCBB51D0C856B00F51FFE /* Localizable.strings */; };
09E9600722C23FF200B13673 /* BlackNotificationIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600122C23FF000B13673 /* BlackNotificationIcon@3x.png */; };
09E9600822C23FF200B13673 /* BlackNotificationIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600222C23FF000B13673 /* BlackNotificationIcon.png */; };
09E9600922C23FF200B13673 /* BlueNotificationIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600322C23FF000B13673 /* BlueNotificationIcon.png */; };
09E9600A22C23FF200B13673 /* BlueNotificationIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600422C23FF100B13673 /* BlueNotificationIcon@2x.png */; };
09E9600B22C23FF200B13673 /* BlackNotificationIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600522C23FF100B13673 /* BlackNotificationIcon@2x.png */; };
09E9600C22C23FF200B13673 /* BlueNotificationIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600622C23FF100B13673 /* BlueNotificationIcon@3x.png */; };
09E9601322C2441000B13673 /* BlackClassicNotificationIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600D22C2440E00B13673 /* BlackClassicNotificationIcon@2x.png */; };
09E9601422C2441000B13673 /* BlackClassicNotificationIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600E22C2440E00B13673 /* BlackClassicNotificationIcon@3x.png */; };
09E9601522C2441000B13673 /* BlueClassicNotificationIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9600F22C2440F00B13673 /* BlueClassicNotificationIcon.png */; };
09E9601622C2441000B13673 /* BlueClassicNotificationIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9601022C2440F00B13673 /* BlueClassicNotificationIcon@2x.png */; };
09E9601722C2441000B13673 /* BlackClassicNotificationIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9601122C2440F00B13673 /* BlackClassicNotificationIcon.png */; };
09E9601822C2441000B13673 /* BlueClassicNotificationIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09E9601222C2441000B13673 /* BlueClassicNotificationIcon@3x.png */; };
09EBE2A522B004EA00F670AB /* BlueFilledIconIpad.png in Resources */ = {isa = PBXBuildFile; fileRef = 09EBE29922B004E800F670AB /* BlueFilledIconIpad.png */; };
09EBE2A622B004EA00F670AB /* BlueIconIpad@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 09EBE29A22B004E800F670AB /* BlueIconIpad@2x.png */; };
09EBE2A722B004EA00F670AB /* BlueIconIpad.png in Resources */ = {isa = PBXBuildFile; fileRef = 09EBE29B22B004E900F670AB /* BlueIconIpad.png */; };
@ -803,6 +815,18 @@
09C573152172953800BDF00F /* TGBridgeLocationVenue+TGTableItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TGBridgeLocationVenue+TGTableItem.h"; path = "Bridge/TGBridgeLocationVenue+TGTableItem.h"; sourceTree = "<group>"; };
09CFB211217299E80083F7A3 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS5.0.sdk/System/Library/Frameworks/CoreLocation.framework; sourceTree = DEVELOPER_DIR; };
09D304212174335F00C00567 /* WatchBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchBridge.swift; sourceTree = "<group>"; };
09E9600122C23FF000B13673 /* BlackNotificationIcon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlackNotificationIcon@3x.png"; sourceTree = "<group>"; };
09E9600222C23FF000B13673 /* BlackNotificationIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlackNotificationIcon.png; sourceTree = "<group>"; };
09E9600322C23FF000B13673 /* BlueNotificationIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlueNotificationIcon.png; sourceTree = "<group>"; };
09E9600422C23FF100B13673 /* BlueNotificationIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlueNotificationIcon@2x.png"; sourceTree = "<group>"; };
09E9600522C23FF100B13673 /* BlackNotificationIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlackNotificationIcon@2x.png"; sourceTree = "<group>"; };
09E9600622C23FF100B13673 /* BlueNotificationIcon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlueNotificationIcon@3x.png"; sourceTree = "<group>"; };
09E9600D22C2440E00B13673 /* BlackClassicNotificationIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlackClassicNotificationIcon@2x.png"; sourceTree = "<group>"; };
09E9600E22C2440E00B13673 /* BlackClassicNotificationIcon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlackClassicNotificationIcon@3x.png"; sourceTree = "<group>"; };
09E9600F22C2440F00B13673 /* BlueClassicNotificationIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlueClassicNotificationIcon.png; sourceTree = "<group>"; };
09E9601022C2440F00B13673 /* BlueClassicNotificationIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlueClassicNotificationIcon@2x.png"; sourceTree = "<group>"; };
09E9601122C2440F00B13673 /* BlackClassicNotificationIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlackClassicNotificationIcon.png; sourceTree = "<group>"; };
09E9601222C2441000B13673 /* BlueClassicNotificationIcon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlueClassicNotificationIcon@3x.png"; sourceTree = "<group>"; };
09EBE29922B004E800F670AB /* BlueFilledIconIpad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlueFilledIconIpad.png; sourceTree = "<group>"; };
09EBE29A22B004E800F670AB /* BlueIconIpad@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlueIconIpad@2x.png"; sourceTree = "<group>"; };
09EBE29B22B004E900F670AB /* BlueIconIpad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlueIconIpad.png; sourceTree = "<group>"; };
@ -1198,6 +1222,18 @@
09A218E122A14E5600DE6898 /* App Icons */ = {
isa = PBXGroup;
children = (
09E9601122C2440F00B13673 /* BlackClassicNotificationIcon.png */,
09E9600D22C2440E00B13673 /* BlackClassicNotificationIcon@2x.png */,
09E9600E22C2440E00B13673 /* BlackClassicNotificationIcon@3x.png */,
09E9600F22C2440F00B13673 /* BlueClassicNotificationIcon.png */,
09E9601022C2440F00B13673 /* BlueClassicNotificationIcon@2x.png */,
09E9601222C2441000B13673 /* BlueClassicNotificationIcon@3x.png */,
09E9600222C23FF000B13673 /* BlackNotificationIcon.png */,
09E9600522C23FF100B13673 /* BlackNotificationIcon@2x.png */,
09E9600122C23FF000B13673 /* BlackNotificationIcon@3x.png */,
09E9600322C23FF000B13673 /* BlueNotificationIcon.png */,
09E9600422C23FF100B13673 /* BlueNotificationIcon@2x.png */,
09E9600622C23FF100B13673 /* BlueNotificationIcon@3x.png */,
09A4192E22B7A4D400637EB4 /* BlackClassicIconIpad.png */,
09A4192F22B7A4D400637EB4 /* BlackClassicIconIpad@2x.png */,
09A4192B22B7A4D400637EB4 /* BlackClassicIconLargeIpad@2x.png */,
@ -2681,11 +2717,13 @@
D04DCC211F71C80000B021D7 /* 0.m4a in Resources */,
D04DCC261F71C80000B021D7 /* 103.m4a in Resources */,
09A218F222A1570A00DE6898 /* BlueFilledIcon@2x.png in Resources */,
09E9600922C23FF200B13673 /* BlueNotificationIcon.png in Resources */,
090E777722A6945900CD99F5 /* BlueClassicIcon@2x.png in Resources */,
D0CE6F69213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
D08DB0C0213F4D1D00F2ADBF /* telegram_sphere@2x.png in Resources */,
D08DB0AB213F4D1D00F2ADBF /* ic_bubble@2x.png in Resources */,
D04DCC341F71C80000B021D7 /* 7.m4a in Resources */,
09E9600722C23FF200B13673 /* BlackNotificationIcon@3x.png in Resources */,
09EBE2A522B004EA00F670AB /* BlueFilledIconIpad.png in Resources */,
D0CE6F60213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
D0CE6F63213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
@ -2698,8 +2736,12 @@
D0E8B8AE2044496C00605593 /* voip_end.caf in Resources */,
D0CE6F68213EDA4400BCD44B /* InfoPlist.strings in Resources */,
09A218EE22A1570A00DE6898 /* BlueFilledIcon@3x.png in Resources */,
09E9600C22C23FF200B13673 /* BlueNotificationIcon@3x.png in Resources */,
09E9601422C2441000B13673 /* BlackClassicNotificationIcon@3x.png in Resources */,
D0CE6F6A213EDA4400BCD44B /* Localizable.strings in Resources */,
09A4193522B7A4D500637EB4 /* BlackClassicIconIpad@2x.png in Resources */,
09E9601622C2441000B13673 /* BlueClassicNotificationIcon@2x.png in Resources */,
09E9601322C2441000B13673 /* BlackClassicNotificationIcon@2x.png in Resources */,
D0E8C2DE2285EA55009F26E8 /* BlackIcon@2x.png in Resources */,
D09DCBB71D0C856B00F51FFE /* Localizable.strings in Resources */,
D0CE6F66213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
@ -2718,9 +2760,11 @@
D04DCC2C1F71C80000B021D7 /* 109.m4a in Resources */,
D08DB0BD213F4D1D00F2ADBF /* start_arrow_ipad.png in Resources */,
09EBE2AB22B004EA00F670AB /* BlackIconLargeIpad@2x.png in Resources */,
09E9601722C2441000B13673 /* BlackClassicNotificationIcon.png in Resources */,
D0CE6F64213EDA4400BCD44B /* Localizable.strings in Resources */,
D0CE6F6C213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
D04DCC231F71C80000B021D7 /* 100.m4a in Resources */,
09E9600A22C23FF200B13673 /* BlueNotificationIcon@2x.png in Resources */,
09A218EF22A1570A00DE6898 /* BlueIcon@2x.png in Resources */,
D04DCC281F71C80000B021D7 /* 105.m4a in Resources */,
D08DB0BB213F4D1D00F2ADBF /* private_screw@2x.png in Resources */,
@ -2742,6 +2786,7 @@
D0CE6F59213EDA4400BCD44B /* InfoPlist.strings in Resources */,
D08DB0B9213F4D1D00F2ADBF /* powerful_star@2x.png in Resources */,
D04DCC271F71C80000B021D7 /* 104.m4a in Resources */,
09E9600B22C23FF200B13673 /* BlackNotificationIcon@2x.png in Resources */,
090E777522A6945900CD99F5 /* BlueClassicIcon@3x.png in Resources */,
09A218F122A1570A00DE6898 /* BlackFilledIcon@3x.png in Resources */,
D04DCC2A1F71C80000B021D7 /* 107.m4a in Resources */,
@ -2754,6 +2799,7 @@
09EBE2B022B004EA00F670AB /* BlueFilledIconLargeIpad@2x.png in Resources */,
D0CE6F58213EDA4400BCD44B /* Localizable.strings in Resources */,
D08DB0AF213F4D1D00F2ADBF /* ic_pencil@2x.png in Resources */,
09E9601822C2441000B13673 /* BlueClassicNotificationIcon@3x.png in Resources */,
D0CE6F67213EDA4400BCD44B /* Localizable.strings in Resources */,
09A4193322B7A4D500637EB4 /* BlueClassicIconLargeIpad@2x.png in Resources */,
D04DCC291F71C80000B021D7 /* 106.m4a in Resources */,
@ -2787,11 +2833,13 @@
090E777622A6945900CD99F5 /* BlackClassicIcon@3x.png in Resources */,
D08DB0BF213F4D1D00F2ADBF /* telegram_plane1@2x.png in Resources */,
D0E8B8B02044496C00605593 /* voip_ringback.caf in Resources */,
09E9600822C23FF200B13673 /* BlackNotificationIcon.png in Resources */,
09EBE2A922B004EA00F670AB /* BlackFilledIconLargeIpad@2x.png in Resources */,
090E777422A6945900CD99F5 /* BlackClassicIcon@2x.png in Resources */,
D00ED75A1FE94630001F38BD /* AppIntentVocabulary.plist in Resources */,
D04DCC221F71C80000B021D7 /* 1.m4a in Resources */,
09EBE2AD22B004EA00F670AB /* BlackFilledIconIpad.png in Resources */,
09E9601522C2441000B13673 /* BlueClassicNotificationIcon.png in Resources */,
D0E8C2E02285EA6A009F26E8 /* BlackIcon@3x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -19,6 +19,7 @@
<key>CFBundleIconFiles</key>
<array>
<string>BlackIcon</string>
<string>BlackNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
@ -28,6 +29,7 @@
<key>CFBundleIconFiles</key>
<array>
<string>BlackClassicIcon</string>
<string>BlackClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
@ -46,6 +48,7 @@
<key>CFBundleIconFiles</key>
<array>
<string>BlueIcon</string>
<string>BlueNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
@ -55,6 +58,7 @@
<key>CFBundleIconFiles</key>
<array>
<string>BlueClassicIcon</string>
<string>BlueClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
@ -96,6 +100,7 @@
<array>
<string>BlackIconIpad</string>
<string>BlackIconLargeIpad</string>
<string>BlackNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
@ -106,6 +111,7 @@
<array>
<string>BlackClassicIconIpad</string>
<string>BlackClassicIconLargeIpad</string>
<string>BlackClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
@ -126,6 +132,7 @@
<array>
<string>BlueIconIpad</string>
<string>BlueIconLargeIpad</string>
<string>BlueNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
@ -136,6 +143,7 @@
<array>
<string>BlueClassicIconIpad</string>
<string>BlueClassicIconLargeIpad</string>
<string>BlueClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>

View File

@ -179,11 +179,11 @@ public final class DeviceAccess {
func statusForCellularState(_ state: CTCellularDataRestrictedState) -> AccessType? {
switch state {
case .restricted:
return .denied
return .allowed
case .notRestricted:
return .allowed
default:
return nil
return .allowed
}
}
let cellState = CTCellularData.init()
@ -196,7 +196,7 @@ public final class DeviceAccess {
}
}
} else {
subscriber.putNext(.notDetermined)
subscriber.putNext(.allowed)
subscriber.putCompletion()
}
return EmptyDisposable
@ -446,8 +446,12 @@ public final class DeviceAccess {
completion(result)
}
}
default:
break
case .cellularData:
if let presentationData = presentationData {
present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Permissions_CellularDataTitle_v0, text: presentationData.strings.Permissions_CellularDataText_v0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
openSettings()
})]), nil)
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
09E9601B22C2BE4900B13673 /* SecretApiLayer101.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09E9601A22C2BE4900B13673 /* SecretApiLayer101.swift */; };
D035732422B5C1FC00F0920D /* TelegramApi.h in Headers */ = {isa = PBXBuildFile; fileRef = D035732222B5C1FC00F0920D /* TelegramApi.h */; settings = {ATTRIBUTES = (Public, ); }; };
D035733422B5C29900F0920D /* Api0.swift in Sources */ = {isa = PBXBuildFile; fileRef = D035733022B5C29900F0920D /* Api0.swift */; };
D035733522B5C29900F0920D /* Api2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D035733122B5C29900F0920D /* Api2.swift */; };
@ -32,6 +33,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
09E9601A22C2BE4900B13673 /* SecretApiLayer101.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretApiLayer101.swift; sourceTree = "<group>"; };
D035731F22B5C1FC00F0920D /* TelegramApi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TelegramApi.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D035732222B5C1FC00F0920D /* TelegramApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TelegramApi.h; sourceTree = "<group>"; };
D035732322B5C1FC00F0920D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -94,6 +96,7 @@
D035734522B5C9BF00F0920D /* SecretApiLayer8.swift */,
D035734422B5C9BF00F0920D /* SecretApiLayer46.swift */,
D035734622B5C9BF00F0920D /* SecretApiLayer73.swift */,
09E9601A22C2BE4900B13673 /* SecretApiLayer101.swift */,
D035733C22B5C39100F0920D /* DeserializeFunctionResponse.swift */,
D035733A22B5C31400F0920D /* TelegramApiLogger.swift */,
D035733822B5C2E200F0920D /* Buffer.swift */,
@ -230,6 +233,7 @@
D035733D22B5C39100F0920D /* DeserializeFunctionResponse.swift in Sources */,
D035734822B5C9BF00F0920D /* SecretApiLayer8.swift in Sources */,
D035733522B5C29900F0920D /* Api2.swift in Sources */,
09E9601B22C2BE4900B13673 /* SecretApiLayer101.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -19,6 +19,7 @@ public enum CreateChannelError {
case generic
case restricted
case tooMuchLocationBasedGroups
case serverProvided(String)
}
private func createChannel(account: Account, title: String, description: String?, isSupergroup:Bool, location: (latitude: Double, longitude: Double, address: String)? = nil) -> Signal<PeerId, CreateChannelError> {
@ -40,7 +41,9 @@ private func createChannel(account: Account, title: String, description: String?
return account.network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address), automaticFloodWait: false)
|> mapError { error -> CreateChannelError in
if error.errorDescription == "CHANNELS_ADMIN_LOCATED_TOO_MUCH" {
if error.errorCode == 406 {
return .serverProvided(error.errorDescription)
} else if error.errorDescription == "CHANNELS_ADMIN_LOCATED_TOO_MUCH" {
return .tooMuchLocationBasedGroups
} else if error.errorDescription == "USER_RESTRICTED" {
return .restricted

View File

@ -20,6 +20,7 @@ public enum CreateGroupError {
case privacy
case restricted
case tooMuchLocationBasedGroups
case serverProvided(String)
}
public func createGroup(account: Account, title: String, peerIds: [PeerId]) -> Signal<PeerId?, CreateGroupError> {

View File

@ -361,6 +361,7 @@ private enum BoxedDecryptedMessage {
case layer8(SecretApi8.DecryptedMessage)
case layer46(SecretApi46.DecryptedMessage)
case layer73(SecretApi73.DecryptedMessage)
case layer101(SecretApi101.DecryptedMessage)
func serialize(_ buffer: Buffer, role: SecretChatRole, sequenceInfo: SecretChatOperationSequenceInfo?) {
switch self {
@ -405,6 +406,26 @@ private enum BoxedDecryptedMessage {
assertionFailure()
}
let _ = message.serialize(buffer, true)
case let .layer101(message):
buffer.appendInt32(0x1be31789)
let randomBytes = malloc(15)!
arc4random_buf(randomBytes, 15)
serializeBytes(Buffer(memory: randomBytes, size: 15, capacity: 15, freeWhenDone: false), buffer: buffer, boxed: false)
free(randomBytes)
buffer.appendInt32(101)
if let sequenceInfo = sequenceInfo {
let inSeqNo = (sequenceInfo.topReceivedOperationIndex + 1) * 2 + (role == .creator ? 0 : 1)
let outSeqNo = sequenceInfo.operationIndex * 2 + (role == .creator ? 1 : 0)
buffer.appendInt32(inSeqNo)
buffer.appendInt32(outSeqNo)
} else {
buffer.appendInt32(0)
buffer.appendInt32(0)
assertionFailure()
}
let _ = message.serialize(buffer, true)
}
}
@ -567,6 +588,59 @@ private func decryptedAttributes73(_ attributes: [TelegramMediaFileAttribute], t
return result
}
private func decryptedAttributes101(_ attributes: [TelegramMediaFileAttribute], transaction: Transaction) -> [SecretApi101.DocumentAttribute] {
var result: [SecretApi101.DocumentAttribute] = []
for attribute in attributes {
switch attribute {
case let .FileName(fileName):
result.append(.documentAttributeFilename(fileName: fileName))
case .Animated:
result.append(.documentAttributeAnimated)
case let .Sticker(displayText, packReference, _):
var stickerSet: SecretApi101.InputStickerSet = .inputStickerSetEmpty
if let packReference = packReference {
switch packReference {
case let .name(name):
stickerSet = .inputStickerSetShortName(shortName: name)
case .id:
if let (info, _, _) = cachedStickerPack(transaction: transaction, reference: packReference) {
stickerSet = .inputStickerSetShortName(shortName: info.shortName)
}
}
}
result.append(.documentAttributeSticker(alt: displayText, stickerset: stickerSet))
case let .ImageSize(size):
result.append(.documentAttributeImageSize(w: Int32(size.width), h: Int32(size.height)))
case let .Video(duration, size, videoFlags):
var flags: Int32 = 0
if videoFlags.contains(.instantRoundVideo) {
flags |= 1 << 0
}
result.append(.documentAttributeVideo(flags: flags, duration: Int32(duration), w: Int32(size.width), h: Int32(size.height)))
case let .Audio(isVoice, duration, title, performer, waveform):
var flags: Int32 = 0
if isVoice {
flags |= (1 << 10)
}
if let _ = title {
flags |= Int32(1 << 0)
}
if let _ = performer {
flags |= Int32(1 << 1)
}
var waveformBuffer: Buffer?
if let waveform = waveform {
flags |= Int32(1 << 2)
waveformBuffer = Buffer(data: waveform.makeData())
}
result.append(.documentAttributeAudio(flags: flags, duration: Int32(duration), title: title, performer: performer, waveform: waveformBuffer))
case .HasLinkedStickers:
break
}
}
return result
}
private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi73.MessageEntity]? {
guard let entities = entities else {
return nil
@ -614,6 +688,53 @@ private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi
return result
}
private func decryptedEntities101(_ entities: [MessageTextEntity]?) -> [SecretApi101.MessageEntity]? {
guard let entities = entities else {
return nil
}
var result: [SecretApi101.MessageEntity] = []
for entity in entities {
switch entity.type {
case .Unknown:
break
case .Mention:
result.append(.messageEntityMention(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Hashtag:
result.append(.messageEntityHashtag(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .BotCommand:
result.append(.messageEntityBotCommand(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Url:
result.append(.messageEntityUrl(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Email:
result.append(.messageEntityEmail(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Bold:
result.append(.messageEntityBold(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Italic:
result.append(.messageEntityItalic(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Code:
result.append(.messageEntityCode(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Pre:
result.append(.messageEntityPre(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count), language: ""))
case let .TextUrl(url):
result.append(.messageEntityTextUrl(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count), url: url))
case .TextMention:
break
case .PhoneNumber:
break
case .Strikethrough:
result.append(.messageEntityStrike(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .BlockQuote:
result.append(.messageEntityBlockquote(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Underline:
result.append(.messageEntityUnderline(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Custom:
break
}
}
return result
}
private func boxedDecryptedMessage(transaction: Transaction, message: Message, globallyUniqueId: Int64, uploadedFile: SecretChatOutgoingFile?, thumbnailData: [MediaId: (CGSize, Data)], layer: SecretChatLayer) -> BoxedDecryptedMessage {
let media: Media? = message.media.first
var messageAutoremoveTimeout: Int32 = 0
@ -691,6 +812,20 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 17)
}
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
if let _ = viaBotName {
flags |= (1 << 11)
}
let decryptedEntites = entities.flatMap(decryptedEntities101)
if let _ = decryptedEntites {
flags |= (1 << 7)
}
let decryptedMedia = SecretApi101.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: thumb, thumbW: thumbW, thumbH: thumbH, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), caption: "")
flags |= (1 << 9)
if message.groupingKey != nil {
flags |= (1 << 17)
}
return .layer101(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
}
} else if let file = media as? TelegramMediaFile {
let thumbW: Int32
@ -786,6 +921,37 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 9)
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
}
case .layer101:
var decryptedMedia: SecretApi101.DecryptedMessageMedia?
if let uploadedFile = uploadedFile {
decryptedMedia = SecretApi101.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: thumb, thumbW: thumbW, thumbH: thumbH, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), attributes: decryptedAttributes101(file.attributes, transaction: transaction), caption: "")
} else {
if let resource = file.resource as? CloudDocumentMediaResource, let size = file.size {
let thumb: SecretApi101.PhotoSize
if let smallestRepresentation = smallestImageRepresentation(file.previewRepresentations), let thumbResource = smallestRepresentation.resource as? CloudFileMediaResource {
thumb = .photoSize(type: "s", location: .fileLocation(dcId: Int32(thumbResource.datacenterId), volumeId: thumbResource.volumeId, localId: thumbResource.localId, secret: thumbResource.secret), w: Int32(smallestRepresentation.dimensions.width), h: Int32(smallestRepresentation.dimensions.height), size: thumbResource.size.flatMap(Int32.init) ?? 0)
} else {
thumb = SecretApi101.PhotoSize.photoSizeEmpty(type: "s")
}
decryptedMedia = SecretApi101.DecryptedMessageMedia.decryptedMessageMediaExternalDocument(id: resource.fileId, accessHash: resource.accessHash, date: 0, mimeType: file.mimeType, size: Int32(size), thumb: thumb, dcId: Int32(resource.datacenterId), attributes: decryptedAttributes101(file.attributes, transaction: transaction))
}
}
if let decryptedMedia = decryptedMedia {
if let _ = viaBotName {
flags |= (1 << 11)
}
let decryptedEntites = entities.flatMap(decryptedEntities101)
if let _ = decryptedEntites {
flags |= (1 << 7)
}
if message.groupingKey != nil {
flags |= (1 << 17)
}
flags |= (1 << 9)
return .layer101(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
}
}
} else if let webpage = media as? TelegramMediaWebpage {
var url: String?
@ -815,6 +981,17 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
let decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaWebPage(url: url)
flags |= (1 << 9)
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
if let _ = viaBotName {
flags |= (1 << 11)
}
let decryptedEntites = entities.flatMap(decryptedEntities101)
if let _ = decryptedEntites {
flags |= (1 << 7)
}
let decryptedMedia = SecretApi101.DecryptedMessageMedia.decryptedMessageMediaWebPage(url: url)
flags |= (1 << 9)
return .layer101(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
}
}
} else if let location = media as? TelegramMediaMap {
@ -850,6 +1027,23 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
decryptedMedia = .decryptedMessageMediaGeoPoint(lat: location.latitude, long: location.longitude)
}
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
if let _ = viaBotName {
flags |= (1 << 11)
}
let decryptedEntites = entities.flatMap(decryptedEntities101)
if let _ = decryptedEntites {
flags |= (1 << 7)
}
let decryptedMedia: SecretApi101.DecryptedMessageMedia
flags |= (1 << 9)
if let venue = location.venue {
decryptedMedia = .decryptedMessageMediaVenue(lat: location.latitude, long: location.longitude, title: venue.title, address: venue.address ?? "", provider: venue.provider ?? "", venueId: venue.id ?? "")
} else {
decryptedMedia = .decryptedMessageMediaGeoPoint(lat: location.latitude, long: location.longitude)
}
return .layer101(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
}
} else if let contact = media as? TelegramMediaContact {
switch layer {
@ -874,6 +1068,18 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
let decryptedMedia: SecretApi73.DecryptedMessageMedia = .decryptedMessageMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName, userId: 0)
flags |= (1 << 9)
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
if let _ = viaBotName {
flags |= (1 << 11)
}
let decryptedEntites = entities.flatMap(decryptedEntities101)
if let _ = decryptedEntites {
flags |= (1 << 7)
}
let decryptedMedia: SecretApi101.DecryptedMessageMedia = .decryptedMessageMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName, userId: 0)
flags |= (1 << 9)
return .layer101(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
}
}
}
@ -899,6 +1105,15 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 7)
}
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
if let _ = viaBotName {
flags |= (1 << 11)
}
let decryptedEntites = entities.flatMap(decryptedEntities101)
if let _ = decryptedEntites {
flags |= (1 << 7)
}
return .layer101(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
}
}
@ -916,6 +1131,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionDeleteMessages(randomIds: globallyUniqueIds)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionDeleteMessages(randomIds: globallyUniqueIds)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionDeleteMessages(randomIds: globallyUniqueIds)))
}
case let .screenshotMessages(layer, actionGloballyUniqueId, globallyUniqueIds, _):
switch layer {
@ -929,6 +1146,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionScreenshotMessages(randomIds: globallyUniqueIds)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionScreenshotMessages(randomIds: globallyUniqueIds)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionScreenshotMessages(randomIds: globallyUniqueIds)))
}
case let .clearHistory(layer, actionGloballyUniqueId):
switch layer {
@ -941,6 +1160,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionFlushHistory))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionFlushHistory))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionFlushHistory))
}
case let .resendOperations(layer, actionGloballyUniqueId, fromSeqNo, toSeqNo):
switch layer {
@ -948,6 +1169,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionResend(startSeqNo: fromSeqNo, endSeqNo: toSeqNo)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionResend(startSeqNo: fromSeqNo, endSeqNo: toSeqNo)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionResend(startSeqNo: fromSeqNo, endSeqNo: toSeqNo)))
}
case let .reportLayerSupport(layer, actionGloballyUniqueId, layerSupport):
switch layer {
@ -961,6 +1184,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNotifyLayer(layer: layerSupport)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNotifyLayer(layer: layerSupport)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNotifyLayer(layer: layerSupport)))
}
case let .pfsRequestKey(layer, actionGloballyUniqueId, rekeySessionId, gA):
switch layer {
@ -968,6 +1193,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionRequestKey(exchangeId: rekeySessionId, gA: Buffer(buffer: gA))))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionRequestKey(exchangeId: rekeySessionId, gA: Buffer(buffer: gA))))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionRequestKey(exchangeId: rekeySessionId, gA: Buffer(buffer: gA))))
}
case let .pfsAcceptKey(layer, actionGloballyUniqueId, rekeySessionId, gB, keyFingerprint):
switch layer {
@ -975,6 +1202,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAcceptKey(exchangeId: rekeySessionId, gB: Buffer(buffer: gB), keyFingerprint: keyFingerprint)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAcceptKey(exchangeId: rekeySessionId, gB: Buffer(buffer: gB), keyFingerprint: keyFingerprint)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAcceptKey(exchangeId: rekeySessionId, gB: Buffer(buffer: gB), keyFingerprint: keyFingerprint)))
}
case let .pfsAbortSession(layer, actionGloballyUniqueId, rekeySessionId):
switch layer {
@ -982,6 +1211,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAbortKey(exchangeId: rekeySessionId)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAbortKey(exchangeId: rekeySessionId)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAbortKey(exchangeId: rekeySessionId)))
}
case let .pfsCommitKey(layer, actionGloballyUniqueId, rekeySessionId, keyFingerprint):
switch layer {
@ -989,6 +1220,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionCommitKey(exchangeId: rekeySessionId, keyFingerprint: keyFingerprint)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionCommitKey(exchangeId: rekeySessionId, keyFingerprint: keyFingerprint)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionCommitKey(exchangeId: rekeySessionId, keyFingerprint: keyFingerprint)))
}
case let .noop(layer, actionGloballyUniqueId):
switch layer {
@ -996,6 +1229,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNoop))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNoop))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNoop))
}
case let .readMessageContents(layer, actionGloballyUniqueId, globallyUniqueIds):
switch layer {
@ -1009,6 +1244,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionReadMessages(randomIds: globallyUniqueIds)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionReadMessages(randomIds: globallyUniqueIds)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionReadMessages(randomIds: globallyUniqueIds)))
}
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout, _):
switch layer {
@ -1022,6 +1259,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionSetMessageTTL(ttlSeconds: timeout)))
case .layer73:
return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionSetMessageTTL(ttlSeconds: timeout)))
case .layer101:
return .layer101(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionSetMessageTTL(ttlSeconds: timeout)))
}
}
}

View File

@ -52,6 +52,10 @@ private func parsedServiceAction(_ operation: SecretChatIncomingDecryptedOperati
if let parsedObject = SecretApi73.parse(Buffer(bufferNoCopy: operation.contents)), let apiMessage = parsedObject as? SecretApi73.DecryptedMessage {
return SecretChatServiceAction(apiMessage)
}
case .layer101:
if let parsedObject = SecretApi101.parse(Buffer(bufferNoCopy: operation.contents)), let apiMessage = parsedObject as? SecretApi101.DecryptedMessage {
return SecretChatServiceAction(apiMessage)
}
}
return nil
}
@ -153,6 +157,18 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, transactio
} else {
contentParsingError = true
}
case .layer101:
if let parsedObject = SecretApi101.parse(Buffer(bufferNoCopy: operation.contents)), let apiMessage = parsedObject as? SecretApi101.DecryptedMessage {
if let (parsedMessage, parsedResources) = parseMessage(peerId: peerId, authorId: updatedPeer.regularPeerId, tagLocalIndex: entry.tagLocalIndex, timestamp: operation.timestamp, apiMessage: apiMessage, file: operation.file, messageIdForGloballyUniqueMessageId: { id in
return transaction.messageIdForGloballyUniqueMessageId(peerId: peerId, id: id)
}) {
message = parsedMessage
resources = parsedResources
}
serviceAction = SecretChatServiceAction(apiMessage)
} else {
contentParsingError = true
}
}
switch updatedState.embeddedState {
@ -214,7 +230,11 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, transactio
case .handshake:
throw MessageParsingError.invalidChatState
case .basicLayer:
if layerSupport >= 73 {
if layerSupport >= 101 {
let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: .layer101, locallyRequestedLayer: 101, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil)
updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState))
updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer101, actionGloballyUniqueId: arc4random64(), layerSupport: 101), state: updatedState)
} else if layerSupport >= 73 {
let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: .layer73, locallyRequestedLayer: 73, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil)
updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState))
updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer73, actionGloballyUniqueId: arc4random64(), layerSupport: 73), state: updatedState)
@ -455,6 +475,42 @@ extension SecretChatServiceAction {
}
}
extension SecretChatServiceAction {
init?(_ apiMessage: SecretApi101.DecryptedMessage) {
switch apiMessage {
case .decryptedMessage:
return nil
case let .decryptedMessageService(_, action):
switch action {
case let .decryptedMessageActionDeleteMessages(randomIds):
self = .deleteMessages(globallyUniqueIds: randomIds)
case .decryptedMessageActionFlushHistory:
self = .clearHistory
case let .decryptedMessageActionNotifyLayer(layer):
self = .reportLayerSupport(layer)
case let .decryptedMessageActionReadMessages(randomIds):
self = .markMessagesContentAsConsumed(globallyUniqueIds: randomIds)
case .decryptedMessageActionScreenshotMessages:
return nil
case let .decryptedMessageActionSetMessageTTL(ttlSeconds):
self = .setMessageAutoremoveTimeout(ttlSeconds)
case let .decryptedMessageActionResend(startSeqNo, endSeqNo):
self = .resendOperations(fromSeq: startSeqNo, toSeq: endSeqNo)
case let .decryptedMessageActionRequestKey(exchangeId, gA):
self = .rekeyAction(.pfsRequestKey(rekeySessionId: exchangeId, gA: MemoryBuffer(gA)))
case let .decryptedMessageActionAcceptKey(exchangeId, gB, keyFingerprint):
self = .rekeyAction(.pfsAcceptKey(rekeySessionId: exchangeId, gB: MemoryBuffer(gB), keyFingerprint: keyFingerprint))
case let .decryptedMessageActionCommitKey(exchangeId, keyFingerprint):
self = .rekeyAction(.pfsCommitKey(rekeySessionId: exchangeId, keyFingerprint: keyFingerprint))
case let .decryptedMessageActionAbortKey(exchangeId):
self = .rekeyAction(.pfsAbortSession(rekeySessionId: exchangeId))
case .decryptedMessageActionNoop:
return nil
}
}
}
}
extension StoreMessage {
convenience init?(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi8.DecryptedMessage, file: SecretChatFileReference?) {
switch apiMessage {
@ -549,6 +605,43 @@ extension TelegramMediaFileAttribute {
}
}
extension TelegramMediaFileAttribute {
init?(_ apiAttribute: SecretApi101.DocumentAttribute) {
switch apiAttribute {
case .documentAttributeAnimated:
self = .Animated
case let .documentAttributeAudio(flags, duration, title, performer, waveform):
let isVoice = (flags & (1 << 10)) != 0
var waveformBuffer: MemoryBuffer?
if let waveform = waveform {
let memory = malloc(waveform.size)!
memcpy(memory, waveform.data, waveform.size)
waveformBuffer = MemoryBuffer(memory: memory, capacity: waveform.size, length: waveform.size, freeWhenDone: true)
}
self = .Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer)
case let .documentAttributeFilename(fileName):
self = .FileName(fileName: fileName)
case let .documentAttributeImageSize(w, h):
self = .ImageSize(size: CGSize(width: CGFloat(w), height: CGFloat(h)))
case let .documentAttributeSticker(alt, stickerset):
let packReference: StickerPackReference?
switch stickerset {
case .inputStickerSetEmpty:
packReference = nil
case let .inputStickerSetShortName(shortName):
packReference = .name(shortName)
}
self = .Sticker(displayText: alt, packReference: packReference, maskData: nil)
case let .documentAttributeVideo(flags, duration, w, h):
var videoFlags: TelegramMediaVideoFlags = []
if (flags & (1 << 0)) != 0 {
videoFlags.insert(.instantRoundVideo)
}
self = .Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: videoFlags)
}
}
}
private func parseEntities(_ entities: [SecretApi46.MessageEntity]?) -> TextEntitiesMessageAttribute {
var result: [MessageTextEntity] = []
if let entities = entities {
@ -986,3 +1079,237 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
}
}
}
private func parseEntities(_ entities: [SecretApi101.MessageEntity]) -> TextEntitiesMessageAttribute {
var result: [MessageTextEntity] = []
for entity in entities {
switch entity {
case let .messageEntityMention(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Mention))
case let .messageEntityHashtag(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Hashtag))
case let .messageEntityBotCommand(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BotCommand))
case let .messageEntityUrl(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Url))
case let .messageEntityEmail(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Email))
case let .messageEntityBold(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Bold))
case let .messageEntityItalic(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic))
case let .messageEntityCode(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code))
case let .messageEntityPre(offset, length, _):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre))
case let .messageEntityTextUrl(offset, length, url):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url)))
case let .messageEntityStrike(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Strikethrough))
case let .messageEntityUnderline(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Underline))
case let .messageEntityBlockquote(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BlockQuote))
case .messageEntityUnknown:
break
}
}
return TextEntitiesMessageAttribute(entities: result)
}
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi101.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
switch apiMessage {
case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
var text = message
var parsedMedia: [Media] = []
var attributes: [MessageAttribute] = []
var resources: [(MediaResource, Data)] = []
if let entitiesAttribute = entities.flatMap(parseEntities) {
attributes.append(entitiesAttribute)
}
if let viaBotName = viaBotName, !viaBotName.isEmpty {
attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName))
}
if let media = media {
switch media {
case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption):
if !caption.isEmpty {
text = caption
}
if let file = file {
var representations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource))
resources.append((resource, thumb.makeData()))
}
representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size)))
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil)
parsedMedia.append(image)
}
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
if let file = file {
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
parsedMedia.append(fileMedia)
attributes.append(ConsumableContentMessageAttribute(consumed: false))
}
case let .decryptedMessageMediaDocument(thumb, thumbW, thumbH, mimeType, size, key, iv, decryptedAttributes, caption):
if !caption.isEmpty {
text = caption
}
if let file = file {
var parsedAttributes: [TelegramMediaFileAttribute] = []
for attribute in decryptedAttributes {
if let parsedAttribute = TelegramMediaFileAttribute(attribute) {
parsedAttributes.append(parsedAttribute)
}
}
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
loop: for attr in parsedAttributes {
switch attr {
case let .Video(_, _, flags):
if flags.contains(.instantRoundVideo) {
attributes.append(ConsumableContentMessageAttribute(consumed: false))
}
break loop
case let .Audio(isVoice, _, _, _, _):
if isVoice {
attributes.append(ConsumableContentMessageAttribute(consumed: false))
}
default:
break
}
}
}
case let .decryptedMessageMediaVideo(thumb, thumbW, thumbH, duration, mimeType, w, h, size, key, iv, caption):
if !caption.isEmpty {
text = caption
}
if let file = file {
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []), .FileName(fileName: "video.mov")]
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
}
case let .decryptedMessageMediaExternalDocument(id, accessHash, date, mimeType, size, thumb, dcId, attributes):
var parsedAttributes: [TelegramMediaFileAttribute] = []
for attribute in attributes {
if let parsedAttribute = TelegramMediaFileAttribute(attribute) {
parsedAttributes.append(parsedAttribute)
}
}
var previewRepresentations: [TelegramMediaImageRepresentation] = []
switch thumb {
case let .photoSize(_, location, w, h, size):
switch location {
case let .fileLocation(dcId, volumeId, localId, secret):
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil)))
case .fileLocationUnavailable:
break
}
case let .photoCachedSize(_, location, w, h, bytes):
if bytes.size > 0 {
switch location {
case let .fileLocation(dcId, volumeId, localId, secret):
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size, fileReference: nil)
resources.append((resource, bytes.makeData()))
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
case .fileLocationUnavailable:
break
}
}
default:
break
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
case let .decryptedMessageMediaWebPage(url):
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
case let .decryptedMessageMediaGeoPoint(lat, long):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil))
case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId):
parsedMedia.append(TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), vCardData: nil))
case let .decryptedMessageMediaVenue(lat, long, title, address, provider, venueId):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil))
case .decryptedMessageMediaEmpty:
break
}
}
if ttl > 0 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttl, countdownBeginTime: nil))
}
var groupingKey: Int64?
if let groupedId = groupedId {
inner: for media in parsedMedia {
if let _ = media as? TelegramMediaImage {
groupingKey = groupedId
break inner
} else if let _ = media as? TelegramMediaFile {
groupingKey = groupedId
break inner
}
}
}
if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) {
attributes.append(ReplyMessageAttribute(messageId: replyMessageId))
}
var entitiesAttribute: TextEntitiesMessageAttribute?
for attribute in attributes {
if let attribute = attribute as? TextEntitiesMessageAttribute {
entitiesAttribute = attribute
break
}
}
let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
case let .decryptedMessageService(randomId, action):
switch action {
case .decryptedMessageActionDeleteMessages:
return nil
case .decryptedMessageActionFlushHistory:
return nil
case .decryptedMessageActionNotifyLayer:
return nil
case .decryptedMessageActionReadMessages:
return nil
case .decryptedMessageActionScreenshotMessages:
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
case let .decryptedMessageActionSetMessageTTL(ttlSeconds):
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(ttlSeconds))]), [])
case .decryptedMessageActionResend:
return nil
case .decryptedMessageActionRequestKey:
return nil
case .decryptedMessageActionAcceptKey:
return nil
case .decryptedMessageActionCommitKey:
return nil
case .decryptedMessageActionAbortKey:
return nil
case .decryptedMessageActionNoop:
return nil
}
}
}

View File

@ -7,7 +7,7 @@ import Foundation
import SwiftSignalKit
#endif
private let topSupportedLayer: SecretChatSequenceBasedLayer = .layer73
private let topSupportedLayer: SecretChatSequenceBasedLayer = .layer101
func secretChatCommonSupportedLayer(remoteLayer: Int32) -> SecretChatSequenceBasedLayer {
switch remoteLayer {
@ -15,6 +15,8 @@ func secretChatCommonSupportedLayer(remoteLayer: Int32) -> SecretChatSequenceBas
return .layer46
case 73:
return .layer73
case 101:
return .layer101
default:
return topSupportedLayer
}

View File

@ -81,6 +81,7 @@ struct SecretChatOutgoingFile: PostboxCoding {
public enum SecretChatSequenceBasedLayer: Int32 {
case layer46 = 46
case layer73 = 73
case layer101 = 101
var secretChatLayer: SecretChatLayer {
switch self {
@ -88,6 +89,8 @@ public enum SecretChatSequenceBasedLayer: Int32 {
return .layer46
case .layer73:
return .layer73
case .layer101:
return .layer101
}
}
}

View File

@ -14,6 +14,7 @@ enum SecretChatLayer: Int32 {
case layer8 = 8
case layer46 = 46
case layer73 = 73
case layer101 = 101
}
public struct SecretChatKeySha1Fingerprint: PostboxCoding, Equatable {

View File

@ -118,7 +118,8 @@ private let list = PresentationThemeList(
),
mediaPlaceholderColor: UIColor(rgb: 0x1e2c3a),
scrollIndicatorColor: UIColor(white: 1.0, alpha: 0.3),
pageIndicatorInactiveColor: UIColor(rgb: 0xDBF5FF, alpha: 0.4)
pageIndicatorInactiveColor: UIColor(rgb: 0xDBF5FF, alpha: 0.4),
inputClearButtonColor: UIColor(rgb: 0x8B9197)
)
private let chatList = PresentationThemeChatList(

View File

@ -116,7 +116,8 @@ private let list = PresentationThemeList(
),
mediaPlaceholderColor: UIColor(rgb: 0x1c1c1d),
scrollIndicatorColor: UIColor(white: 1.0, alpha: 0.3),
pageIndicatorInactiveColor: UIColor(white: 1.0, alpha: 0.3)
pageIndicatorInactiveColor: UIColor(white: 1.0, alpha: 0.3),
inputClearButtonColor: UIColor(rgb: 0x8B9197)
)
private let chatList = PresentationThemeChatList(

View File

@ -116,7 +116,8 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, serviceBackgroun
),
mediaPlaceholderColor: UIColor(rgb: 0xe4e4e4),
scrollIndicatorColor: UIColor(white: 0.0, alpha: 0.3),
pageIndicatorInactiveColor: UIColor(rgb: 0xe3e3e7)
pageIndicatorInactiveColor: UIColor(rgb: 0xe3e3e7),
inputClearButtonColor: UIColor(rgb: 0xcccccc)
)
let chatList = PresentationThemeChatList(

View File

@ -271,6 +271,8 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager) -
effectiveTheme = themeSettings.theme
}
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color ?? defaultDayAccentColor
switch effectiveTheme {
case let .builtin(reference):
switch reference {
@ -281,7 +283,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager) -
case .nightAccent:
themeValue = defaultDarkAccentPresentationTheme
case .day:
themeValue = makeDefaultDayPresentationTheme(accentColor: themeSettings.themeAccentColor ?? defaultDayAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor)
themeValue = makeDefaultDayPresentationTheme(accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor)
}
}
let dateTimeFormat = currentDateTimeFormat()
@ -550,6 +552,8 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
effectiveTheme = themeSettings.theme
}
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color ?? defaultDayAccentColor
let themeValue: PresentationTheme
switch effectiveTheme {
case let .builtin(reference):
@ -561,7 +565,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
case .nightAccent:
themeValue = defaultDarkAccentPresentationTheme
case .day:
themeValue = makeDefaultDayPresentationTheme(accentColor: themeSettings.themeAccentColor ?? defaultDayAccentColor, serviceBackgroundColor: serviceBackgroundColor)
themeValue = makeDefaultDayPresentationTheme(accentColor: effectiveAccentColor, serviceBackgroundColor: serviceBackgroundColor)
}
}

View File

@ -331,8 +331,9 @@ public final class PresentationThemeList {
public let mediaPlaceholderColor: UIColor
public let scrollIndicatorColor: UIColor
public let pageIndicatorInactiveColor: UIColor
public let 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, freeMonoIcon: UIColor, itemSwitchColors: PresentationThemeSwitch, itemDisclosureActions: PresentationThemeItemDisclosureActions, itemCheckColors: PresentationThemeCheck, controlSecondaryColor: UIColor, freeInputField: PresentationInputFieldTheme, mediaPlaceholderColor: UIColor, scrollIndicatorColor: UIColor, pageIndicatorInactiveColor: 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, freeMonoIcon: UIColor, itemSwitchColors: PresentationThemeSwitch, itemDisclosureActions: PresentationThemeItemDisclosureActions, itemCheckColors: PresentationThemeCheck, controlSecondaryColor: UIColor, freeInputField: PresentationInputFieldTheme, mediaPlaceholderColor: UIColor, scrollIndicatorColor: UIColor, pageIndicatorInactiveColor: UIColor, inputClearButtonColor: UIColor) {
self.blocksBackgroundColor = blocksBackgroundColor
self.plainBackgroundColor = plainBackgroundColor
self.itemPrimaryTextColor = itemPrimaryTextColor
@ -360,6 +361,7 @@ public final class PresentationThemeList {
self.mediaPlaceholderColor = mediaPlaceholderColor
self.scrollIndicatorColor = scrollIndicatorColor
self.pageIndicatorInactiveColor = pageIndicatorInactiveColor
self.inputClearButtonColor = inputClearButtonColor
}
}
@ -500,7 +502,7 @@ public func bubbleColorComponents(theme: PresentationTheme, incoming: Bool, wall
public func bubbleVariableColor(variableColor: PresentationThemeVariableColor, wallpaper: TelegramWallpaper) -> UIColor {
switch wallpaper {
case .builtin, .color(0xffffff):
case .color(0xffffff):
return variableColor.withoutWallpaper
default:
return variableColor.withWallpaper
@ -708,7 +710,7 @@ public func serviceMessageColorComponents(theme: PresentationTheme, wallpaper: T
public func serviceMessageColorComponents(chatTheme: PresentationThemeChat, wallpaper: TelegramWallpaper) -> PresentationThemeServiceMessageColorComponents {
switch wallpaper {
case .builtin, .color(0xffffff):
case .color(0xffffff):
return chatTheme.serviceMessage.components.withDefaultWallpaper
default:
return chatTheme.serviceMessage.components.withCustomWallpaper

View File

@ -590,6 +590,9 @@ final class SharedApplicationContext {
}, requestSetAlternateIconName: { name, completion in
if #available(iOS 10.3, *) {
application.setAlternateIconName(name, completionHandler: { error in
if let error = error {
Logger.shared.log("App \(self.episodeId)", "failed to set alternate icon with error \(error.localizedDescription)")
}
completion(error == nil)
})
} else {

View File

@ -148,14 +148,7 @@ final class AuthorizedApplicationContext {
context.keyShortcutsController = keyShortcutsController
}
/*self.applicationInForegroundDisposable = context.sharedContext.applicationBindings.applicationInForeground.start(next: { [weak self] value in
Queue.mainQueue().async {
self?.notificationManager.isApplicationInForeground = value
}
})*/
let previousPasscodeState = Atomic<PasscodeState?>(value: nil)
let passcodeStatusData = combineLatest(queue: Queue.mainQueue(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), context.sharedContext.accountManager.accessChallengeData(), context.sharedContext.applicationBindings.applicationIsActive)
let passcodeState = passcodeStatusData
|> map { sharedData, accessChallengeDataView, isActive -> PasscodeState in
@ -281,7 +274,7 @@ final class AuthorizedApplicationContext {
if #available(iOS 10.0, *) {
} else {
DeviceAccess.authorizeAccess(to: .contacts, presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, present: { c, a in
}, openSettings: {}, { _ in })
})
}
if let passcodeController = strongSelf.passcodeController {
@ -520,23 +513,27 @@ final class AuthorizedApplicationContext {
if #available(iOS 10.0, *) {
let permissionsPosition = ValuePromise(0, ignoreRepeated: true)
self.permissionsDisposable.set((combineLatest(queue: .mainQueue(), requiredPermissions(context: context), permissionUISplitTest(postbox: context.account.postbox), permissionsPosition.get(), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.contactsPermissionWarningKey()), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.notificationsPermissionWarningKey()))
|> deliverOnMainQueue).start(next: { [weak self] required, splitTest, position, contactsPermissionWarningNotice, notificationsPermissionWarningNotice in
self.permissionsDisposable.set((combineLatest(queue: .mainQueue(), requiredPermissions(context: context), permissionUISplitTest(postbox: context.account.postbox), permissionsPosition.get(), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .contacts)!), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .notifications)!), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .cellularData)!))
|> deliverOnMainQueue).start(next: { [weak self] required, splitTest, position, contactsPermissionWarningNotice, notificationsPermissionWarningNotice, cellularDataPermissionWarningNotice in
guard let strongSelf = self else {
return
}
let contactsTimestamp = contactsPermissionWarningNotice.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
let notificationsTimestamp = notificationsPermissionWarningNotice.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
let cellularDataTimestamp = cellularDataPermissionWarningNotice.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
if contactsTimestamp == nil, case .requestable = required.0.status {
ApplicationSpecificNotice.setContactsPermissionWarning(accountManager: context.sharedContext.accountManager, value: 1)
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .contacts, value: 1)
}
if notificationsTimestamp == nil, case .requestable = required.1.status {
ApplicationSpecificNotice.setNotificationsPermissionWarning(accountManager: context.sharedContext.accountManager, value: 1)
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: 1)
}
let config = splitTest.configuration
var order = config.order
if !order.contains(.cellularData) {
order.append(.cellularData)
}
if !order.contains(.siri) {
order.append(.siri)
}
@ -563,9 +560,13 @@ final class AuthorizedApplicationContext {
if case .requestable = required.1.status, notificationsTimestamp != 0 {
requestedPermissions.append((required.1, modal))
}
case .cellularData:
if case .denied = required.2.status, cellularDataTimestamp != 0 {
requestedPermissions.append((required.2, true))
}
case .siri:
if case .requestable = required.2.status {
requestedPermissions.append((required.2, false))
if case .requestable = required.3.status {
requestedPermissions.append((required.3, false))
}
default:
break
@ -590,9 +591,11 @@ final class AuthorizedApplicationContext {
permissionsPosition.set(position + 1)
switch state {
case .contacts:
ApplicationSpecificNotice.setContactsPermissionWarning(accountManager: context.sharedContext.accountManager, value: 0)
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .contacts, value: 0)
case .notifications:
ApplicationSpecificNotice.setNotificationsPermissionWarning(accountManager: context.sharedContext.accountManager, value: 0)
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: 0)
case .cellularData:
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .cellularData, value: 0)
default:
break
}
@ -616,7 +619,7 @@ final class AuthorizedApplicationContext {
splitTest.addEvent(.ContactsDenied)
}
permissionsPosition.set(position + 1)
ApplicationSpecificNotice.setContactsPermissionWarning(accountManager: context.sharedContext.accountManager, value: 0)
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .contacts, value: 0)
}
case .notifications:
splitTest.addEvent(.NotificationsRequest)
@ -629,8 +632,19 @@ final class AuthorizedApplicationContext {
splitTest.addEvent(.NotificationsDenied)
}
permissionsPosition.set(position + 1)
ApplicationSpecificNotice.setNotificationsPermissionWarning(accountManager: context.sharedContext.accountManager, value: 0)
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: 0)
}
case .cellularData:
DeviceAccess.authorizeAccess(to: .cellularData, presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: { [weak self] c, a in
if let strongSelf = self {
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(c, in: .window(.root))
}
}, openSettings: {
context.sharedContext.applicationBindings.openSettings()
}, { result in
permissionsPosition.set(position + 1)
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .cellularData, value: 0)
})
case .siri:
DeviceAccess.authorizeAccess(to: .siri, requestSiriAuthorization: { completion in
return context.sharedContext.applicationBindings.requestSiriAuthorization(completion)

View File

@ -277,7 +277,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
}
var dismissImpl: (() -> Void)?
let alertTheme = AlertControllerTheme(presentationTheme: strongSelf.theme)
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), fixedFont: Font.regular(13.0))
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0))
let contentNode = TextAlertContentNode(theme: alertTheme, title: NSAttributedString(string: strongSelf.strings.Login_TermsOfServiceHeader, font: Font.medium(17.0), textColor: alertTheme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.strings.Login_TermsOfServiceAgree, action: {
dismissImpl?()

View File

@ -415,7 +415,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
let (dateLayout, dateApply) = makeDateLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: dateText, font: dateFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - leftInset - rightInset), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - leftInset - dateRightInset - dateLayout.size.width - 10.0), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - leftInset - dateRightInset - dateLayout.size.width - (item.editing ? -30.0 : 10.0)), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let (statusLayout, statusApply) = makeStatusLayout(TextNodeLayoutArguments(attributedString: statusAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - leftInset - rightInset), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))

View File

@ -280,7 +280,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
}
}, tag: ChannelVisibilityEntryTag.privateLink)
case let .editablePublicLink(theme, placeholder, currentText):
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: currentText, placeholder: placeholder, tag: ChannelVisibilityEntryTag.publicLink, sectionId: self.section, textUpdated: { updatedText in
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: currentText, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), clearButton: true, tag: ChannelVisibilityEntryTag.publicLink, sectionId: self.section, textUpdated: { updatedText in
arguments.updatePublicLinkText(currentText, updatedText)
}, receivedFocus: {
arguments.scrollToPublicLinkText()

View File

@ -6,10 +6,11 @@ import SwiftSignalKit
import Postbox
import TelegramCore
private let messageFont: UIFont = UIFont.systemFont(ofSize: 17.0)
private let messageBoldFont: UIFont = UIFont.boldSystemFont(ofSize: 17.0)
private let messageItalicFont: UIFont = UIFont.italicSystemFont(ofSize: 17.0)
private let messageFixedFont: UIFont = UIFont(name: "Menlo-Regular", size: 16.0) ?? UIFont.systemFont(ofSize: 17.0)
private let messageFont = Font.regular(17.0)
private let messageBoldFont = Font.semibold(17.0)
private let messageItalicFont = Font.italic(17.0)
private let messageBoldItalicFont = Font.semiboldItalic(17.0)
private let messageFixedFont = UIFont(name: "Menlo-Regular", size: 16.0) ?? UIFont.systemFont(ofSize: 17.0)
final class ChatBotInfoItem: ListViewItem {
fileprivate let text: String
@ -151,7 +152,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
updatedTextAndEntities = (item.text, generateTextEntities(item.text, enabledTypes: .all))
}
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.bubble.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.bubble.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, fixedFont: messageFixedFont)
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.bubble.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.bubble.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, boldItalicFont: messageBoldItalicFont, fixedFont: messageFixedFont)
let horizontalEdgeInset: CGFloat = 10.0 + params.leftInset
let horizontalContentInset: CGFloat = 12.0

View File

@ -233,7 +233,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
private var beginMediaRecordingRequestId: Int = 0
private var lockMediaRecordingRequestId: Int?
public override var customData: Any? {
return self.chatLocation
}
@ -4241,6 +4241,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState {
controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState
self.updateItemNodesSelectionStates(animated: transition.isAnimated)
(self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(controllerInteraction.selectionState != nil ? .master : nil, transition: transition)
}
}
@ -6944,7 +6945,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
}
private func updateReminderActivity() {
if (self.isReminderActivityEnabled) {
if self.isReminderActivityEnabled && false {
if #available(iOS 9.0, *) {
if self.reminderActivity == nil, case let .peer(peerId) = self.chatLocation, let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer {
let reminderActivity = NSUserActivity(activityType: "RemindAboutChatIntent")

View File

@ -412,14 +412,6 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
}
}
storeMessageTextInPasteboard(message.text, entities: messageEntities)
// if let messageEntities = messageEntities {
//
// let attributedString = chatInputStateStringWithAppliedEntities(message.text, entities: messageEntities)
// //stringWithAppliedEntities(message.text, entities: messageEntities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.bold(14.0), italicFont: Font.italic(14.0), fixedFont: Font.monospace(14.0))
// UIPasteboard.general.set(attributedString: attributedString)
// } else {
// UIPasteboard.general.string = message.text
// }
}
})))
}

View File

@ -692,7 +692,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
scrollToEndIfExists = true
}
navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] in
navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] in
self?.chatListDisplayNode.chatListNode.clearHighlightAnimated(true)
})
}

View File

@ -1532,7 +1532,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let textDeltaX = textFrame.origin.x - contentRect.origin.x
transition.animatePositionAdditive(node: self.textNode, offset: CGPoint(x: textDeltaX, y: 0.0))
textFrame.origin.x = contentRect.origin.x
self.textNode.frame = textFrame
transition.updateFrame(node: textNode, frame: textFrame)
var contentImageFrame = self.contentImageNode.frame
contentImageFrame.origin = textFrame.origin.offsetBy(dx: 1.0, dy: 0.0)

View File

@ -385,7 +385,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
case let .customText(text, entities):
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, fixedFont: titleFont, underlineLinks: false)
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, underlineLinks: false)
case let .botDomainAccessGranted(domain):
attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).0, font: titleFont, textColor: primaryTextColor)
case let .botSentSecureValues(types):

View File

@ -9,12 +9,13 @@ import Postbox
import TelegramPresentationData
import TelegramUIPreferences
private let titleFont: UIFont = Font.semibold(15.0)
private let textFont: UIFont = Font.regular(15.0)
private let textBoldFont: UIFont = Font.semibold(15.0)
private let textItalicFont: UIFont = Font.italic(15.0)
private let textFixedFont: UIFont = Font.regular(15.0)
private let buttonFont: UIFont = Font.semibold(13.0)
private let titleFont = Font.semibold(15.0)
private let textFont = Font.regular(15.0)
private let textBoldFont = Font.semibold(15.0)
private let textItalicFont = Font.italic(15.0)
private let textBoldItalicFont = Font.semiboldItalic(15.0)
private let textFixedFont = Font.regular(15.0)
private let buttonFont = Font.semibold(13.0)
enum ChatMessageAttachedContentActionIcon {
case instant
@ -370,7 +371,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
string.append(NSAttributedString(string: "\n", font: textFont, textColor: incoming ? bubbleTheme.incomingPrimaryTextColor : bubbleTheme.outgoingPrimaryTextColor))
}
if let entities = entities {
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: incoming ? bubbleTheme.incomingPrimaryTextColor : bubbleTheme.outgoingPrimaryTextColor, linkColor: incoming ? bubbleTheme.incomingLinkTextColor : bubbleTheme.outgoingLinkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, fixedFont: textFixedFont))
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: incoming ? bubbleTheme.incomingPrimaryTextColor : bubbleTheme.outgoingPrimaryTextColor, linkColor: incoming ? bubbleTheme.incomingLinkTextColor : bubbleTheme.outgoingLinkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont))
} else {
string.append(NSAttributedString(string: text + "\n", font: textFont, textColor: incoming ? bubbleTheme.incomingPrimaryTextColor : bubbleTheme.outgoingPrimaryTextColor))
}

View File

@ -1849,7 +1849,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if item.message.id.peerId == item.context.account.peerId, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)

View File

@ -230,7 +230,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
let forceStatusNewline = false
if let entities = entities {
attributedText = stringWithAppliedEntities(rawText, entities: entities, baseColor: incoming ? bubbleTheme.incomingPrimaryTextColor : bubbleTheme.outgoingPrimaryTextColor, linkColor: incoming ? bubbleTheme.incomingLinkTextColor : bubbleTheme.outgoingLinkTextColor, baseFont: textFont, linkFont: textFont, boldFont: item.presentationData.messageBoldFont, italicFont: item.presentationData.messageItalicFont, fixedFont: item.presentationData.messageFixedFont)
attributedText = stringWithAppliedEntities(rawText, entities: entities, baseColor: incoming ? bubbleTheme.incomingPrimaryTextColor : bubbleTheme.outgoingPrimaryTextColor, linkColor: incoming ? bubbleTheme.incomingLinkTextColor : bubbleTheme.outgoingLinkTextColor, baseFont: textFont, linkFont: textFont, boldFont: item.presentationData.messageBoldFont, italicFont: item.presentationData.messageItalicFont, boldItalicFont: item.presentationData.messageBoldItalicFont, fixedFont: item.presentationData.messageFixedFont)
} else {
attributedText = NSAttributedString(string: rawText, font: textFont, textColor: incoming ? bubbleTheme.incomingPrimaryTextColor : bubbleTheme.outgoingPrimaryTextColor)
}

View File

@ -1,5 +1,6 @@
import Foundation
import UIKit
import Display
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
@ -28,7 +29,7 @@ extension PresentationFontSize {
extension TelegramWallpaper {
var isEmpty: Bool {
switch self {
case .builtin, .image:
case .image:
return false
case let .file(file):
if file.isPattern, file.settings.color == 0xffffff {
@ -38,6 +39,8 @@ extension TelegramWallpaper {
}
case let .color(color):
return color == 0xffffff
default:
return false
}
}
var isBuiltin: Bool {
@ -79,6 +82,7 @@ public final class ChatPresentationData {
let messageEmojiFont3: UIFont
let messageBoldFont: UIFont
let messageItalicFont: UIFont
let messageBoldItalicFont: UIFont
let messageFixedFont: UIFont
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) {
@ -97,6 +101,7 @@ public final class ChatPresentationData {
self.messageEmojiFont3 = UIFont.systemFont(ofSize: 24.0)
self.messageBoldFont = UIFont.boldSystemFont(ofSize: baseFontSize)
self.messageItalicFont = UIFont.italicSystemFont(ofSize: baseFontSize)
self.messageBoldItalicFont = Font.semiboldItalic(baseFontSize)
self.messageFixedFont = UIFont(name: "Menlo-Regular", size: baseFontSize - 1.0) ?? UIFont.systemFont(ofSize: baseFontSize)
}
}

View File

@ -10,12 +10,12 @@ import TelegramUIPreferences
private final class ChatRecentActionsFilterControllerArguments {
let account: Account
let toggleAllActions: () -> Void
let toggleAllActions: (Bool) -> Void
let toggleAction: ([AdminLogEventsFlags]) -> Void
let toggleAllAdmins: () -> Void
let toggleAllAdmins: (Bool) -> Void
let toggleAdmin: (PeerId) -> Void
init(account: Account, toggleAllActions: @escaping () -> Void, toggleAction: @escaping ([AdminLogEventsFlags]) -> Void, toggleAllAdmins: @escaping () -> Void, toggleAdmin: @escaping (PeerId) -> Void) {
init(account: Account, toggleAllActions: @escaping (Bool) -> Void, toggleAction: @escaping ([AdminLogEventsFlags]) -> Void, toggleAllAdmins: @escaping (Bool) -> Void, toggleAdmin: @escaping (PeerId) -> Void) {
self.account = account
self.toggleAllActions = toggleAllActions
self.toggleAction = toggleAction
@ -206,8 +206,8 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
case let .actionsTitle(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .allActions(theme, text, value):
return ItemListSwitchItem(theme: theme, title: text, value: value, enabled: true, sectionId: self.section, style: .blocks, updated: { _ in
arguments.toggleAllActions()
return ItemListSwitchItem(theme: theme, title: text, value: value, enabled: true, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleAllActions(value)
})
case let .actionItem(theme, _, events, text, value):
return ItemListCheckboxItem(theme: theme, title: text, style: .right, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
@ -216,8 +216,8 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
case let .adminsTitle(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .allAdmins(theme, text, value):
return ItemListSwitchItem(theme: theme, title: text, value: value, enabled: true, sectionId: self.section, style: .blocks, updated: { _ in
arguments.toggleAllAdmins()
return ItemListSwitchItem(theme: theme, title: text, value: value, enabled: true, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleAllAdmins(value)
})
case let .adminPeerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, participant, checked):
let peerText: String
@ -374,9 +374,9 @@ public func channelRecentActionsFilterController(context: AccountContext, peer:
let actionsDisposable = DisposableSet()
let arguments = ChatRecentActionsFilterControllerArguments(account: context.account, toggleAllActions: {
let arguments = ChatRecentActionsFilterControllerArguments(account: context.account, toggleAllActions: { value in
updateState { current in
if current.events.isEmpty {
if value {
return current.withUpdatedEvents(.all)
} else {
return current.withUpdatedEvents([])
@ -398,13 +398,13 @@ public func channelRecentActionsFilterController(context: AccountContext, peer:
return current.withUpdatedEvents(updatedEvents)
}
}
}, toggleAllAdmins: {
}, toggleAllAdmins: { value in
let _ = (adminsPromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { admins in
if let _ = admins {
updateState { current in
if let _ = current.adminPeerIds {
if value {
return current.withUpdatedAdminPeerIds(nil)
} else {
return current.withUpdatedAdminPeerIds([])
@ -424,7 +424,9 @@ public func channelRecentActionsFilterController(context: AccountContext, peer:
return current.withUpdatedAdminPeerIds(updatedAdminPeerIds)
} else {
var updatedAdminPeerIds = current.adminPeerIds ?? admins.map { $0.peer.id }
if !updatedAdminPeerIds.contains(adminId) {
if updatedAdminPeerIds.contains(adminId), let index = updatedAdminPeerIds.index(of: adminId) {
updatedAdminPeerIds.remove(at: index)
} else {
updatedAdminPeerIds.append(adminId)
}
return current.withUpdatedAdminPeerIds(updatedAdminPeerIds)

View File

@ -12,8 +12,11 @@ struct ChatTextInputAttributes {
static let italic = NSAttributedStringKey(rawValue: "Attribute__Italic")
static let monospace = NSAttributedStringKey(rawValue: "Attribute__Monospace")
static let strikethrough = NSAttributedStringKey(rawValue: "Attribute__Strikethrough")
static let underline = NSAttributedStringKey(rawValue: "Attribute__Underline")
static let textMention = NSAttributedStringKey(rawValue: "Attribute__TextMention")
static let textUrl = NSAttributedStringKey(rawValue: "Attribute__TextUrl")
static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl]
}
func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedString {
@ -22,9 +25,7 @@ func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedStr
text.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in
for (key, value) in attributes {
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
result.addAttribute(key, value: value, range: range)
} else if key == ChatTextInputAttributes.bold || key == ChatTextInputAttributes.italic || key == ChatTextInputAttributes.monospace || key == ChatTextInputAttributes.strikethrough {
if ChatTextInputAttributes.allAttributes.contains(key) {
result.addAttribute(key, value: value, range: range)
}
}
@ -32,12 +33,12 @@ func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedStr
return result
}
private struct FontAttributes: OptionSet {
struct ChatTextFontAttributes: OptionSet {
var rawValue: Int32 = 0
static let bold = FontAttributes(rawValue: 1 << 0)
static let italic = FontAttributes(rawValue: 1 << 1)
static let monospace = FontAttributes(rawValue: 1 << 2)
static let bold = ChatTextFontAttributes(rawValue: 1 << 0)
static let italic = ChatTextFontAttributes(rawValue: 1 << 1)
static let monospace = ChatTextFontAttributes(rawValue: 1 << 2)
}
func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor) -> NSAttributedString {
@ -48,7 +49,7 @@ func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize:
result.addAttribute(NSAttributedStringKey.foregroundColor, value: textColor, range: fullRange)
stateText.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in
var fontAttributes: FontAttributes = []
var fontAttributes: ChatTextFontAttributes = []
for (key, value) in attributes {
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
@ -69,6 +70,9 @@ func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize:
} else if key == ChatTextInputAttributes.strikethrough {
result.addAttribute(key, value: value, range: range)
result.addAttribute(NSAttributedStringKey.strikethroughStyle, value: NSUnderlineStyle.styleSingle.rawValue as NSNumber, range: range)
} else if key == ChatTextInputAttributes.underline {
result.addAttribute(key, value: value, range: range)
result.addAttribute(NSAttributedStringKey.underlineStyle, value: NSUnderlineStyle.styleSingle.rawValue as NSNumber, range: range)
}
}
@ -421,7 +425,7 @@ func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme: Prese
textNode.textView.textStorage.addAttribute(NSAttributedStringKey.foregroundColor, value: theme.chat.inputPanel.primaryTextColor, range: fullRange)
attributedText.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in
var fontAttributes: FontAttributes = []
var fontAttributes: ChatTextFontAttributes = []
for (key, value) in attributes {
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
@ -443,6 +447,9 @@ func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme: Prese
} else if key == ChatTextInputAttributes.strikethrough {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
textNode.textView.textStorage.addAttribute(NSAttributedStringKey.strikethroughStyle, value: NSUnderlineStyle.styleSingle.rawValue as NSNumber, range: range)
} else if key == ChatTextInputAttributes.underline {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
textNode.textView.textStorage.addAttribute(NSAttributedStringKey.underlineStyle, value: NSUnderlineStyle.styleSingle.rawValue as NSNumber, range: range)
}
}

View File

@ -849,7 +849,7 @@ final class ContactListNode: ASDisplayNode {
let contactsWarningSuppressed = Promise<(Bool, Bool)>()
contactsWarningSuppressed.set(.single((false, false))
|> then(
combineLatest(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.contactsPermissionWarningKey()), context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings]))
combineLatest(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .contacts)!), context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings]))
|> map { noticeView, preferences -> (Bool, Bool) in
let settings: ContactsSettings = preferences.values[PreferencesKeys.contactsSettings] as? ContactsSettings ?? ContactsSettings.defaultSettings
let synchronizeDeviceContacts: Bool = settings.synchronizeContacts

View File

@ -132,7 +132,7 @@ public class ContactsController: ViewController {
})
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.authorizationDisposable = (combineLatest(DeviceAccess.authorizationStatus(subject: .contacts), combineLatest(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.contactsPermissionWarningKey()), context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings]), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]))
self.authorizationDisposable = (combineLatest(DeviceAccess.authorizationStatus(subject: .contacts), combineLatest(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .contacts)!), context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings]), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]))
|> map { noticeView, preferences, sharedData -> (Bool, ContactsSortOrder) in
let settings: ContactsSettings = preferences.values[PreferencesKeys.contactsSettings] as? ContactsSettings ?? ContactsSettings.defaultSettings
let synchronizeDeviceContacts: Bool = settings.synchronizeContacts

View File

@ -258,14 +258,18 @@ public func createChannelController(context: AccountContext) -> ViewController {
replaceControllerImpl?(controller)
}, error: { error in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
let text: String?
switch error {
case .generic, .tooMuchLocationBasedGroups:
text = presentationData.strings.Login_UnknownError
case .restricted:
text = presentationData.strings.Common_ActionNotAllowedError
default:
text = nil
}
if let text = text {
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}))
}
}, changeProfilePhoto: {

View File

@ -358,6 +358,8 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
return .restricted
case .tooMuchLocationBasedGroups:
return .tooMuchLocationBasedGroups
case let .serverProvided(error):
return .serverProvided(error)
}
}
case .locatedGroup:
@ -381,6 +383,8 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
return .restricted
case .tooMuchLocationBasedGroups:
return .tooMuchLocationBasedGroups
case let .serverProvided(error):
return .serverProvided(error)
}
}
}
@ -431,8 +435,12 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
}
}
}, error: { error in
if case .serverProvided = error {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
let text: String?
switch error {
case .privacy:
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelMultipleError
@ -442,8 +450,13 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
text = presentationData.strings.Common_ActionNotAllowedError
case .tooMuchLocationBasedGroups:
text = presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch
default:
text = nil
}
if let text = text {
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}))
}
}, changeProfilePhoto: {

View File

@ -150,7 +150,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
let _ = (updatePresentationThemeSettingsInteractively(accountManager: accountManager, { current in
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
themeSpecificChatWallpapers[current.theme.index] = wallpaper
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})).start()
}

View File

@ -118,10 +118,11 @@ func internalDocumentItemSupportsMimeType(_ type: String, fileName: String?) ->
private let textFont = Font.regular(16.0)
private let boldFont = Font.bold(16.0)
private let italicFont = Font.italic(16.0)
private let boldItalicFont = Font.semiboldItalic(16.0)
private let fixedFont = UIFont(name: "Menlo-Regular", size: 15.0) ?? textFont
func galleryCaptionStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> NSAttributedString {
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, fixedFont: fixedFont, underlineLinks: false)
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, underlineLinks: false)
}
private func galleryMessageCaptionText(_ message: Message) -> String {

View File

@ -134,6 +134,8 @@ func generateChatInputTextEntities(_ text: NSAttributedString) -> [MessageTextEn
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Code))
} else if key == ChatTextInputAttributes.strikethrough {
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Strikethrough))
} else if key == ChatTextInputAttributes.underline {
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Underline))
} else if key == ChatTextInputAttributes.textMention, let value = value as? ChatTextInputTextMentionAttribute {
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .TextMention(peerId: value.peerId)))
} else if key == ChatTextInputAttributes.textUrl, let value = value as? ChatTextInputTextUrlAttribute {

View File

@ -2001,7 +2001,9 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
return
}
let mapMedia = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, geoPlace: nil, venue: MapVenue(title: peer.displayTitle, address: location.address, provider: nil, id: nil, type: nil), liveBroadcastingTimeout: nil)
let controller = legacyLocationController(message: nil, mapMedia: mapMedia, context: context, isModal: false, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: {}, openUrl: { _ in })
let controller = legacyLocationController(message: nil, mapMedia: mapMedia, context: context, isModal: false, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: {}, openUrl: { url in
context.sharedContext.applicationBindings.openUrl(url)
})
pushControllerImpl?(controller)
})
}, changeLocation: {

View File

@ -80,6 +80,7 @@ private let labelFont = Font.regular(14.0)
private let textFont = Font.regular(17.0)
private let textBoldFont = Font.medium(17.0)
private let textItalicFont = Font.italic(17.0)
private let textBoldItalicFont = Font.semiboldItalic(17.0)
private let textFixedFont = Font.regular(17.0)
class ItemListAddressItemNode: ListViewItemNode {
@ -181,7 +182,7 @@ class ItemListAddressItemNode: ListViewItemNode {
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor: labelColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let baseColor = item.theme.list.itemPrimaryTextColor
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, fixedFont: textFixedFont)
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset - 98.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let padding: CGFloat = !item.label.isEmpty ? 39.0 : 20.0

View File

@ -9,6 +9,10 @@ import TelegramPresentationData
private let updatingAvatarOverlayImage = generateFilledCircleImage(diameter: 66.0, color: UIColor(white: 0.0, alpha: 0.4), backgroundColor: nil)
private func generateClearIcon(color: UIColor) -> UIImage? {
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color)
}
enum ItemListAvatarAndNameInfoItemTitleType {
case group
case channel
@ -164,6 +168,7 @@ class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
let sectionId: ItemListSectionId
let style: ItemListAvatarAndNameInfoItemStyle
let editingNameUpdated: (ItemListAvatarAndNameInfoItemName) -> Void
let editingNameCompleted: () -> Void
let avatarTapped: () -> Void
let context: ItemListAvatarAndNameInfoItemContext?
let updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar?
@ -174,7 +179,7 @@ class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
let selectable: Bool
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, mode: ItemListAvatarAndNameInfoItemMode, peer: Peer?, presence: PeerPresence?, label: String? = nil, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, tag: ItemListItemTag? = nil) {
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, mode: ItemListAvatarAndNameInfoItemMode, peer: Peer?, presence: PeerPresence?, label: String? = nil, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, editingNameCompleted: @escaping () -> Void = {}, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, tag: ItemListItemTag? = nil) {
self.account = account
self.theme = theme
self.strings = strings
@ -188,6 +193,7 @@ class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
self.sectionId = sectionId
self.style = style
self.editingNameUpdated = editingNameUpdated
self.editingNameCompleted = editingNameCompleted
self.avatarTapped = avatarTapped
self.context = context
self.updatingImage = updatingImage
@ -248,7 +254,7 @@ private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 28.0)!
private let nameFont = Font.medium(19.0)
private let statusFont = Font.regular(15.0)
class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, ItemListItemFocusableNode {
class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, ItemListItemFocusableNode, UITextFieldDelegate {
private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let topStripeNode: ASDisplayNode
@ -270,6 +276,9 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
private var inputFirstField: UITextField?
private var inputSecondField: UITextField?
private var inputFirstClearButton: HighlightableButtonNode?
private var inputSecondClearButton: HighlightableButtonNode?
private var item: ItemListAvatarAndNameInfoItem?
private var layoutWidthAndNeighbors: (width: ListViewItemLayoutParams, neighbors: ItemListNeighbors)?
private var peerPresenceManager: PeerPresenceStatusManager?
@ -547,6 +556,9 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
strongSelf.inputSeparator?.backgroundColor = itemSeparatorColor
strongSelf.callButton.setImage(PresentationResourcesChat.chatInfoCallButtonImage(item.theme), for: [])
strongSelf.inputFirstClearButton?.setImage(generateClearIcon(color: item.theme.list.inputClearButtonColor), for: [])
strongSelf.inputSecondClearButton?.setImage(generateClearIcon(color: item.theme.list.inputClearButtonColor), for: [])
updatedArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.theme)
}
@ -708,6 +720,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
if strongSelf.inputFirstField == nil {
let inputFirstField = TextFieldNodeView()
inputFirstField.delegate = self
inputFirstField.font = Font.regular(17.0)
inputFirstField.autocorrectionType = .no
inputFirstField.returnKeyType = .next
@ -725,8 +738,20 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
strongSelf.inputFirstField?.keyboardAppearance = keyboardAppearance
}
if strongSelf.inputFirstClearButton == nil {
strongSelf.inputFirstClearButton = HighlightableButtonNode()
strongSelf.inputFirstClearButton?.imageNode.displaysAsynchronously = false
strongSelf.inputFirstClearButton?.imageNode.displayWithoutProcessing = true
strongSelf.inputFirstClearButton?.displaysAsynchronously = false
strongSelf.inputFirstClearButton?.setImage(generateClearIcon(color: item.theme.list.inputClearButtonColor), for: [])
strongSelf.inputFirstClearButton?.addTarget(strongSelf, action: #selector(strongSelf.firstClearPressed), forControlEvents: .touchUpInside)
strongSelf.inputFirstClearButton?.isHidden = true
strongSelf.addSubnode(strongSelf.inputFirstClearButton!)
}
if strongSelf.inputSecondField == nil {
let inputSecondField = TextFieldNodeView()
inputSecondField.delegate = self
inputSecondField.font = Font.regular(17.0)
inputSecondField.autocorrectionType = .no
inputSecondField.returnKeyType = .done
@ -744,9 +769,27 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
strongSelf.inputSecondField?.keyboardAppearance = keyboardAppearance
}
if strongSelf.inputSecondClearButton == nil {
strongSelf.inputSecondClearButton = HighlightableButtonNode()
strongSelf.inputSecondClearButton?.imageNode.displaysAsynchronously = false
strongSelf.inputSecondClearButton?.imageNode.displayWithoutProcessing = true
strongSelf.inputSecondClearButton?.displaysAsynchronously = false
strongSelf.inputSecondClearButton?.setImage(generateClearIcon(color: item.theme.list.inputClearButtonColor), for: [])
strongSelf.inputSecondClearButton?.addTarget(strongSelf, action: #selector(strongSelf.secondClearPressed), forControlEvents: .touchUpInside)
strongSelf.inputSecondClearButton?.isHidden = true
strongSelf.addSubnode(strongSelf.inputSecondClearButton!)
}
strongSelf.inputSeparator?.frame = CGRect(origin: CGPoint(x: params.leftInset + 100.0, y: 46.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 100.0, height: separatorHeight))
strongSelf.inputFirstField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 111.0, y: 12.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 111.0 - 8.0, height: 30.0))
strongSelf.inputSecondField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 111.0, y: 52.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 111.0 - 8.0, height: 30.0))
strongSelf.inputFirstField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 111.0, y: 12.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 111.0 - 36.0, height: 30.0))
strongSelf.inputSecondField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 111.0, y: 52.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 111.0 - 36.0, height: 30.0))
if let image = strongSelf.inputFirstClearButton?.image(for: []), let inputFieldFrame = strongSelf.inputFirstField?.frame {
strongSelf.inputFirstClearButton?.frame = CGRect(origin: CGPoint(x: inputFieldFrame.maxX, y: inputFieldFrame.minY + floor((inputFieldFrame.size.height - image.size.height) / 2.0) - 1.0 + UIScreenPixel), size: image.size)
}
if let image = strongSelf.inputSecondClearButton?.image(for: []), let inputFieldFrame = strongSelf.inputSecondField?.frame {
strongSelf.inputSecondClearButton?.frame = CGRect(origin: CGPoint(x: inputFieldFrame.maxX, y: inputFieldFrame.minY + floor((inputFieldFrame.size.height - image.size.height) / 2.0) - 1.0 + UIScreenPixel), size: image.size)
}
if animated && animateIn {
strongSelf.inputSeparator?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
@ -764,6 +807,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
if strongSelf.inputFirstField == nil {
let inputFirstField = TextFieldNodeView()
inputFirstField.delegate = self
inputFirstField.font = Font.regular(17.0)
inputFirstField.autocorrectionType = .no
inputFirstField.attributedText = NSAttributedString(string: title, font: Font.regular(19.0), textColor: item.theme.list.itemPrimaryTextColor)
@ -786,8 +830,23 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
strongSelf.inputFirstField?.keyboardAppearance = keyboardAppearance
}
if strongSelf.inputFirstClearButton == nil {
strongSelf.inputFirstClearButton = HighlightableButtonNode()
strongSelf.inputFirstClearButton?.imageNode.displaysAsynchronously = false
strongSelf.inputFirstClearButton?.imageNode.displayWithoutProcessing = true
strongSelf.inputFirstClearButton?.displaysAsynchronously = false
strongSelf.inputFirstClearButton?.setImage(generateClearIcon(color: item.theme.list.inputClearButtonColor), for: [])
strongSelf.inputFirstClearButton?.addTarget(strongSelf, action: #selector(strongSelf.firstClearPressed), forControlEvents: .touchUpInside)
strongSelf.inputFirstClearButton?.isHidden = true
strongSelf.addSubnode(strongSelf.inputFirstClearButton!)
}
strongSelf.inputSeparator?.frame = CGRect(origin: CGPoint(x: params.leftInset + 100.0, y: 62.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 100.0, height: separatorHeight))
strongSelf.inputFirstField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 102.0, y: 26.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 102.0 - 8.0, height: 35.0))
strongSelf.inputFirstField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 111.0, y: 26.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 111.0 - 36.0, height: 35.0))
if let image = strongSelf.inputFirstClearButton?.image(for: []), let inputFieldFrame = strongSelf.inputFirstField?.frame {
strongSelf.inputFirstClearButton?.frame = CGRect(origin: CGPoint(x: inputFieldFrame.maxX, y: inputFieldFrame.minY + floor((inputFieldFrame.size.height - image.size.height) / 2.0) - 1.0 + UIScreenPixel), size: image.size)
}
if animated && animateIn {
strongSelf.inputSeparator?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
@ -835,6 +894,17 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
} else {
inputFirstField.removeFromSuperview()
}
if let inputFirstClearButton = strongSelf.inputFirstClearButton {
strongSelf.inputFirstClearButton = nil
if animated {
inputFirstClearButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak inputFirstClearButton] _ in
inputFirstClearButton?.removeFromSupernode()
})
} else {
inputFirstClearButton.removeFromSupernode()
}
}
}
if let inputSecondField = strongSelf.inputSecondField {
strongSelf.inputSecondField = nil
@ -845,6 +915,17 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
} else {
inputSecondField.removeFromSuperview()
}
if let inputSecondClearButton = strongSelf.inputSecondClearButton {
strongSelf.inputSecondClearButton = nil
if animated {
inputSecondClearButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak inputSecondClearButton] _ in
inputSecondClearButton?.removeFromSupernode()
})
} else {
inputSecondClearButton.removeFromSupernode()
}
}
}
if animated && animateOut {
strongSelf.statusNode.layer.animateAlpha(from: CGFloat(strongSelf.statusNode.layer.opacity), to: 1.0, duration: 0.3)
@ -928,6 +1009,13 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
}
}
private func updateClearButtonVisibility(_ button: HighlightableButtonNode?, textField: UITextField?) {
guard let button = button, let textField = textField else {
return
}
button.isHidden = !textField.isFirstResponder || (textField.text?.isEmpty ?? true)
}
@objc func textFieldDidChange(_ inputField: UITextField) {
if let item = self.item, let currentEditingName = item.state.editingName {
var editingName: ItemListAvatarAndNameInfoItemName?
@ -942,6 +1030,41 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
item.editingNameUpdated(editingName)
}
}
if inputField == self.inputFirstField {
self.updateClearButtonVisibility(self.inputFirstClearButton, textField: inputField)
} else if inputField == self.inputSecondField {
self.updateClearButtonVisibility(self.inputSecondClearButton, textField: inputField)
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.inputFirstField {
self.updateClearButtonVisibility(self.inputFirstClearButton, textField: textField)
} else if textField == self.inputSecondField {
self.updateClearButtonVisibility(self.inputSecondClearButton, textField: textField)
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == self.inputFirstField {
self.updateClearButtonVisibility(self.inputFirstClearButton, textField: textField)
} else if textField == self.inputSecondField {
self.updateClearButtonVisibility(self.inputSecondClearButton, textField: textField)
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == self.inputFirstField {
if let inputSecondField = self.inputSecondField {
inputSecondField.becomeFirstResponder()
} else {
self.item?.editingNameCompleted()
}
} else if textField == self.inputSecondField {
self.item?.editingNameCompleted()
}
return true
}
@objc func avatarTapGesture(_ recognizer: UITapGestureRecognizer) {
@ -975,6 +1098,16 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
self.item?.call?()
}
@objc func firstClearPressed() {
self.inputFirstField?.text = nil
self.updateClearButtonVisibility(self.inputFirstClearButton, textField: self.inputFirstField)
}
@objc func secondClearPressed() {
self.inputSecondField?.text = nil
self.updateClearButtonVisibility(self.inputSecondClearButton, textField: self.inputSecondField)
}
func focus() {
self.inputFirstField?.becomeFirstResponder()
}

View File

@ -86,6 +86,7 @@ class ItemListMultilineTextItem: ListViewItem, ItemListItem {
private let titleFont = Font.regular(17.0)
private let titleBoldFont = Font.medium(17.0)
private let titleItalicFont = Font.italic(17.0)
private let titleBoldItalicFont = Font.semiboldItalic(17.0)
private let titleFixedFont = Font.regular(17.0)
class ItemListMultilineTextItemNode: ListViewItemNode {
@ -184,7 +185,7 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
}
let entities = generateTextEntities(item.text, enabledTypes: item.enabledEntitiyTypes)
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, fixedFont: titleFixedFont)
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, boldItalicFont: titleBoldItalicFont, fixedFont: titleFixedFont)
let (titleLayout, titleApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))

View File

@ -92,6 +92,7 @@ private let labelFont = Font.regular(14.0)
private let textFont = Font.regular(17.0)
private let textBoldFont = Font.medium(17.0)
private let textItalicFont = Font.italic(17.0)
private let textBoldItalicFont = Font.semiboldItalic(17.0)
private let textFixedFont = Font.regular(17.0)
class ItemListTextWithLabelItemNode: ListViewItemNode {
@ -208,7 +209,7 @@ class ItemListTextWithLabelItemNode: ListViewItemNode {
case .highlighted:
baseColor = item.theme.list.itemHighlightedColor
}
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, fixedFont: textFixedFont)
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: item.multiline ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let contentSize = CGSize(width: params.width, height: textLayout.size.height + 39.0)

View File

@ -189,7 +189,8 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA
settings.theme = .builtin(.day)
if presentationState.userInfo != 0 {
settings.themeAccentColor = presentationState.userInfo
//themeSpecificAccentColors: current.themeSpecificAccentColors
//settings.themeAccentColor = presentationState.userInfo
}
settings.chatWallpaper = .color(0xffffff)
case 2:
@ -214,7 +215,8 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA
settings.fontSize = fontSizeMap[presentationState.fontSize] ?? .regular
if presentationState.userInfo != 0 {
settings.themeAccentColor = presentationState.userInfo
//themeSpecificAccentColors: current.themeSpecificAccentColors
//settings.themeAccentColor = presentationState.userInfo
}
}

View File

@ -11,7 +11,6 @@ public enum NavigateToChatKeepStack {
}
public func navigateToChatController(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) {
var found = false
var isFirst = true
for controller in navigationController.viewControllers.reversed() {

View File

@ -129,6 +129,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case archiveChatTips = 10
case archiveIntroDismissed = 11
case callsTabTip = 12
case cellularDataPermissionWarning = 13
var key: ValueBoxKey {
let v = ValueBoxKey(length: 4)
@ -137,6 +138,21 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
}
}
private extension PermissionKind {
var noticeKey: NoticeEntryKey? {
switch self {
case .contacts:
return ApplicationSpecificNoticeKeys.contactsPermissionWarning()
case .notifications:
return ApplicationSpecificNoticeKeys.notificationsPermissionWarning()
case .cellularData:
return ApplicationSpecificNoticeKeys.cellularDataPermissionWarning()
default:
return nil
}
}
}
private struct ApplicationSpecificNoticeKeys {
private static let botPaymentLiabilityNamespace: Int32 = 1
private static let globalNamespace: Int32 = 2
@ -195,6 +211,10 @@ private struct ApplicationSpecificNoticeKeys {
return NoticeEntryKey(namespace: noticeNamespace(namespace: permissionsNamespace), key: ApplicationSpecificGlobalNotice.notificationsPermissionWarning.key)
}
static func cellularDataPermissionWarning() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: permissionsNamespace), key: ApplicationSpecificGlobalNotice.cellularDataPermissionWarning.key)
}
static func volumeButtonToUnmuteTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.volumeButtonToUnmuteTip.key)
}
@ -408,20 +428,19 @@ public struct ApplicationSpecificNotice {
}
}
public static func contactsPermissionWarningKey() -> NoticeEntryKey {
return ApplicationSpecificNoticeKeys.contactsPermissionWarning()
public static func permissionWarningKey(permission: PermissionKind) -> NoticeEntryKey? {
return permission.noticeKey
}
public static func setContactsPermissionWarning(accountManager: AccountManager, value: Int32) {
public static func setPermissionWarning(accountManager: AccountManager, permission: PermissionKind, value: Int32) {
guard let noticeKey = permission.noticeKey else {
return
}
let _ = accountManager.transaction { transaction -> Void in
transaction.setNotice(ApplicationSpecificNoticeKeys.contactsPermissionWarning(), ApplicationSpecificTimestampNotice(value: value))
transaction.setNotice(noticeKey, ApplicationSpecificTimestampNotice(value: value))
}.start()
}
public static func notificationsPermissionWarningKey() -> NoticeEntryKey {
return ApplicationSpecificNoticeKeys.notificationsPermissionWarning()
}
public static func getTimestampValue(_ entry: NoticeEntry) -> Int32? {
if let value = entry as? ApplicationSpecificTimestampNotice {
return value.value
@ -430,12 +449,6 @@ public struct ApplicationSpecificNotice {
}
}
public static func setNotificationsPermissionWarning(accountManager: AccountManager, value: Int32) {
let _ = accountManager.transaction { transaction -> Void in
transaction.setNotice(ApplicationSpecificNoticeKeys.notificationsPermissionWarning(), ApplicationSpecificTimestampNotice(value: value))
}.start()
}
static func getVolumeButtonToUnmute(accountManager: AccountManager) -> Signal<Bool, NoError> {
return accountManager.transaction { transaction -> Bool in
if let _ = transaction.getNotice(ApplicationSpecificNoticeKeys.volumeButtonToUnmuteTip()) as? ApplicationSpecificBoolNotice {

View File

@ -836,7 +836,7 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
case .denied, .restricted:
context.sharedContext.applicationBindings.openSettings()
case .unreachable:
ApplicationSpecificNotice.setNotificationsPermissionWarning(accountManager: context.sharedContext.accountManager, value: Int32(Date().timeIntervalSince1970))
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: Int32(Date().timeIntervalSince1970))
context.sharedContext.applicationBindings.openSettings()
default:
break
@ -845,7 +845,7 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
}, suppressWarning: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Notifications_PermissionsSuppressWarningTitle, text: presentationData.strings.Notifications_PermissionsSuppressWarningText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Notifications_PermissionsKeepDisabled, action: {
ApplicationSpecificNotice.setNotificationsPermissionWarning(accountManager: context.sharedContext.accountManager, value: Int32(Date().timeIntervalSince1970))
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .notifications, value: Int32(Date().timeIntervalSince1970))
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Notifications_PermissionsEnable, action: {
context.sharedContext.applicationBindings.openSettings()
})]), nil)
@ -1051,7 +1051,7 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
notificationsWarningSuppressed.set(.single(true)
|> then(
context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.notificationsPermissionWarningKey())
context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .notifications)!)
|> map { noticeView -> Bool in
let timestamp = noticeView.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
if let timestamp = timestamp, timestamp > 0 {

View File

@ -5,7 +5,7 @@ import TelegramCore
import MobileCoreServices
private func rtfStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> String {
let test = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), fixedFont: Font.monospace(14.0), underlineLinks: false, external: true)
let test = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), underlineLinks: false, external: true)
if let data = try? test.data(from: NSRange(location: 0, length: test.length), documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.rtf]) {
if var rtf = String(data: data, encoding: .windowsCP1252) {

View File

@ -389,7 +389,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
}
}
}
dataPromise.set(dataSignal)
dataPromise.set(.single(nil) |> then(dataSignal))
let previousData = Atomic<PeersNearbyData?>(value: nil)
let displayLoading: Signal<Bool, NoError> = .single(false)

View File

@ -36,7 +36,7 @@ public enum PermissionState: Equatable {
case contacts(status: PermissionRequestStatus)
case notifications(status: PermissionRequestStatus)
case siri(status: PermissionRequestStatus)
case cellularData
case cellularData(status: PermissionRequestStatus)
case nearbyLocation(status: PermissionRequestStatus)
var kind: PermissionKind {
@ -62,19 +62,21 @@ public enum PermissionState: Equatable {
return status
case let .siri(status):
return status
case .cellularData:
return .unreachable
case let .cellularData(status):
return status
case let .nearbyLocation(status):
return status
}
}
}
public func requiredPermissions(context: AccountContext) -> Signal<(PermissionState, PermissionState, PermissionState), NoError> {
public func requiredPermissions(context: AccountContext) -> Signal<(contacts: PermissionState, notifications: PermissionState, cellularData: PermissionState, siri: PermissionState), NoError> {
return combineLatest(DeviceAccess.authorizationStatus(subject: .contacts), DeviceAccess.authorizationStatus(applicationInForeground: context.sharedContext.applicationBindings.applicationInForeground, subject: .notifications), DeviceAccess.authorizationStatus(siriAuthorization: {
return context.sharedContext.applicationBindings.siriAuthorization()
}, subject: .cellularData), DeviceAccess.authorizationStatus(siriAuthorization: {
return context.sharedContext.applicationBindings.siriAuthorization()
}, subject: .siri))
|> map { contactsStatus, notificationsStatus, siriStatus in
return (.contacts(status: PermissionRequestStatus(accessType: contactsStatus)), .notifications(status: PermissionRequestStatus(accessType: notificationsStatus)), .siri(status: PermissionRequestStatus(accessType: siriStatus)))
|> map { contactsStatus, notificationsStatus, cellularDataStatus, siriStatus in
return (.contacts(status: PermissionRequestStatus(accessType: contactsStatus)), .notifications(status: PermissionRequestStatus(accessType: notificationsStatus)), .cellularData(status: PermissionRequestStatus(accessType: cellularDataStatus)), .siri(status: PermissionRequestStatus(accessType: siriStatus)))
}
}

View File

@ -108,6 +108,11 @@ public final class PermissionController : ViewController {
self.state = state
if case let .permission(permission) = state, let state = permission {
if case .nearbyLocation = state {
} else {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Permissions_Skip, style: .plain, target: self, action: #selector(PermissionController.nextPressed))
}
switch state {
case let .contacts(status):
self.splitTest?.addEvent(.ContactsModalRequest)
@ -170,11 +175,13 @@ public final class PermissionController : ViewController {
}
case .cellularData:
self.allow = { [weak self] in
self?.proceed?(true)
if let strongSelf = self {
strongSelf.openAppSettings()
strongSelf.proceed?(true)
}
}
case let .nearbyLocation(status):
self.title = self.presentationData.strings.Permissions_PeopleNearbyTitle_v0
self.navigationItem.rightBarButtonItem = nil
self.allow = { [weak self] in
if let strongSelf = self {

View File

@ -136,7 +136,7 @@ struct PresentationResourcesItemList {
static func itemListClearInputIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListClearInputIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.rootController.activeNavigationSearchBar.inputIconColor)
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.list.inputClearButtonColor)
})
}

View File

@ -138,7 +138,7 @@ private enum ProxySettingsEntry: ItemListNodeEntry {
case let .connectionHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .connectionServer(theme, placeholder, text):
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: text, placeholder: placeholder, sectionId: self.section, textUpdated: { value in
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: text, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), sectionId: self.section, textUpdated: { value in
arguments.updateState { current in
var state = current
state.host = value

View File

@ -1052,7 +1052,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|> then(
contextValue.get()
|> mapToSignal { context -> Signal<Bool, NoError> in
return context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.notificationsPermissionWarningKey())
return context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .notifications)!)
|> map { noticeView -> Bool in
let timestamp = noticeView.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
if let timestamp = timestamp, timestamp > 0 {

View File

@ -92,6 +92,8 @@ final class SinglePhoneInputNode: ASDisplayNode, UITextFieldDelegate {
}
}
var numberUpdated: ((String) -> Void)?
var beginEditing: (() -> Void)?
var endEditing: (() -> Void)?
private let phoneFormatter = InteractivePhoneFormatter()
@ -131,6 +133,14 @@ final class SinglePhoneInputNode: ASDisplayNode, UITextFieldDelegate {
return self.enableEditing
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.beginEditing?()
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.endEditing?()
}
private func updateNumberFromTextFields() {
guard let numberField = self.numberField else {
return

View File

@ -374,7 +374,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
if self.currentItems.isEmpty && !updatedItems.isEmpty {
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: Font.medium(20.0), linkFont: Font.medium(20.0), boldFont: Font.medium(20.0), italicFont: Font.medium(20.0), fixedFont: Font.medium(20.0))
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: Font.medium(20.0), linkFont: Font.medium(20.0), boldFont: Font.medium(20.0), italicFont: Font.medium(20.0), boldItalicFont: Font.medium(20.0), fixedFont: Font.medium(20.0))
animateIn = true
}
transaction = StickerPackPreviewGridTransaction(previousList: self.currentItems, list: updatedItems, account: self.context.account, interaction: self.interaction)

View File

@ -36,6 +36,8 @@ func chatInputStateStringWithAppliedEntities(_ text: String, entities: [MessageT
string.addAttribute(ChatTextInputAttributes.monospace, value: true as NSNumber, range: range)
case .Strikethrough:
string.addAttribute(ChatTextInputAttributes.strikethrough, value: true as NSNumber, range: range)
case .Underline:
string.addAttribute(ChatTextInputAttributes.underline, value: true as NSNumber, range: range)
default:
break
}
@ -43,7 +45,7 @@ func chatInputStateStringWithAppliedEntities(_ text: String, entities: [MessageT
return string
}
func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, fixedFont: UIFont, underlineLinks: Bool = true, external: Bool = false) -> NSAttributedString {
func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, underlineLinks: Bool = true, external: Bool = false) -> NSAttributedString {
var nsString: NSString?
let string = NSMutableAttributedString(string: text, attributes: [NSAttributedStringKey.font: baseFont, NSAttributedStringKey.foregroundColor: baseColor])
var skipEntity = false
@ -52,6 +54,8 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
if linkColor.isEqual(baseColor) {
underlineAllLinks = true
}
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]
for i in 0 ..< entities.count {
if skipEntity {
skipEntity = false
@ -108,9 +112,17 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
string.addAttribute(NSAttributedStringKey(rawValue: TelegramTextAttributes.URL), value: url, range: range)
}
case .Bold:
string.addAttribute(NSAttributedStringKey.font, value: boldFont, range: range)
if let fontAttribute = fontAttributes[range] {
fontAttributes[range] = fontAttribute.union(.bold)
} else {
fontAttributes[range] = .bold
}
case .Italic:
string.addAttribute(NSAttributedStringKey.font, value: italicFont, range: range)
if let fontAttribute = fontAttributes[range] {
fontAttributes[range] = fontAttribute.union(.italic)
} else {
fontAttributes[range] = .italic
}
case .Mention:
string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range)
if underlineLinks && underlineAllLinks {
@ -125,6 +137,8 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
string.addAttribute(NSAttributedStringKey(rawValue: TelegramTextAttributes.PeerTextMention), value: nsString!.substring(with: range), range: range)
case .Strikethrough:
string.addAttribute(NSAttributedStringKey.strikethroughStyle, value: NSUnderlineStyle.styleSingle.rawValue as NSNumber, range: range)
case .Underline:
string.addAttribute(NSAttributedStringKey.underlineStyle, value: NSUnderlineStyle.styleSingle.rawValue as NSNumber, range: range)
case let .TextMention(peerId):
string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range)
if underlineLinks && underlineAllLinks {
@ -191,6 +205,20 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
default:
break
}
for (range, fontAttributes) in fontAttributes {
var font: UIFont?
if fontAttributes == [.bold, .italic] {
font = boldItalicFont
} else if fontAttributes == [.bold] {
font = boldFont
} else if fontAttributes == [.italic] {
font = italicFont
}
if let font = font {
string.addAttribute(NSAttributedStringKey.font, value: font, range: range)
}
}
}
return string
}

View File

@ -8,7 +8,7 @@ import DeviceAccess
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
present(textAlertController(context: context, title: presentationData.strings.Contacts_PermissionsSuppressWarningTitle, text: presentationData.strings.Contacts_PermissionsSuppressWarningText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Contacts_PermissionsKeepDisabled, action: {
ApplicationSpecificNotice.setContactsPermissionWarning(accountManager: context.sharedContext.accountManager, value: Int32(Date().timeIntervalSince1970))
ApplicationSpecificNotice.setPermissionWarning(accountManager: context.sharedContext.accountManager, permission: .contacts, value: Int32(Date().timeIntervalSince1970))
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Contacts_PermissionsEnable, action: {
let _ = (DeviceAccess.authorizationStatus(subject: .contacts)
|> take(1)

View File

@ -57,7 +57,7 @@ final class TermsOfServiceControllerNode: ViewControllerTracingNode {
self.contentTextNode = ImmediateTextNode()
self.contentTextNode.displaysAsynchronously = false
self.contentTextNode.maximumNumberOfLines = 0
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primary, linkColor: theme.accent, baseFont: Font.regular(15.0), linkFont: Font.regular(15.0), boldFont: Font.semibold(15.0), italicFont: Font.italic(15.0), fixedFont: Font.monospace(15.0))
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primary, linkColor: theme.accent, baseFont: Font.regular(15.0), linkFont: Font.regular(15.0), boldFont: Font.semibold(15.0), italicFont: Font.italic(15.0), boldItalicFont: Font.semiboldItalic(15.0), fixedFont: Font.monospace(15.0))
self.toolbarNode = ASDisplayNode()
self.toolbarSeparatorNode = ASDisplayNode()

View File

@ -185,7 +185,7 @@ final class ThemeGridController: ViewController {
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
themeSpecificChatWallpapers[current.theme.index] = fallbackWallpaper
return PresentationThemeSettings(chatWallpaper: fallbackWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: fallbackWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})).start()
break
}
@ -257,7 +257,7 @@ final class ThemeGridController: ViewController {
} else {
wallpaper = .builtin(WallpaperSettings())
}
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: [:], fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: [:], fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})
}).start()

View File

@ -5,33 +5,26 @@ import AsyncDisplayKit
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? {
return generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in
private func generateSwatchImage(color: PresentationThemeAccentColor, selected: Bool) -> UIImage? {
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.setFillColor(theme.list.itemBlocksBackgroundColor.cgColor)
context.fill(bounds)
context.setBlendMode(.clear)
context.fillEllipse(in: bounds)
context.setBlendMode(.normal)
context.clear(bounds)
let fillColor = UIColor(rgb: UInt32(bitPattern: color.color))
let strokeColor = UIColor(rgb: UInt32(bitPattern: color.baseColor.colorValue))
context.setFillColor(fillColor.cgColor)
context.setStrokeColor(strokeColor.cgColor)
context.setLineWidth(2.0)
let lineWidth: CGFloat
if selected {
var accentColor = theme.list.itemAccentColor
if accentColor.rgb == UIColor.white.rgb {
accentColor = UIColor(rgb: 0x999999)
}
context.setStrokeColor(accentColor.cgColor)
lineWidth = 2.0
context.fillEllipse(in: bounds.insetBy(dx: 4.0, dy: 4.0))
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
} else {
context.setStrokeColor(theme.list.disclosureArrowColor.withAlphaComponent(0.4).cgColor)
lineWidth = 1.0
}
if bordered || selected {
context.setLineWidth(lineWidth)
context.strokeEllipse(in: bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0))
context.fillEllipse(in: bounds)
}
})?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
}
@ -40,18 +33,18 @@ class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
var sectionId: ItemListSectionId
let theme: PresentationTheme
let strings: PresentationStrings
let colors: [UIColor]
let currentColor: UIColor
let updated: (UIColor) -> Void
let colors: [PresentationThemeBaseColor]
let currentColor: PresentationThemeAccentColor
let updated: (PresentationThemeAccentColor) -> Void
let toggleSlider: () -> Void
let tag: ItemListItemTag?
init(theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, colors: [UIColor], currentColor: UIColor, updated: @escaping (UIColor) -> Void, tag: ItemListItemTag? = nil) {
init(theme: PresentationTheme, sectionId: ItemListSectionId, colors: [PresentationThemeBaseColor], currentColor: PresentationThemeAccentColor, updated: @escaping (PresentationThemeAccentColor) -> Void, toggleSlider: @escaping () -> Void, tag: ItemListItemTag? = nil) {
self.theme = theme
self.strings = strings
self.colors = colors
self.currentColor = currentColor
self.updated = updated
self.toggleSlider = toggleSlider
self.tag = tag
self.sectionId = sectionId
}
@ -92,8 +85,6 @@ class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
private final class ThemeSettingsAccentColorNode : ASDisplayNode {
private let iconNode: ASImageNode
private let overlayNode: ASImageNode
private let textNode: ASTextNode
private var action: (() -> Void)?
override init() {
@ -101,25 +92,13 @@ private final class ThemeSettingsAccentColorNode : ASDisplayNode {
self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 62.0, height: 62.0))
self.iconNode.isLayerBacked = true
self.overlayNode = ASImageNode()
self.overlayNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 62.0, height: 62.0))
self.overlayNode.isLayerBacked = true
self.textNode = ASTextNode()
self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = true
super.init()
self.addSubnode(self.iconNode)
self.addSubnode(self.overlayNode)
self.addSubnode(self.textNode)
}
func setup(theme: PresentationTheme, icon: UIImage, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void) {
self.iconNode.image = icon
self.textNode.attributedText = title
self.overlayNode.image = generateBorderImage(theme: theme, bordered: bordered, selected: selected)
func setup(color: PresentationThemeAccentColor, selected: Bool, action: @escaping () -> Void) {
self.iconNode.image = generateSwatchImage(color: color, selected: selected)
self.action = {
action()
}
@ -139,12 +118,8 @@ private final class ThemeSettingsAccentColorNode : ASDisplayNode {
override func layout() {
super.layout()
let bounds = self.bounds
self.iconNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 14.0), size: CGSize(width: 62.0, height: 62.0))
self.overlayNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 14.0), size: CGSize(width: 62.0, height: 62.0))
self.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 14.0 + 60.0 + 4.0 + 9.0), size: CGSize(width: bounds.size.width, height: 16.0))
self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 40.0))
}
}
@ -190,6 +165,15 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
self.scrollNode.view.showsHorizontalScrollIndicator = false
}
private func scrollToNode(_ node: ThemeSettingsAccentColorNode, animated: Bool) {
let bounds = self.scrollNode.view.bounds
let frame = node.frame.insetBy(dx: -48.0, dy: 0.0)
if frame.minX < bounds.minX || frame.maxX > bounds.maxX {
self.scrollNode.view.scrollRectToVisible(frame, animated: animated)
}
}
func asyncLayout() -> (_ item: ThemeSettingsAccentColorItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentItem = self.item
@ -197,14 +181,13 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
var themeUpdated = false
if currentItem?.theme !== item.theme {
themeUpdated = true
}
let contentSize: CGSize
let insets: UIEdgeInsets
let separatorHeight = UIScreenPixel
contentSize = CGSize(width: params.width, height: 116.0)
contentSize = CGSize(width: params.width, height: 60.0)
insets = itemListNeighborsGroupedInsets(neighbors)
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
@ -230,33 +213,36 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
}
switch neighbors.top {
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
strongSelf.topStripeNode.isHidden = false
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
strongSelf.topStripeNode.isHidden = false
}
let bottomStripeInset: CGFloat
let bottomStripeOffset: CGFloat
switch neighbors.bottom {
case .sameSection(false):
bottomStripeInset = params.leftInset + 16.0
bottomStripeOffset = -separatorHeight
default:
bottomStripeInset = 0.0
bottomStripeOffset = 0.0
case .sameSection(false):
bottomStripeInset = params.leftInset + 16.0
bottomStripeOffset = -separatorHeight
default:
bottomStripeInset = 0.0
bottomStripeOffset = 0.0
}
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
strongSelf.scrollNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: CGSize(width: layoutSize.width, height: layoutSize.height))
strongSelf.scrollNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layoutSize.width, height: layoutSize.height))
let nodeInset: CGFloat = 4.0
let nodeSize = CGSize(width: 80.0, height: 112.0)
let nodeInset: CGFloat = 15.0
let nodeSize = CGSize(width: 40.0, height: 40.0)
var nodeOffset = nodeInset
var updated = false
var selectedNode: ThemeSettingsAccentColorNode?
var i = 0
for icon in item.colors {
for color in item.colors {
let imageNode: ThemeSettingsAccentColorNode
if strongSelf.nodes.count > i {
imageNode = strongSelf.nodes[i]
@ -264,41 +250,27 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
imageNode = ThemeSettingsAccentColorNode()
strongSelf.nodes.append(imageNode)
strongSelf.scrollNode.addSubnode(imageNode)
updated = true
}
// if let image = UIImage(named: icon.imageName, in: Bundle.main, compatibleWith: nil) {
// let selected = icon.name == item.currentIconName
//
// var name = "Icon"
// var bordered = true
// switch icon.name {
// case "Blue":
// name = item.strings.Appearance_AppIconDefault
// case "Black":
// name = item.strings.Appearance_AppIconDefaultX
// case "BlueClassic":
// name = item.strings.Appearance_AppIconClassic
// case "BlackClassic":
// name = item.strings.Appearance_AppIconClassicX
// case "BlueFilled":
// name = item.strings.Appearance_AppIconFilled
// bordered = false
// case "BlackFilled":
// name = item.strings.Appearance_AppIconFilledX
// bordered = false
// case "WhiteFilled":
// name = " White"
// default:
// break
// }
//
// imageNode.setup(theme: item.theme, icon: image, title: NSAttributedString(string: name, font: textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: bordered, selected: selected, action: {
// item.updated(icon.name)
// })
// }
let accentColor: PresentationThemeAccentColor
let selected = item.currentColor.baseColor == color
if selected {
accentColor = item.currentColor
selectedNode = imageNode
} else {
accentColor = PresentationThemeAccentColor(baseColor: color, value: 0.5)
}
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize)
nodeOffset += nodeSize.width + 15.0
imageNode.setup(color: accentColor, selected: selected, action: { [weak self, weak imageNode] in
item.updated(accentColor)
if let imageNode = imageNode {
self?.scrollToNode(imageNode, animated: true)
}
})
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 10.0), size: nodeSize)
nodeOffset += nodeSize.width + 18.0
i += 1
}
@ -309,6 +281,10 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.scrollNode.view.contentSize = contentSize
}
}
if updated, let selectedNode = selectedNode {
strongSelf.scrollToNode(selectedNode, animated: false)
}
}
})
}

View File

@ -9,7 +9,7 @@ import TelegramUIPreferences
private final class ThemeSettingsControllerArguments {
let context: AccountContext
let selectTheme: (Int32) -> Void
let selectTheme: (PresentationThemeReference) -> Void
let selectFontSize: (PresentationFontSize) -> Void
let openWallpaperSettings: () -> Void
let openAccentColor: (Int32) -> Void
@ -18,7 +18,7 @@ private final class ThemeSettingsControllerArguments {
let disableAnimations: (Bool) -> Void
let selectAppIcon: (String) -> Void
init(context: AccountContext, selectTheme: @escaping (Int32) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, openAccentColor: @escaping (Int32) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void) {
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, openAccentColor: @escaping (Int32) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void) {
self.context = context
self.selectTheme = selectTheme
self.selectFontSize = selectFontSize
@ -62,9 +62,9 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
case fontSize(PresentationTheme, PresentationFontSize)
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder)
case wallpaper(PresentationTheme, String)
case accentColor(PresentationTheme, String, Int32)
case accentColor(PresentationTheme, String, PresentationThemeAccentColor?)
case autoNightTheme(PresentationTheme, String, String)
case themeItem(PresentationTheme, PresentationStrings, [PresentationBuiltinThemeReference], PresentationBuiltinThemeReference, UIColor?)
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor])
case iconHeader(PresentationTheme, String)
case iconItem(PresentationTheme, PresentationStrings, [PresentationAppIcon], String?)
case otherHeader(PresentationTheme, String)
@ -228,18 +228,28 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
arguments.openWallpaperSettings()
})
case let .accentColor(theme, text, color):
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: "", labelStyle: .color(UIColor(rgb: UInt32(bitPattern: color))), sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openAccentColor(color)
let colorValue = color?.baseColor.colorValue ?? defaultDayAccentColor
let accentColor = UIColor(rgb: UInt32(bitPattern: colorValue))
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: "", labelStyle: .color(accentColor), sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openAccentColor(colorValue)
}, tag: ThemeSettingsEntryTag.accentColor)
// return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, colors: PresentationThemeBaseColor.allCases, currentColor: color ?? PresentationThemeAccentColor(baseColor: .blue, value: 0.5), updated: { color in
// let _ = updatePresentationThemeSettingsInteractively(accountManager: arguments.context.sharedContext.accountManager, { current in
// var themeSpecificAccentColors = current.themeSpecificAccentColors
// themeSpecificAccentColors[current.theme.index] = color
// return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
// }).start()
// }, toggleSlider: {
// })
case let .autoNightTheme(theme, text, value):
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openAutoNightTheme()
})
case let .themeListHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .themeItem(theme, strings, themes, currentTheme, themeAccentColor):
return ThemeSettingsThemeItem(theme: theme, strings: strings, sectionId: self.section, themes: themes.map { ($0, $0 == .day ? themeAccentColor : nil) }, currentTheme: currentTheme, updated: { theme in
arguments.selectTheme(theme.rawValue)
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors):
return ThemeSettingsThemeItem(theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updated: { theme in
arguments.selectTheme(theme)
})
case let .iconHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
@ -263,16 +273,16 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
}
}
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeAccentColor: Int32?, autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], 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()))
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder))
if case let .builtin(theme) = theme.name {
entries.append(.themeItem(presentationData.theme, presentationData.strings, [.dayClassic, .day, .nightAccent, .nightGrayscale], theme.reference, themeAccentColor != nil ? UIColor(rgb: UInt32(bitPattern: themeAccentColor!)) : nil))
if case .builtin = themeReference {
entries.append(.themeItem(presentationData.theme, presentationData.strings, [.builtin(.dayClassic), .builtin(.day), .builtin(.nightAccent), .builtin(.nightGrayscale)], themeReference, themeSpecificAccentColors))
}
if theme.name == .builtin(.day) {
entries.append(.accentColor(presentationData.theme, strings.Appearance_AccentColor, themeAccentColor ?? defaultDayAccentColor))
entries.append(.accentColor(presentationData.theme, strings.Appearance_AccentColor, themeSpecificAccentColors[themeReference.index]))
}
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
@ -306,8 +316,6 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
return entries
}
private let themeColors = [UIColor(rgb: 0x007aff), UIColor(rgb: 0x70bb23), UIColor(rgb: 0xeb6ca4), UIColor(rgb: 0xf08200), UIColor(rgb: 0x9472ee), UIColor(rgb: 0xd33213), UIColor(rgb: 0xedb400), UIColor(rgb: 0x6d839e), UIColor(rgb: 0x000000)]
public func themeSettingsController(context: AccountContext, focusOnItemTag: ThemeSettingsEntryTag? = nil) -> ViewController {
var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController) -> Void)?
@ -318,19 +326,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let currentAppIconName = ValuePromise<String?>()
currentAppIconName.set(context.sharedContext.applicationBindings.getAlternateIconName() ?? "Blue")
let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { index in
let theme: PresentationThemeReference
switch index {
case 1:
theme = .builtin(.nightGrayscale)
case 2:
theme = .builtin(.day)
case 3:
theme = .builtin(.nightAccent)
default:
theme = .builtin(.dayClassic)
}
let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { theme in
let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
let current: PresentationThemeSettings
@ -345,51 +341,78 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[theme.index] {
wallpaper = themeSpecificWallpaper
} else {
switch index {
case 1:
wallpaper = .color(0x000000)
case 2:
wallpaper = .color(0xffffff)
case 3:
wallpaper = .color(0x18222d)
default:
wallpaper = .builtin(WallpaperSettings())
if case let .builtin(theme) = theme {
switch theme {
case .day:
wallpaper = .color(0xffffff)
case .dayClassic:
wallpaper = .builtin(WallpaperSettings())
case .nightAccent:
wallpaper = .color(0x18222d)
case .nightGrayscale:
wallpaper = .color(0x000000)
}
} else {
wallpaper = .builtin(WallpaperSettings())
}
}
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})
}).start()
}, selectFontSize: { size in
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
}).start()
}, openWallpaperSettings: {
pushControllerImpl?(ThemeGridController(context: context))
}, openAccentColor: { color in
presentControllerImpl?(ThemeAccentColorActionSheet(context: context, currentValue: color, applyValue: { color in
let themeAccentColor: PresentationThemeBaseColor
switch color {
case 0xf83b4c:
themeAccentColor = .red
case 0xff7519:
themeAccentColor = .orange
case 0xeba239:
themeAccentColor = .yellow
case 0x29b327:
themeAccentColor = .green
case 0x00c2ed:
themeAccentColor = .cyan
case 0x007ee5:
themeAccentColor = .blue
case 0x7748ff:
themeAccentColor = .purple
case 0xff5da2:
themeAccentColor = .pink
default:
themeAccentColor = .blue
}
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: color, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
var themeSpecificAccentColors = current.themeSpecificAccentColors
themeSpecificAccentColors[current.theme.index] = PresentationThemeAccentColor(baseColor: themeAccentColor, value: 0.5)
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
}).start()
}))
}, openAutoNightTheme: {
pushControllerImpl?(themeAutoNightSettingsController(context: context))
}, toggleLargeEmoji: { largeEmoji in
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: current.disableAnimations)
}).start()
}, disableAnimations: { disabled in
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: disabled)
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: disabled)
}).start()
}, selectAppIcon: { name in
currentAppIconName.set(name)
context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { _ in
})
})
let previousTheme = Atomic<PresentationTheme?>(value: nil)
let signal = combineLatest(context.sharedContext.presentationData |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) |> deliverOnMainQueue, availableAppIcons, currentAppIconName.get() |> deliverOnMainQueue)
|> map { presentationData, sharedData, availableAppIcons, currentAppIconName -> (ItemListControllerState, (ItemListNodeState<ThemeSettingsControllerEntry>, ThemeSettingsControllerEntry.ItemGenerationArguments)) in
let theme: PresentationTheme
@ -410,7 +433,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
case .nightAccent:
theme = defaultDarkAccentPresentationTheme
case .day:
theme = makeDefaultDayPresentationTheme(accentColor: settings.themeAccentColor ?? defaultDayAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor)
theme = makeDefaultDayPresentationTheme(accentColor: settings.themeSpecificAccentColors[settings.theme.index]?.color ?? defaultDayAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor)
}
}
wallpaper = settings.chatWallpaper
@ -421,7 +444,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
disableAnimations = settings.disableAnimations
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeAccentColor: settings.themeAccentColor, 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: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, 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))
}

View File

@ -37,8 +37,11 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
})?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
}
private func generateThemeIconImage(theme: PresentationBuiltinThemeReference, accentColor: UIColor?) -> UIImage {
private func generateThemeIconImage(theme: PresentationThemeReference, accentColor: Int32?) -> UIImage {
return generateImage(CGSize(width: 98.0, height: 62.0), rotatedContext: { size, context in
guard case let .builtin(theme) = theme else {
return
}
let bounds = CGRect(origin: CGPoint(), size: size)
let background: UIColor
@ -53,7 +56,7 @@ private func generateThemeIconImage(theme: PresentationBuiltinThemeReference, ac
background = .white
incomingBubble = UIColor(rgb: 0xd5dde6)
if let accentColor = accentColor {
outgoingBubble = accentColor
outgoingBubble = UIColor(rgb: UInt32(bitPattern: accentColor))
} else {
outgoingBubble = UIColor(rgb: 0x007aff)
}
@ -66,7 +69,7 @@ private func generateThemeIconImage(theme: PresentationBuiltinThemeReference, ac
incomingBubble = UIColor(rgb: 0x32475e)
outgoingBubble = UIColor(rgb: 0x3d6a97)
}
context.setFillColor(background.cgColor)
context.fill(bounds)
@ -91,15 +94,17 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
let theme: PresentationTheme
let strings: PresentationStrings
let themes: [(PresentationBuiltinThemeReference, UIColor?)]
let currentTheme: PresentationBuiltinThemeReference
let updated: (PresentationBuiltinThemeReference) -> Void
let themes: [PresentationThemeReference]
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
let currentTheme: PresentationThemeReference
let updated: (PresentationThemeReference) -> Void
let tag: ItemListItemTag?
init(theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [(PresentationBuiltinThemeReference, UIColor?)], currentTheme: PresentationBuiltinThemeReference, updated: @escaping (PresentationBuiltinThemeReference) -> Void, tag: ItemListItemTag? = nil) {
init(theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updated: @escaping (PresentationThemeReference) -> Void, tag: ItemListItemTag? = nil) {
self.theme = theme
self.strings = strings
self.themes = themes
self.themeSpecificAccentColors = themeSpecificAccentColors
self.currentTheme = currentTheme
self.updated = updated
self.tag = tag
@ -312,7 +317,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
var selectedNode: ThemeSettingsThemeItemIconNode?
var i = 0
for (theme, accentColor) in item.themes {
for theme in item.themes {
let imageNode: ThemeSettingsThemeItemIconNode
if strongSelf.nodes.count > i {
imageNode = strongSelf.nodes[i]
@ -328,27 +333,34 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
selectedNode = imageNode
}
let name: String
switch theme {
case .dayClassic:
name = item.strings.Appearance_ThemeCarouselClassic
case .day:
name = item.strings.Appearance_ThemeCarouselDay
case .nightGrayscale:
name = item.strings.Appearance_ThemeCarouselNight
case .nightAccent:
name = item.strings.Appearance_ThemeCarouselNightBlue
let name: String?
if case let .builtin(theme) = theme {
switch theme {
case .dayClassic:
name = item.strings.Appearance_ThemeCarouselClassic
case .day:
name = item.strings.Appearance_ThemeCarouselDay
case .nightGrayscale:
name = item.strings.Appearance_ThemeCarouselNight
case .nightAccent:
name = item.strings.Appearance_ThemeCarouselNightBlue
}
} else {
name = nil
}
imageNode.setup(theme: item.theme, icon: generateThemeIconImage(theme: theme, accentColor: accentColor), title: NSAttributedString(string: name, font: textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { [weak self, weak imageNode] in
item.updated(theme)
if let imageNode = imageNode {
self?.scrollToNode(imageNode, animated: true)
}
})
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize)
nodeOffset += nodeSize.width + 2.0
if let name = name {
imageNode.setup(theme: item.theme, icon: generateThemeIconImage(theme: theme, accentColor: item.themeSpecificAccentColors[theme.index]?.color), title: NSAttributedString(string: name, font: textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { [weak self, weak imageNode] in
item.updated(theme)
if let imageNode = imageNode {
self?.scrollToNode(imageNode, animated: true)
}
})
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize)
nodeOffset += nodeSize.width + 2.0
}
i += 1
}

View File

@ -5,6 +5,10 @@ import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
private func generateClearIcon(color: UIColor) -> UIImage? {
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color)
}
struct UserInfoEditingPhoneItemEditing {
let editable: Bool
let hasActiveRevealControls: Bool
@ -87,6 +91,7 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN
private let editableControlNode: ItemListEditableControlNode
private let labelSeparatorNode: ASDisplayNode
private let phoneNode: SinglePhoneInputNode
private let clearButton: HighlightableButtonNode
private var item: UserInfoEditingPhoneItem?
private var layoutParams: ListViewItemLayoutParams?
@ -120,6 +125,12 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN
self.phoneNode = SinglePhoneInputNode(fontSize: 17.0)
self.clearButton = HighlightableButtonNode()
self.clearButton.imageNode.displaysAsynchronously = false
self.clearButton.imageNode.displayWithoutProcessing = true
self.clearButton.displaysAsynchronously = false
self.clearButton.isHidden = true
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
self.addSubnode(self.editableControlNode)
@ -127,6 +138,7 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN
self.addSubnode(self.labelButtonNode)
self.addSubnode(self.labelSeparatorNode)
self.addSubnode(self.phoneNode)
self.addSubnode(self.clearButton)
self.labelButtonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
@ -150,7 +162,18 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN
self.phoneNode.numberUpdated = { [weak self] number in
self?.item?.updated(number)
self?.updateClearButtonVisibility()
}
self.phoneNode.beginEditing = { [weak self] in
self?.updateClearButtonVisibility()
}
self.phoneNode.endEditing = { [weak self] in
self?.updateClearButtonVisibility()
}
self.clearButton.addTarget(self, action: #selector(self.clearPressed), forControlEvents: .touchUpInside)
}
override func didLoad() {
@ -208,6 +231,8 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN
strongSelf.phoneNode.numberField?.textField.textColor = updatedTheme.list.itemPrimaryTextColor
strongSelf.phoneNode.numberField?.textField.keyboardAppearance = updatedTheme.chatList.searchBarKeyboardColor.keyboardAppearance
strongSelf.clearButton.setImage(generateClearIcon(color: updatedTheme.list.inputClearButtonColor), for: [])
}
let revealOffset = strongSelf.revealOffset
@ -246,6 +271,10 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN
strongSelf.phoneNode.updateLayout(size: phoneFrame.size)
strongSelf.phoneNode.number = item.value
if let image = strongSelf.clearButton.image(for: []) {
strongSelf.clearButton.frame = CGRect(origin: CGPoint(x: phoneFrame.maxX - image.size.width - 23.0, y: phoneFrame.minY + floor((phoneFrame.size.height - image.size.height) / 2.0) - 1.0 + UIScreenPixel), size: image.size)
}
strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)]))
@ -297,6 +326,17 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN
self.item?.selectLabel?()
}
@objc func clearPressed() {
self.phoneNode.numberField?.textField.text = "+"
self.updateClearButtonVisibility()
}
private func updateClearButtonVisibility() {
let text = self.phoneNode.numberField?.textField.text ?? ""
let isEmpty = text.isEmpty || text == "+"
self.clearButton.isHidden = isEmpty || !(self.phoneNode.numberField?.textField.isFirstResponder ?? false)
}
func focus() {
self.phoneNode.numberField?.becomeFirstResponder()
}

View File

@ -89,7 +89,7 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
func item(_ arguments: UsernameSetupControllerArguments) -> ListViewItem {
switch self {
case let .editablePublicLink(theme, prefix, currentText, text):
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: prefix, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .username, spacing: 10.0, tag: UsernameEntryTag.username, sectionId: self.section, textUpdated: { updatedText in
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: prefix, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .username, spacing: 10.0, clearButton: true, tag: UsernameEntryTag.username, sectionId: self.section, textUpdated: { updatedText in
arguments.updatePublicLinkText(currentText, updatedText)
}, action: {
})

View File

@ -380,7 +380,7 @@ class WallpaperGalleryController: ViewController {
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
themeSpecificChatWallpapers[current.theme.index] = wallpaper
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
}) |> deliverOnMainQueue).start(completed: {
self?.dismiss(forceAway: true)
})

View File

@ -139,7 +139,7 @@ final class WallpaperUploadManager {
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
themeSpecificChatWallpapers[current.theme.index] = updatedWallpaper
return PresentationThemeSettings(chatWallpaper: updatedWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: updatedWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})).start()
}
}

View File

@ -164,11 +164,96 @@ public struct AutomaticThemeSwitchSetting: PostboxCoding, Equatable {
}
}
public enum PresentationThemeBaseColor: Int32, CaseIterable {
case blue
case cyan
case green
case pink
case orange
case purple
case red
case yellow
case gray
case black
public var colorValue: Int32 {
switch self {
case .blue:
return 0x007ee5
case .cyan:
return 0x00c2ed
case .green:
return 0x29b327
case .pink:
return 0xff5da2
case .orange:
return 0xff7519
case .purple:
return 0x7748ff
case .red:
return 0xf83b4c
case .yellow:
return 0xeba239
case .gray:
return 0x6d839e
case .black:
return 0x000000
}
// switch self {
// case .blue:
// return 0x007aff
// case .cyan:
// return 0x00c2ed
// case .green:
// return 0x70bb23
// case .pink:
// return 0xeb6ca4
// case .orange:
// return 0xf08200
// case .purple:
// return 0x9472ee
// case .red:
// return 0xd33213
// case .yellow:
// return 0xedb400
// case .gray:
// return 0x6d839e
// case .black:
// return 0x000000
// }
}
}
public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
public var baseColor: PresentationThemeBaseColor
public var value: CGFloat
public init(baseColor: PresentationThemeBaseColor, value: CGFloat) {
self.baseColor = baseColor
self.value = value
}
public init(decoder: PostboxDecoder) {
self.baseColor = PresentationThemeBaseColor(rawValue: decoder.decodeInt32ForKey("b", orElse: 0)) ?? .blue
self.value = CGFloat(decoder.decodeDoubleForKey("v", orElse: 0.5))
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.baseColor.rawValue, forKey: "b")
encoder.encodeDouble(Double(self.value), forKey: "v")
}
public var color: Int32 {
return self.baseColor.colorValue
}
}
public struct PresentationThemeSettings: PreferencesEntry {
public var chatWallpaper: TelegramWallpaper
public var theme: PresentationThemeReference
public var themeAccentColor: Int32?
public var themeSpecificChatWallpapers: Dictionary<Int64, TelegramWallpaper>
public var themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
public var themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
public var fontSize: PresentationFontSize
public var automaticThemeSwitchSetting: AutomaticThemeSwitchSetting
public var largeEmoji: Bool
@ -198,13 +283,13 @@ public struct PresentationThemeSettings: PreferencesEntry {
}
public static var defaultSettings: PresentationThemeSettings {
return PresentationThemeSettings(chatWallpaper: .builtin(WallpaperSettings()), theme: .builtin(.dayClassic), themeAccentColor: nil, themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent), largeEmoji: true, disableAnimations: true)
return PresentationThemeSettings(chatWallpaper: .builtin(WallpaperSettings()), theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent), largeEmoji: true, disableAnimations: true)
}
public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeAccentColor: Int32?, themeSpecificChatWallpapers: Dictionary<Int64, TelegramWallpaper>, fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
self.chatWallpaper = chatWallpaper
self.theme = theme
self.themeAccentColor = themeAccentColor
self.themeSpecificAccentColors = themeSpecificAccentColors
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
self.fontSize = fontSize
self.automaticThemeSwitchSetting = automaticThemeSwitchSetting
@ -215,12 +300,44 @@ public struct PresentationThemeSettings: PreferencesEntry {
public init(decoder: PostboxDecoder) {
self.chatWallpaper = (decoder.decodeObjectForKey("w", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper) ?? .builtin(WallpaperSettings())
self.theme = decoder.decodeObjectForKey("t", decoder: { PresentationThemeReference(decoder: $0) }) as! PresentationThemeReference
self.themeAccentColor = decoder.decodeOptionalInt32ForKey("themeAccentColor")
self.themeSpecificChatWallpapers = decoder.decodeObjectDictionaryForKey("themeSpecificChatWallpapers", keyDecoder: { decoder in
return decoder.decodeInt64ForKey("k", orElse: 0)
}, valueDecoder: { decoder in
return TelegramWallpaper(decoder: decoder)
})
self.themeSpecificAccentColors = decoder.decodeObjectDictionaryForKey("themeSpecificAccentColors", keyDecoder: { decoder in
return decoder.decodeInt64ForKey("k", orElse: 0)
}, valueDecoder: { decoder in
return PresentationThemeAccentColor(decoder: decoder)
})
if self.themeSpecificAccentColors[PresentationThemeReference.builtin(.day).index] == nil, let themeAccentColor = decoder.decodeOptionalInt32ForKey("themeAccentColor") {
let baseColor: PresentationThemeBaseColor
switch themeAccentColor {
case 0xf83b4c:
baseColor = .red
case 0xff7519:
baseColor = .orange
case 0xeba239:
baseColor = .yellow
case 0x29b327:
baseColor = .green
case 0x00c2ed:
baseColor = .cyan
case 0x007ee5:
baseColor = .blue
case 0x7748ff:
baseColor = .purple
case 0xff5da2:
baseColor = .pink
default:
baseColor = .blue
}
self.themeSpecificAccentColors[PresentationThemeReference.builtin(.day).index] = PresentationThemeAccentColor(baseColor: baseColor, value: 0.5)
}
self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular
self.automaticThemeSwitchSetting = (decoder.decodeObjectForKey("automaticThemeSwitchSetting", decoder: { AutomaticThemeSwitchSetting(decoder: $0) }) as? AutomaticThemeSwitchSetting) ?? AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent)
self.largeEmoji = decoder.decodeBoolForKey("largeEmoji", orElse: true)
@ -230,11 +347,9 @@ public struct PresentationThemeSettings: PreferencesEntry {
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObject(self.chatWallpaper, forKey: "w")
encoder.encodeObject(self.theme, forKey: "t")
if let themeAccentColor = self.themeAccentColor {
encoder.encodeInt32(themeAccentColor, forKey: "themeAccentColor")
} else {
encoder.encodeNil(forKey: "themeAccentColor")
}
encoder.encodeObjectDictionary(self.themeSpecificAccentColors, forKey: "themeSpecificAccentColors", keyEncoder: { key, encoder in
encoder.encodeInt64(key, forKey: "k")
})
encoder.encodeObjectDictionary(self.themeSpecificChatWallpapers, forKey: "themeSpecificChatWallpapers", keyEncoder: { key, encoder in
encoder.encodeInt64(key, forKey: "k")
})
@ -253,7 +368,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
}
public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool {
return lhs.chatWallpaper == rhs.chatWallpaper && lhs.theme == rhs.theme && lhs.themeAccentColor == rhs.themeAccentColor && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.disableAnimations == rhs.disableAnimations
return lhs.chatWallpaper == rhs.chatWallpaper && lhs.theme == rhs.theme && lhs.themeSpecificAccentColors == rhs.themeSpecificAccentColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.disableAnimations == rhs.disableAnimations
}
}