Merge commit '16d7853b4efbf173967166881aabbe0ee14ae74b'
@ -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;
|
||||
|
||||
BIN
Telegram-iOS/BlackClassicNotificationIcon.png
Normal file
|
After Width: | Height: | Size: 737 B |
BIN
Telegram-iOS/BlackClassicNotificationIcon@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram-iOS/BlackClassicNotificationIcon@3x.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 13 KiB |
BIN
Telegram-iOS/BlackNotificationIcon.png
Executable file
|
After Width: | Height: | Size: 749 B |
BIN
Telegram-iOS/BlackNotificationIcon@2x.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram-iOS/BlackNotificationIcon@3x.png
Executable file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
Telegram-iOS/BlueClassicNotificationIcon.png
Normal file
|
After Width: | Height: | Size: 715 B |
BIN
Telegram-iOS/BlueClassicNotificationIcon@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram-iOS/BlueClassicNotificationIcon@3x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram-iOS/BlueNotificationIcon.png
Executable file
|
After Width: | Height: | Size: 665 B |
BIN
Telegram-iOS/BlueNotificationIcon@2x.png
Executable file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram-iOS/BlueNotificationIcon@3x.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
@ -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/>
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1581
submodules/TelegramApi/Sources/SecretApiLayer101.swift
Normal 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;
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ enum SecretChatLayer: Int32 {
|
||||
case layer8 = 8
|
||||
case layer46 = 46
|
||||
case layer73 = 73
|
||||
case layer101 = 101
|
||||
}
|
||||
|
||||
public struct SecretChatKeySha1Fingerprint: PostboxCoding, Equatable {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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?()
|
||||
|
||||
@ -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()))
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
// }
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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()))
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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: {
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||