Merge commit 'd58bd7415d508d06f32d7658675b2426fa924472' into features/reply-list

This commit is contained in:
Ali 2020-08-25 17:35:26 +01:00
commit f33ebbbad5
27 changed files with 351 additions and 131 deletions

@ -1 +1 @@
Subproject commit b8755bd2884d6bf651827c30e00bd0ea318e41a2
Subproject commit 501b4afb27745c4813a88ffa28acd901408014e4

@ -1 +1 @@
Subproject commit d6f9a87d70781e92b95b9344c7d21d921ccc3ae2
Subproject commit 748e7e2b1ee5e8976ba873394cbd5e32b61818c7

@ -1 +1 @@
Subproject commit 44e8c29afe3baa7f5fc434f4a7e83f7ac786644e
Subproject commit 6408d85da799ec2af053c4e2883dce3ce6d30f08

@ -1 +1 @@
Subproject commit e890fb6c88846454c4f69abd8d265a302c0e80e4
Subproject commit ee185c4c20ea4384bc3cbf8ccd8705c904154abb

View File

@ -769,7 +769,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
messageTags = .photo
} else if query.hasPrefix("%video ") {
messageTags = .video
} else if query.hasPrefix("%file ") {
} else if query.hasPrefix("%file ") {
messageTags = .file
} else if query.hasPrefix("%music ") {
messageTags = .music

View File

@ -25,6 +25,7 @@ private func loadCountryCodes() -> [Country] {
let endOfLine = "\n"
var result: [Country] = []
var countriesByPrefix: [String: (Country, Country.CountryCode)] = [:]
var currentLocation = data.startIndex
@ -46,8 +47,11 @@ private func loadCountryCodes() -> [Country] {
let maybeNameRange = data.range(of: endOfLine, options: [], range: idRange.upperBound ..< data.endIndex)
let countryName = locale.localizedString(forIdentifier: countryId) ?? ""
if let countryCodeInt = Int(countryCode) {
result.append(Country(code: countryId, name: countryName, localizedName: nil, countryCodes: [Country.CountryCode(code: countryCode, prefixes: [], patterns: [])], hidden: false))
if let _ = Int(countryCode) {
let code = Country.CountryCode(code: countryCode, prefixes: [], patterns: [])
let country = Country(id: countryId, name: countryName, localizedName: nil, countryCodes: [code], hidden: false)
result.append(country)
countriesByPrefix["\(code.code)"] = (country, code)
}
if let maybeNameRange = maybeNameRange {
@ -57,15 +61,35 @@ private func loadCountryCodes() -> [Country] {
}
}
countryCodesByPrefix = countriesByPrefix
return result
}
private var countryCodes: [Country] = loadCountryCodes()
private var countryCodesByPrefix: [String: (Country, Country.CountryCode)] = [:]
public func loadServerCountryCodes(accountManager: AccountManager, network: Network) {
public func loadServerCountryCodes(accountManager: AccountManager, network: Network, completion: @escaping () -> Void) {
let _ = (getCountriesList(accountManager: accountManager, network: network, langCode: nil)
|> deliverOnMainQueue).start(next: { countries in
countryCodes = countries
var countriesByPrefix: [String: (Country, Country.CountryCode)] = [:]
for country in countries {
for code in country.countryCodes {
if !code.prefixes.isEmpty {
for prefix in code.prefixes {
countriesByPrefix["\(code.code)\(prefix)"] = (country, code)
}
} else {
countriesByPrefix[code.code] = (country, code)
}
}
}
countryCodesByPrefix = countriesByPrefix
Queue.mainQueue().async {
completion()
}
})
}
@ -128,10 +152,26 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode:
}
}
private func removePlus(_ text: String?) -> String {
var result = ""
if let text = text {
for c in text {
if c != "+" {
result += String(c)
}
}
}
return result
}
public final class AuthorizationSequenceCountrySelectionController: ViewController {
static func countries() -> [Country] {
return countryCodes
}
public static func lookupCountryNameById(_ id: String, strings: PresentationStrings) -> String? {
for country in countryCodes {
if id == country.code {
for country in self.countries() {
if id == country.id {
let locale = localeWithStrings(strings)
if let countryName = locale.localizedString(forRegionCode: id) {
return countryName
@ -142,25 +182,74 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll
}
return nil
}
static func lookupCountryById(_ id: String) -> Country? {
return countryCodes.first { $0.id == id }
}
public static func lookupCountryIdByNumber(_ number: String, preferredCountries: [String: String]) -> (Country, Country.CountryCode)? {
let number = removePlus(number)
var results: [(Country, Country.CountryCode)]? = nil
if number.count == 1, let preferredCountryId = preferredCountries[number], let country = lookupCountryById(preferredCountryId), let code = country.countryCodes.first {
return (country, code)
}
for i in 0..<number.count {
let prefix = String(number.prefix(number.count - i))
if let country = countryCodesByPrefix[prefix] {
if var currentResults = results {
if let result = currentResults.first, result.1.code.count > country.1.code.count {
break
} else {
currentResults.append(country)
}
} else {
results = [country]
}
}
}
if let results = results {
if !preferredCountries.isEmpty, let (_, code) = results.first {
if let preferredCountry = preferredCountries[code.code] {
for (country, code) in results {
if country.id == preferredCountry {
return (country, code)
}
}
}
}
return results.first
} else {
return nil
}
}
public static func lookupCountryIdByCode(_ code: Int) -> String? {
for country in countryCodes {
for country in self.countries() {
for countryCode in country.countryCodes {
if countryCode.code == "\(code)" {
return country.code
return country.id
}
}
}
return nil
}
public static func lookupPatternByCode(_ code: Int) -> String? {
for country in countryCodes {
for countryCode in country.countryCodes {
if countryCode.code == "\(code)" {
return countryCode.patterns.first
public static func lookupPatternByNumber(_ number: String, preferredCountries: [String: String]) -> String? {
if let (_, code) = lookupCountryIdByNumber(number, preferredCountries: preferredCountries), !code.patterns.isEmpty {
var prefixes: [String: String] = [:]
for pattern in code.patterns {
let cleanPattern = pattern.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "X", with: "")
let cleanPrefix = "\(code.code)\(cleanPattern)"
prefixes[cleanPrefix] = pattern
}
for i in 0..<number.count {
let prefix = String(number.prefix(number.count - i))
if let pattern = prefixes[prefix] {
return pattern
}
}
return code.patterns.first
}
return nil
}

View File

@ -57,12 +57,15 @@ private func loadCountryCodes() -> [(String, Int)] {
private let countryCodes: [(String, Int)] = loadCountryCodes()
func localizedContryNamesAndCodes(strings: PresentationStrings) -> [((String, String), String, Int)] {
func localizedCountryNamesAndCodes(strings: PresentationStrings) -> [((String, String), String, Int)] {
let locale = localeWithStrings(strings)
var result: [((String, String), String, Int)] = []
for (id, code) in countryCodes {
if let englishCountryName = usEnglishLocale.localizedString(forRegionCode: id), let countryName = locale.localizedString(forRegionCode: id) {
result.append(((englishCountryName, countryName), id, code))
for country in AuthorizationSequenceCountrySelectionController.countries() {
if country.hidden {
continue
}
if let englishCountryName = usEnglishLocale.localizedString(forRegionCode: country.id), let countryName = locale.localizedString(forRegionCode: country.id), let codeValue = country.countryCodes.first?.code, let code = Int(codeValue) {
result.append(((englishCountryName, countryName), country.id, code))
} else {
assertionFailure()
}
@ -103,7 +106,7 @@ final class AuthorizationSequenceCountrySelectionControllerNode: ASDisplayNode,
self.searchTableView.contentInsetAdjustmentBehavior = .never
}
let countryNamesAndCodes = localizedContryNamesAndCodes(strings: strings)
let countryNamesAndCodes = localizedCountryNamesAndCodes(strings: strings)
var sections: [(String, [((String, String), String, Int)])] = []
for (names, id, code) in countryNamesAndCodes.sorted(by: { lhs, rhs in
@ -116,8 +119,7 @@ final class AuthorizationSequenceCountrySelectionControllerNode: ASDisplayNode,
sections[sections.count - 1].1.append((names, id, code))
}
self.sections = sections
var sectionTitles = sections.map { $0.0 }
self.sectionTitles = sectionTitles
self.sectionTitles = sections.map { $0.0 }
super.init()

View File

@ -1,6 +1,29 @@
import Foundation
import AppBundle
public func emojiFlagForISOCountryCode(_ countryCode: String) -> String {
if countryCode.count != 2 {
return ""
}
if countryCode == "XG" {
return "🛰️"
} else if countryCode == "XV" {
return "🌍"
}
if ["YL"].contains(countryCode) {
return ""
}
let base : UInt32 = 127397
var s = ""
for v in countryCode.unicodeScalars {
s.unicodeScalars.append(UnicodeScalar(base + v.value)!)
}
return String(s)
}
private func loadCountriesInfo() -> [(Int, String, String)] {
guard let filePath = getAppBundle().path(forResource: "PhoneCountries", ofType: "txt") else {
return []

View File

@ -128,10 +128,14 @@ final class ChatVideoGalleryItemScrubberView: UIView {
self.collapsed = collapsed
guard let (size, _, _) = self.containerLayout else {
return
}
let alpha: CGFloat = collapsed ? 0.0 : 1.0
self.leftTimestampNode.alpha = alpha
self.rightTimestampNode.alpha = alpha
self.infoNode.alpha = alpha
self.infoNode.alpha = size.width < size.height && !self.collapsed ? 1.0 : 0.0
self.updateScrubberVisibility(animated: animated)
}
@ -161,7 +165,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
self.leftTimestampNode.status = mappedStatus
self.rightTimestampNode.status = mappedStatus
if let mappedStatus = mappedStatus {
if let mappedStatus = mappedStatus, false {
self.chapterDisposable.set((mappedStatus
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self, status.duration > 1.0 {

View File

@ -141,7 +141,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO
if peer is TelegramUser {
carouselItem.hasTimer = hasSchedule
}
carouselItem.hasSilentPosting = !isSecretChat
carouselItem.hasSilentPosting = true
}
carouselItem.hasSchedule = hasSchedule
carouselItem.reminder = peer.id == context.account.peerId

View File

@ -39,7 +39,7 @@ public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, co
if peer is TelegramUser {
controller.hasTimer = hasSchedule
}
controller.hasSilentPosting = !isSecretChat
controller.hasSilentPosting = true
}
controller.hasSchedule = hasSchedule
controller.reminder = peer.id == context.account.peerId

View File

@ -78,6 +78,7 @@ private func cleanSuffix(_ text: String) -> String {
extension String {
func applyPatternOnNumbers(pattern: String, replacementCharacter: Character) -> String {
let pattern = pattern.replacingOccurrences( of: "[0-9]", with: "X", options: .regularExpression)
var pureNumber = self.replacingOccurrences( of: "[^0-9]", with: "", options: .regularExpression)
for index in 0 ..< pattern.count {
guard index < pureNumber.count else { return pureNumber }
@ -165,6 +166,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
public var returnAction: (() -> Void)?
private let phoneFormatter = InteractivePhoneFormatter()
public var customFormatter: ((String) -> String?)?
public var mask: NSAttributedString? {
didSet {
@ -175,8 +177,12 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
private func updatePlaceholder() {
if let mask = self.mask {
let mutableMask = NSMutableAttributedString(attributedString: mask)
mutableMask.replaceCharacters(in: NSRange(location: 0, length: mask.string.count), with: mask.string.replacingOccurrences(of: "X", with: "-"))
mutableMask.addAttribute(.foregroundColor, value: UIColor.clear, range: NSRange(location: 0, length: min(self.numberField.textField.text?.count ?? 0, mask.string.count)))
mutableMask.replaceCharacters(in: NSRange(location: 0, length: mask.string.count), with: mask.string.replacingOccurrences(of: "X", with: ""))
if let text = self.numberField.textField.text {
mutableMask.replaceCharacters(in: NSRange(location: 0, length: min(text.count, mask.string.count)), with: text)
}
mutableMask.addAttribute(.foregroundColor, value: UIColor.clear, range: NSRange(location: 0, length: min(self.numberField.textField.text?.count ?? 0, mutableMask.string.count)))
mutableMask.addAttribute(.kern, value: 1.6, range: NSRange(location: 0, length: mask.string.count))
self.placeholderNode.attributedText = mutableMask
} else {
self.placeholderNode.attributedText = NSAttributedString(string: "")
@ -212,7 +218,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
} else {
self.numberField.textField.keyboardType = .numberPad
}
self.numberField.textField.defaultTextAttributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.kern: 2.0]
self.numberField.textField.defaultTextAttributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.kern: 1.6]
super.init()
self.addSubnode(self.countryCodeField)
@ -260,7 +266,12 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
}
private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true, forceNotifyCountryCodeUpdated: Bool = false) {
let (regionPrefix, text) = self.phoneFormatter.updateText(inputText)
var (regionPrefix, text) = self.phoneFormatter.updateText(inputText)
if let customFormatter = self.customFormatter, let customRegionPrefix = customFormatter(inputText) {
regionPrefix = "+\(customRegionPrefix)"
text = inputText
}
var realRegionPrefix: String
var numberText: String

View File

@ -87,6 +87,12 @@ final class ChangePhoneNumberController: ViewController {
strongSelf.push(controller)
}
}
loadServerCountryCodes(accountManager: self.context.sharedContext.accountManager, network: self.context.account.network, completion: { [weak self] in
if let strongSelf = self {
strongSelf.controllerNode.updateCountryCode()
}
})
}
override func viewWillAppear(_ animated: Bool) {

View File

@ -91,6 +91,8 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
}
}
var preferredCountryIdForCode: [String: String] = [:]
var selectCountryCode: (() -> Void)?
var inProgress: Bool = false {
@ -155,9 +157,43 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside)
let processNumberChange: (String) -> Bool = { [weak self] number in
guard let strongSelf = self else {
return false
}
if let (country, _) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode) {
let flagString = emojiFlagForISOCountryCode(country.id)
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: strongSelf.presentationData.strings) ?? country.name
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: [])
let maskFont = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers])
if let mask = AuthorizationSequenceCountrySelectionController.lookupPatternByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode).flatMap({ NSAttributedString(string: $0, font: maskFont, textColor: strongSelf.presentationData.theme.list.itemPlaceholderTextColor) }) {
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = nil
strongSelf.phoneInputNode.mask = mask
} else {
strongSelf.phoneInputNode.mask = nil
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strongSelf.presentationData.strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: strongSelf.presentationData.theme.list.itemPlaceholderTextColor)
}
return true
} else {
return false
}
}
self.phoneInputNode.numberTextUpdated = { [weak self] number in
if let strongSelf = self {
let _ = processNumberChange(strongSelf.phoneInputNode.number)
}
}
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
if let strongSelf = self {
if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
if let name = name {
strongSelf.preferredCountryIdForCode[code] = name
}
if processNumberChange(strongSelf.phoneInputNode.number) {
} else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.presentationData.strings) ?? countryName
strongSelf.countryButton.setTitle(localizedName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: [])
} else if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] {
@ -168,6 +204,14 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
}
}
self.phoneInputNode.customFormatter = { number in
if let (_, code) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: [:]) {
return code.code
} else {
return nil
}
}
var countryId: String? = nil
let networkInfo = CTTelephonyNetworkInfo()
if let carrier = networkInfo.subscriberCellularProvider {
@ -193,6 +237,10 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
self.phoneInputNode.number = "+\(countryCodeAndId.0)"
}
func updateCountryCode() {
self.phoneInputNode.codeAndNumber = self.codeAndNumber
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
var insets = layout.insets(options: [.statusBar, .input])
insets.left = layout.safeInsets.left
@ -215,12 +263,14 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
let countryCodeFrame = CGRect(origin: CGPoint(x: 9.0, y: navigationHeight + 44.0 + 1.0), size: CGSize(width: 45.0, height: 44.0))
let numberFrame = CGRect(origin: CGPoint(x: 70.0, y: navigationHeight + 44.0 + 1.0), size: CGSize(width: layout.size.width - 70.0 - 8.0, height: 44.0))
let placeholderFrame = numberFrame.offsetBy(dx: -1.0, dy: 16.0)
let phoneInputFrame = countryCodeFrame.union(numberFrame)
transition.updateFrame(node: self.phoneInputNode, frame: phoneInputFrame)
transition.updateFrame(node: self.phoneInputNode.countryCodeField, frame: countryCodeFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY))
transition.updateFrame(node: self.phoneInputNode.numberField, frame: numberFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY))
transition.updateFrame(node: self.phoneInputNode.placeholderNode, frame: placeholderFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY))
transition.updateFrame(node: self.noticeNode, frame: CGRect(origin: CGPoint(x: 15.0 + insets.left, y: navigationHeight + inputHeight + 8.0), size: noticeSize))
}

View File

@ -593,25 +593,25 @@ public final class ShareController: ViewController {
}
self.controllerNode.shareExternal = { [weak self] in
if let strongSelf = self {
if case let .messages(messages) = strongSelf.subject, let message = messages.first, let peer = message.peers[message.id.peerId] {
let renderer = MessageStoryRenderer(context: strongSelf.currentContext, messages: messages)
let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)
renderer.update(layout: layout) { image in
if let data = image?.pngData() {
let pasteboardItems: [[String: Any]] = [["com.instagram.sharedSticker.backgroundImage": data,
"com.instagram.sharedSticker.contentURL": "https://t.me/\(peer.addressName ?? "")/\(message.id.id)"]]
if #available(iOS 10.0, *) {
UIPasteboard.general.setItems(pasteboardItems, options: [.expirationDate: Date().addingTimeInterval(5 * 60)])
} else {
// UIPasteboard.general.setItems(pasteboardItems)
}
strongSelf.sharedContext.applicationBindings.openUrl("instagram-stories://share")
}
}
return .complete()
}
// if case let .messages(messages) = strongSelf.subject, let message = messages.first, let peer = message.peers[message.id.peerId] {
// let renderer = MessageStoryRenderer(context: strongSelf.currentContext, messages: messages)
//
// let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)
// renderer.update(layout: layout) { image in
// if let data = image?.pngData() {
// let pasteboardItems: [[String: Any]] = [["com.instagram.sharedSticker.backgroundImage": data,
// "com.instagram.sharedSticker.contentURL": "https://t.me/\(peer.addressName ?? "")/\(message.id.id)"]]
// if #available(iOS 10.0, *) {
// UIPasteboard.general.setItems(pasteboardItems, options: [.expirationDate: Date().addingTimeInterval(5 * 60)])
// } else {
//// UIPasteboard.general.setItems(pasteboardItems)
// }
// strongSelf.sharedContext.applicationBindings.openUrl("instagram-stories://share")
// }
// }
//
// return .complete()
// }
var collectableItems: [CollectableExternalShareItem] = []

View File

@ -300,15 +300,12 @@ private class CallControllerToastItemNode: ASDisplayNode {
let initialFrame = CGRect(x: floor((self.frame.width - 44.0) / 2.0), y: 0.0, width: 44.0, height: 28.0)
self.clipNode.frame = initialFrame
// self.effectView.frame = CGRect(origin: CGPoint(), size: initialFrame.size)
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.45, damping: 105.0, completion: { _ in
self.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.3, damping: 105.0, completion: { _ in
self.clipNode.frame = targetFrame
// self.effectView.frame = CGRect(origin: CGPoint(), size: targetFrame.size)
self.clipNode.layer.animateFrame(from: initialFrame, to: targetFrame, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring)
// self.effectView.layer.animateFrame(from: initialFrame, to: targetFrame, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring)
})
}

View File

@ -166,6 +166,7 @@ private var declaredEncodables: Void = {
declareEncodable(TelegramMediaImage.VideoRepresentation.self, f: { TelegramMediaImage.VideoRepresentation(decoder: $0) })
declareEncodable(Country.self, f: { Country(decoder: $0) })
declareEncodable(Country.CountryCode.self, f: { Country.CountryCode(decoder: $0) })
declareEncodable(CountriesList.self, f: { CountriesList(decoder: $0) })
return
}()

View File

@ -7,7 +7,7 @@ import SyncCore
public struct Country: PostboxCoding, Equatable {
public static func == (lhs: Country, rhs: Country) -> Bool {
return lhs.code == rhs.code && lhs.name == rhs.name && lhs.localizedName == rhs.localizedName && lhs.countryCodes == rhs.countryCodes && lhs.hidden == rhs.hidden
return lhs.id == rhs.id && lhs.name == rhs.name && lhs.localizedName == rhs.localizedName && lhs.countryCodes == rhs.countryCodes && lhs.hidden == rhs.hidden
}
public struct CountryCode: PostboxCoding, Equatable {
@ -34,14 +34,14 @@ public struct Country: PostboxCoding, Equatable {
}
}
public let code: String
public let id: String
public let name: String
public let localizedName: String?
public let countryCodes: [CountryCode]
public let hidden: Bool
public init(code: String, name: String, localizedName: String?, countryCodes: [CountryCode], hidden: Bool) {
self.code = code
public init(id: String, name: String, localizedName: String?, countryCodes: [CountryCode], hidden: Bool) {
self.id = id
self.name = name
self.localizedName = localizedName
self.countryCodes = countryCodes
@ -49,7 +49,7 @@ public struct Country: PostboxCoding, Equatable {
}
public init(decoder: PostboxDecoder) {
self.code = decoder.decodeStringForKey("c", orElse: "")
self.id = decoder.decodeStringForKey("c", orElse: "")
self.name = decoder.decodeStringForKey("n", orElse: "")
self.localizedName = decoder.decodeOptionalStringForKey("ln")
self.countryCodes = decoder.decodeObjectArrayForKey("cc").map { $0 as! CountryCode }
@ -57,7 +57,7 @@ public struct Country: PostboxCoding, Equatable {
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.code, forKey: "c")
encoder.encodeString(self.id, forKey: "c")
encoder.encodeString(self.name, forKey: "n")
if let localizedName = self.localizedName {
encoder.encodeString(localizedName, forKey: "ln")
@ -130,6 +130,7 @@ public func getCountriesList(accountManager: AccountManager, network: Network, l
return fetch(nil, nil)
} else {
return accountManager.sharedData(keys: [SharedDataKeys.countriesList])
|> take(1)
|> map { sharedData -> ([Country], Int32) in
if let countriesList = sharedData.entries[SharedDataKeys.countriesList] as? CountriesList {
return (countriesList.countries, countriesList.hash)
@ -158,7 +159,7 @@ extension Country {
init(apiCountry: Api.help.Country) {
switch apiCountry {
case let .country(flags, iso2, defaultName, name, countryCodes):
self.init(code: iso2, name: defaultName, localizedName: name, countryCodes: countryCodes.map { Country.CountryCode(apiCountryCode: $0) }, hidden: (flags & 1 << 0) != 0)
self.init(id: iso2, name: defaultName, localizedName: name, countryCodes: countryCodes.map { Country.CountryCode(apiCountryCode: $0) }, hidden: (flags & 1 << 0) != 0)
}
}
}

View File

@ -323,8 +323,8 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|> mapToSignal { result, additionalResult -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> in
return account.postbox.transaction { transaction -> (SearchMessagesResult, SearchMessagesState) in
var additional: SearchMessagesPeerState? = mergedState(transaction: transaction, state: state?.additional, result: additionalResult)
if state?.additional == nil, case .general = location {
let secretMessages = transaction.searchMessages(peerId: nil, query: query, tags: nil)
if state?.additional == nil, case let .general(tags) = location {
let secretMessages = transaction.searchMessages(peerId: nil, query: query, tags: tags)
var readStates: [PeerId: CombinedPeerReadState] = [:]
for message in secretMessages {
if let readState = transaction.getCombinedPeerReadState(message.id.peerId) {

View File

@ -57,9 +57,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
self.presentationData = presentationData
self.openUrl = openUrl
self.back = back
loadServerCountryCodes(accountManager: sharedContext.accountManager, network: account.network)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)))
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
@ -140,6 +138,12 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
self.controllerNode.checkPhone = { [weak self] in
self?.nextPressed()
}
loadServerCountryCodes(accountManager: sharedContext.accountManager, network: account.network, completion: { [weak self] in
if let strongSelf = self {
strongSelf.controllerNode.updateCountryCode()
}
})
}
override func viewWillAppear(_ animated: Bool) {
@ -185,7 +189,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
self.loginWithNumber?(self.controllerNode.currentNumber, self.controllerNode.syncContacts)
}
} else {
hapticFeedback.error()
self.hapticFeedback.error()
self.controllerNode.animateError()
}
}

View File

@ -13,24 +13,6 @@ import SwiftSignalKit
import Postbox
import AccountContext
private func emojiFlagForISOCountryCode(_ countryCode: NSString) -> String {
if countryCode.length != 2 {
return ""
}
let base: UInt32 = 127462 - 65
let first: UInt32 = base + UInt32(countryCode.character(at: 0))
let second: UInt32 = base + UInt32(countryCode.character(at: 1))
var data = Data()
data.count = 8
data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt32>) -> Void in
bytes[0] = first
bytes[1] = second
}
return String(data: data, encoding: String.Encoding.utf32LittleEndian) ?? ""
}
private final class PhoneAndCountryNode: ASDisplayNode {
let strings: PresentationStrings
let countryButton: ASButtonNode
@ -40,6 +22,8 @@ private final class PhoneAndCountryNode: ASDisplayNode {
var selectCountryCode: (() -> Void)?
var checkPhone: (() -> Void)?
var preferredCountryIdForCode: [String: String] = [:]
init(strings: PresentationStrings, theme: PresentationTheme) {
self.strings = strings
@ -124,33 +108,70 @@ private final class PhoneAndCountryNode: ASDisplayNode {
self.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 15.0, bottom: 10.0, right: 0.0)
self.countryButton.contentHorizontalAlignment = .left
// self.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside)
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
let font = Font.with(size: 20.0, design: .monospace, traits: [])
let processNumberChange: (String) -> Bool = { [weak self] number in
guard let strongSelf = self else {
return false
}
if let (country, _) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode) {
let flagString = emojiFlagForISOCountryCode(country.id)
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: strongSelf.strings) ?? country.name
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: [])
let maskFont = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers])
if let mask = AuthorizationSequenceCountrySelectionController.lookupPatternByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode).flatMap({ NSAttributedString(string: $0, font: maskFont, textColor: theme.list.itemPlaceholderTextColor) }) {
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = nil
strongSelf.phoneInputNode.mask = mask
} else {
strongSelf.phoneInputNode.mask = nil
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
}
return true
} else {
return false
}
}
self.phoneInputNode.numberTextUpdated = { [weak self] number in
if let strongSelf = self {
if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
let flagString = emojiFlagForISOCountryCode(name as NSString)
let _ = processNumberChange(strongSelf.phoneInputNode.number)
}
}
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
if let strongSelf = self {
if let name = name {
strongSelf.preferredCountryIdForCode[code] = name
}
if processNumberChange(strongSelf.phoneInputNode.number) {
} else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
let flagString = emojiFlagForISOCountryCode(name)
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: [])
strongSelf.phoneInputNode.mask = AuthorizationSequenceCountrySelectionController.lookupPatternByCode(code).flatMap { NSAttributedString(string: $0, font: font, textColor: theme.list.itemPlaceholderTextColor) }
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
} else if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] {
let flagString = emojiFlagForISOCountryCode(countryId as NSString)
let flagString = emojiFlagForISOCountryCode(countryId)
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: [])
strongSelf.phoneInputNode.mask = AuthorizationSequenceCountrySelectionController.lookupPatternByCode(code).flatMap { NSAttributedString(string: $0, font: font, textColor: theme.list.itemPlaceholderTextColor) }
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
} else {
strongSelf.countryButton.setTitle(strings.Login_SelectCountry_Title, with: Font.regular(20.0), with: theme.list.itemPlaceholderTextColor, for: [])
strongSelf.phoneInputNode.mask = nil
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
}
}
}
self.phoneInputNode.customFormatter = { number in
if let (_, code) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: [:]) {
return code.code
} else {
return nil
}
}
self.phoneInputNode.number = "+1"
self.phoneInputNode.returnAction = { [weak self] in
self?.checkPhone?()
@ -331,6 +352,10 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
#endif
}
func updateCountryCode() {
self.phoneAndCountryNode.phoneInputNode.codeAndNumber = self.codeAndNumber
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
var insets = layout.insets(options: [])
insets.top = navigationBarHeight

View File

@ -85,7 +85,7 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAt
if peer is TelegramUser {
controller.hasTimer = hasSchedule
}
controller.hasSilentPosting = !isSecretChat
controller.hasSilentPosting = true
}
controller.hasSchedule = hasSchedule
controller.reminder = peer.id == context.account.peerId

View File

@ -337,7 +337,7 @@ public final class OngoingCallVideoCapturer {
}
public func switchVideoInput(isFront: Bool) {
self.impl.switchVideoInput(isFront)
self.impl.switchVideoInput(isFront ? "" : "back")
}
public func makeOutgoingVideoView(completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {

View File

@ -102,14 +102,16 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
- (void)setOnIsMirroredUpdated:(void (^ _Nullable)(bool))onIsMirroredUpdated;
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
- (void)setVideoContentMode:(CALayerContentsGravity _Nonnull )mode;
- (void)setForceMirrored:(bool)forceMirrored;
#endif
@end
@interface OngoingCallThreadLocalContextVideoCapturer : NSObject
- (instancetype _Nonnull)init;
- (instancetype _Nonnull)initWithDeviceId:(NSString * _Nonnull)deviceId;
- (void)switchVideoInput:(bool)isFront;
- (void)switchVideoInput:(NSString * _Nonnull)deviceId;
- (void)setIsVideoEnabled:(bool)isVideoEnabled;
- (void)makeOutgoingVideoView:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
@ -132,7 +134,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
- (bool)needRate;
- (NSString * _Nullable)debugInfo;
- (NSString * _Nullable)version;
- (NSData * _Nonnull)getDerivedState;

View File

@ -13,7 +13,7 @@
#ifndef WEBRTC_IOS
#import "platform/darwin/VideoMetalViewMac.h"
#define GLVideoView VideoMetalView
#import "platform/darwin/GLVideoViewMac.h"
#define UIViewContentModeScaleAspectFill kCAGravityResizeAspectFill
#define UIViewContentModeScaleAspect kCAGravityResizeAspect
@ -142,18 +142,23 @@
@implementation OngoingCallThreadLocalContextVideoCapturer
- (instancetype _Nonnull)init {
return [self initWithDeviceId:@""];
}
- (instancetype _Nonnull)initWithDeviceId:(NSString * _Nonnull)deviceId {
self = [super init];
if (self != nil) {
_interface = tgcalls::VideoCaptureInterface::Create();
_interface = tgcalls::VideoCaptureInterface::Create(deviceId.UTF8String);
}
return self;
}
- (void)dealloc {
}
- (void)switchVideoInput:(bool)isFront {
_interface->switchToDevice(isFront ? "" : "back");
- (void)switchVideoInput:(NSString * _Nonnull)deviceId {
_interface->switchToDevice(deviceId.UTF8String);
}
- (void)setIsVideoEnabled:(bool)isVideoEnabled {
@ -210,7 +215,7 @@
NSString *_version;
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
int32_t _contextId;
OngoingCallNetworkTypeWebrtc _networkType;
NSTimeInterval _callReceiveTimeout;
NSTimeInterval _callRingTimeout;
@ -260,29 +265,29 @@
static tgcalls::NetworkType callControllerNetworkTypeForType(OngoingCallNetworkTypeWebrtc type) {
switch (type) {
case OngoingCallNetworkTypeWifi:
return tgcalls::NetworkType::WiFi;
case OngoingCallNetworkTypeCellularGprs:
return tgcalls::NetworkType::Gprs;
case OngoingCallNetworkTypeCellular3g:
return tgcalls::NetworkType::ThirdGeneration;
case OngoingCallNetworkTypeCellularLte:
return tgcalls::NetworkType::Lte;
default:
return tgcalls::NetworkType::ThirdGeneration;
case OngoingCallNetworkTypeWifi:
return tgcalls::NetworkType::WiFi;
case OngoingCallNetworkTypeCellularGprs:
return tgcalls::NetworkType::Gprs;
case OngoingCallNetworkTypeCellular3g:
return tgcalls::NetworkType::ThirdGeneration;
case OngoingCallNetworkTypeCellularLte:
return tgcalls::NetworkType::Lte;
default:
return tgcalls::NetworkType::ThirdGeneration;
}
}
static tgcalls::DataSaving callControllerDataSavingForType(OngoingCallDataSavingWebrtc type) {
switch (type) {
case OngoingCallDataSavingNever:
return tgcalls::DataSaving::Never;
case OngoingCallDataSavingCellular:
return tgcalls::DataSaving::Mobile;
case OngoingCallDataSavingAlways:
return tgcalls::DataSaving::Always;
default:
return tgcalls::DataSaving::Never;
case OngoingCallDataSavingNever:
return tgcalls::DataSaving::Never;
case OngoingCallDataSavingCellular:
return tgcalls::DataSaving::Mobile;
case OngoingCallDataSavingAlways:
return tgcalls::DataSaving::Always;
default:
return tgcalls::DataSaving::Never;
}
}
@ -706,11 +711,11 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
dispatch_async(dispatch_get_main_queue(), ^{
if ([VideoMetalView isSupported]) {
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
#if TARGET_OS_IPHONE
#if TARGET_OS_IPHONE
remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
#else
#else
remoteRenderer.videoContentMode = UIViewContentModeScaleAspect;
#endif
#endif
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;

@ -1 +1 @@
Subproject commit b906b5a955f1e6e195cb91c4513d4cb7b3620ea6
Subproject commit d8b106c94e5a5e20020ad862d835e921d4f102d9

@ -1 +1 @@
Subproject commit 2d20a42ab60f8fed37612361f2ea776d0bc7ca1a
Subproject commit c413c8f18eb1932b100850505031980e27160d5f