Peers Nearby improvements

This commit is contained in:
Ilya Laktyushin 2019-06-20 03:41:03 +02:00
parent 6fad8ccc17
commit 8d723f1c45
21 changed files with 2607 additions and 2897 deletions

View File

@ -4425,7 +4425,6 @@ Any member of this group will be able to see messages in the channel.";
"Channel.AdminLog.MessageTransferedNameUsername" = "transferred ownership to %1$@ (%2$@)"; "Channel.AdminLog.MessageTransferedNameUsername" = "transferred ownership to %1$@ (%2$@)";
"Channel.AdminLog.MessageChangedGroupGeoLocation" = "changed group location to \"%@\""; "Channel.AdminLog.MessageChangedGroupGeoLocation" = "changed group location to \"%@\"";
"Channel.AdminLog.MessageRemovedGroupGeoLocation" = "%@ removed group location";
"Map.SetThisLocation" = "Set This Location"; "Map.SetThisLocation" = "Set This Location";
@ -4450,6 +4449,6 @@ Any member of this group will be able to see messages in the channel.";
"Group.PublicLink.Title" = "Public Link"; "Group.PublicLink.Title" = "Public Link";
"Group.PublicLink.Placeholder" = "link"; "Group.PublicLink.Placeholder" = "link";
"Group.PublicLink.Info" = "People can share this link with others and find your group using Telegram search.\n\nYou can use use **a-z**, **0-9** and undescores. Minimum length is **5** characters."; "Group.PublicLink.Info" = "People can share this link with others and find your group using Telegram search.\n\nYou can use **a-z**, **0-9** and underscores. Minimum length is **5** characters.";
"CreateGroup.ErrorLocatedGroupsTooMuch" = "Sorry, you have too many location-based groups already. Please delete one of your existing ones first."; "CreateGroup.ErrorLocatedGroupsTooMuch" = "Sorry, you have too many location-based groups already. Please delete one of your existing ones first.";

View File

@ -31,6 +31,8 @@ public final class PeersNearbyContext {
private var entries: [PeerNearby]? private var entries: [PeerNearby]?
public init(network: Network, accountStateManager: AccountStateManager, coordinate: (latitude: Double, longitude: Double)) { public init(network: Network, accountStateManager: AccountStateManager, coordinate: (latitude: Double, longitude: Double)) {
let expiryThreshold: Double = 10.0
self.disposable.set((network.request(Api.functions.contacts.getLocated(geoPoint: .inputGeoPoint(lat: coordinate.latitude, long: coordinate.longitude))) self.disposable.set((network.request(Api.functions.contacts.getLocated(geoPoint: .inputGeoPoint(lat: coordinate.latitude, long: coordinate.longitude)))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in |> `catch` { _ -> Signal<Api.Updates?, NoError> in
@ -62,8 +64,8 @@ public final class PeersNearbyContext {
} }
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
var entries = strongSelf.entries?.filter { Double($0.expires) > timestamp } ?? [] var entries = strongSelf.entries?.filter { Double($0.expires) + expiryThreshold > timestamp } ?? []
let updatedEntries = updatedEntries.filter { Double($0.expires) > timestamp } let updatedEntries = updatedEntries.filter { Double($0.expires) + expiryThreshold > timestamp }
var existingPeerIds: [PeerId: Int] = [:] var existingPeerIds: [PeerId: Int] = [:]
for i in 0 ..< entries.count { for i in 0 ..< entries.count {
@ -91,7 +93,7 @@ public final class PeersNearbyContext {
} }
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
strongSelf.entries = strongSelf.entries?.filter { Double($0.expires) > timestamp } strongSelf.entries = strongSelf.entries?.filter { Double($0.expires) + expiryThreshold > timestamp }
}, queue: self.queue) }, queue: self.queue)
self.timer?.start() self.timer?.start()
} }

View File

@ -305,13 +305,10 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa
break break
case .Strikethrough: case .Strikethrough:
apiEntities.append(.messageEntityStrike(offset: offset, length: length)) apiEntities.append(.messageEntityStrike(offset: offset, length: length))
break
case .BlockQuote: case .BlockQuote:
apiEntities.append(.messageEntityBlockquote(offset: offset, length: length)) apiEntities.append(.messageEntityBlockquote(offset: offset, length: length))
break
case .Underline: case .Underline:
apiEntities.append(.messageEntityUnderline(offset: offset, length: length)) apiEntities.append(.messageEntityUnderline(offset: offset, length: length))
break
case .Custom: case .Custom:
break break
} }

View File

@ -72,8 +72,6 @@ apple_library(
'TelegramUI/DeviceProximityManager.h', 'TelegramUI/DeviceProximityManager.h',
'TelegramUI/RaiseToListenActivator.h', 'TelegramUI/RaiseToListenActivator.h',
'TelegramUI/TGMimeTypeMap.h', 'TelegramUI/TGMimeTypeMap.h',
'TelegramUI/TGEmojiSuggestions.h',
'TelegramUI/TGChannelIntroController.h',
'TelegramUI/EDSunriseSet.h', 'TelegramUI/EDSunriseSet.h',
'TelegramUI/TGBridgeAudioDecoder.h', 'TelegramUI/TGBridgeAudioDecoder.h',
'TelegramUI/TGBridgeAudioEncoder.h', 'TelegramUI/TGBridgeAudioEncoder.h',

View File

@ -950,13 +950,6 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: [], media: [mediaMap], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: [], media: [mediaMap], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(isAdmin: false, isContact: false))) return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(isAdmin: false, isContact: false)))
} else { } else {
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRemovedGroupGeoLocation(author?.displayTitle ?? ""), generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
}
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities) let action = TelegramMediaActionType.customText(text: text, entities: entities)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])

View File

@ -11,6 +11,7 @@ struct ChatTextInputAttributes {
static let bold = NSAttributedStringKey(rawValue: "Attribute__Bold") static let bold = NSAttributedStringKey(rawValue: "Attribute__Bold")
static let italic = NSAttributedStringKey(rawValue: "Attribute__Italic") static let italic = NSAttributedStringKey(rawValue: "Attribute__Italic")
static let monospace = NSAttributedStringKey(rawValue: "Attribute__Monospace") static let monospace = NSAttributedStringKey(rawValue: "Attribute__Monospace")
static let strikethrough = NSAttributedStringKey(rawValue: "Attribute__Strikethrough")
static let textMention = NSAttributedStringKey(rawValue: "Attribute__TextMention") static let textMention = NSAttributedStringKey(rawValue: "Attribute__TextMention")
static let textUrl = NSAttributedStringKey(rawValue: "Attribute__TextUrl") static let textUrl = NSAttributedStringKey(rawValue: "Attribute__TextUrl")
} }
@ -23,7 +24,7 @@ func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedStr
for (key, value) in attributes { for (key, value) in attributes {
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl { if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
result.addAttribute(key, value: value, range: range) result.addAttribute(key, value: value, range: range)
} else if key == ChatTextInputAttributes.bold || key == ChatTextInputAttributes.italic || key == ChatTextInputAttributes.monospace { } else if key == ChatTextInputAttributes.bold || key == ChatTextInputAttributes.italic || key == ChatTextInputAttributes.monospace || key == ChatTextInputAttributes.strikethrough {
result.addAttribute(key, value: value, range: range) result.addAttribute(key, value: value, range: range)
} }
} }
@ -37,7 +38,6 @@ private struct FontAttributes: OptionSet {
static let bold = FontAttributes(rawValue: 1 << 0) static let bold = FontAttributes(rawValue: 1 << 0)
static let italic = FontAttributes(rawValue: 1 << 1) static let italic = FontAttributes(rawValue: 1 << 1)
static let monospace = FontAttributes(rawValue: 1 << 2) static let monospace = FontAttributes(rawValue: 1 << 2)
static let strikethrough = FontAttributes(rawValue: 1 << 3)
} }
func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor) -> NSAttributedString { func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor) -> NSAttributedString {
@ -66,13 +66,20 @@ func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize:
} else if key == ChatTextInputAttributes.monospace { } else if key == ChatTextInputAttributes.monospace {
result.addAttribute(key, value: value, range: range) result.addAttribute(key, value: value, range: range)
fontAttributes.insert(.monospace) fontAttributes.insert(.monospace)
} else if key == ChatTextInputAttributes.strikethrough {
result.addAttribute(key, value: value, range: range)
result.addAttribute(NSAttributedStringKey.strikethroughStyle, value: NSUnderlineStyle.styleSingle.rawValue as NSNumber, range: range)
} }
} }
if !fontAttributes.isEmpty { if !fontAttributes.isEmpty {
var font: UIFont? var font: UIFont?
if fontAttributes == [.bold, .italic, .monospace] { if fontAttributes == [.bold, .italic, .monospace] {
font = Font.semiboldItalicMonospace(fontSize)
} else if fontAttributes == [.bold, .monospace] {
font = Font.semiboldMonospace(fontSize)
} else if fontAttributes == [.italic, .monospace] {
font = Font.italicMonospace(fontSize)
} else if fontAttributes == [.bold, .italic] { } else if fontAttributes == [.bold, .italic] {
font = Font.semiboldItalic(fontSize) font = Font.semiboldItalic(fontSize)
} else if fontAttributes == [.bold] { } else if fontAttributes == [.bold] {
@ -384,7 +391,7 @@ private func refreshTextUrls(text: NSString, initialAttributedText: NSAttributed
} }
func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme: PresentationTheme, baseFontSize: CGFloat) { func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme: PresentationTheme, baseFontSize: CGFloat) {
guard var initialAttributedText = textNode.attributedText, initialAttributedText.length != 0 else { guard let initialAttributedText = textNode.attributedText, initialAttributedText.length != 0 else {
return return
} }
@ -406,6 +413,7 @@ func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme: Prese
textNode.textView.textStorage.removeAttribute(NSAttributedStringKey.font, range: fullRange) textNode.textView.textStorage.removeAttribute(NSAttributedStringKey.font, range: fullRange)
textNode.textView.textStorage.removeAttribute(NSAttributedStringKey.foregroundColor, range: fullRange) textNode.textView.textStorage.removeAttribute(NSAttributedStringKey.foregroundColor, range: fullRange)
textNode.textView.textStorage.removeAttribute(NSAttributedStringKey.underlineStyle, range: fullRange) textNode.textView.textStorage.removeAttribute(NSAttributedStringKey.underlineStyle, range: fullRange)
textNode.textView.textStorage.removeAttribute(NSAttributedStringKey.strikethroughStyle, range: fullRange)
textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange) textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange)
textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange) textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange)
@ -432,6 +440,9 @@ func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme: Prese
} else if key == ChatTextInputAttributes.monospace { } else if key == ChatTextInputAttributes.monospace {
textNode.textView.textStorage.addAttribute(key, value: value, range: range) textNode.textView.textStorage.addAttribute(key, value: value, range: range)
fontAttributes.insert(.monospace) fontAttributes.insert(.monospace)
} 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)
} }
} }
@ -492,8 +503,8 @@ func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, attribute:
for (key, _) in attributes { for (key, _) in attributes {
if key == attribute && range == nsRange { if key == attribute && range == nsRange {
addAttribute = false addAttribute = false
attributesToRemove.append(key)
} }
attributesToRemove.append(key)
} }
} }
@ -634,7 +645,7 @@ func breakChatInputText(_ text: NSAttributedString) -> [NSAttributedString] {
} }
} }
private let markdownRegexFormat = "(^|\\s|\\n)(````?)([\\s\\S]+?)(````?)([\\s\\n\\.,:?!;]|$)|(^|\\s)(`|\\*\\*|__)([^\\n]+?)\\7([\\s\\.,:?!;]|$)|@(\\d+)\\s*\\((.+?)\\)" private let markdownRegexFormat = "(^|\\s|\\n)(````?)([\\s\\S]+?)(````?)([\\s\\n\\.,:?!;]|$)|(^|\\s)(`|\\*\\*|__|~~)([^\\n]+?)\\7([\\s\\.,:?!;]|$)|@(\\d+)\\s*\\((.+?)\\)"
private let markdownRegex = try? NSRegularExpression(pattern: markdownRegexFormat, options: [.caseInsensitive, .anchorsMatchLines]) private let markdownRegex = try? NSRegularExpression(pattern: markdownRegexFormat, options: [.caseInsensitive, .anchorsMatchLines])
func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttributedString { func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttributedString {
@ -678,6 +689,9 @@ func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttributedStri
case "__": case "__":
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.italic: true as NSNumber])) result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.italic: true as NSNumber]))
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
case "~~":
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.strikethrough: true as NSNumber]))
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
default: default:
break break
} }

View File

@ -855,7 +855,7 @@ final class ContactListNode: ASDisplayNode {
var authorizeImpl: (() -> Void)? var authorizeImpl: (() -> Void)?
var openPrivacyPolicyImpl: (() -> Void)? var openPrivacyPolicyImpl: (() -> Void)?
self.authorizationNode = PermissionContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, kind: PermissionKind.contacts.rawValue, icon: UIImage(bundleImageName: "Settings/Permissions/Contacts"), title: self.presentationData.strings.Contacts_PermissionsTitle, text: self.presentationData.strings.Contacts_PermissionsText, buttonTitle: self.presentationData.strings.Contacts_PermissionsAllow, buttonAction: { self.authorizationNode = PermissionContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, kind: PermissionKind.contacts.rawValue, icon: .image(UIImage(bundleImageName: "Settings/Permissions/Contacts")), title: self.presentationData.strings.Contacts_PermissionsTitle, text: self.presentationData.strings.Contacts_PermissionsText, buttonTitle: self.presentationData.strings.Contacts_PermissionsAllow, buttonAction: {
authorizeImpl?() authorizeImpl?()
}, openPrivacyPolicy: { }, openPrivacyPolicy: {
openPrivacyPolicyImpl?() openPrivacyPolicyImpl?()
@ -1261,7 +1261,7 @@ final class ContactListNode: ASDisplayNode {
let authorizationPreviousHidden = strongSelf.authorizationNode.isHidden let authorizationPreviousHidden = strongSelf.authorizationNode.isHidden
strongSelf.authorizationNode.removeFromSupernode() strongSelf.authorizationNode.removeFromSupernode()
strongSelf.authorizationNode = PermissionContentNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, kind: PermissionKind.contacts.rawValue, icon: UIImage(bundleImageName: "Settings/Permissions/Contacts"), title: strongSelf.presentationData.strings.Contacts_PermissionsTitle, text: strongSelf.presentationData.strings.Contacts_PermissionsText, buttonTitle: strongSelf.presentationData.strings.Contacts_PermissionsAllow, buttonAction: { strongSelf.authorizationNode = PermissionContentNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, kind: PermissionKind.contacts.rawValue, icon: .image(UIImage(bundleImageName: "Settings/Permissions/Contacts")), title: strongSelf.presentationData.strings.Contacts_PermissionsTitle, text: strongSelf.presentationData.strings.Contacts_PermissionsText, buttonTitle: strongSelf.presentationData.strings.Contacts_PermissionsAllow, buttonAction: {
authorizeImpl?() authorizeImpl?()
}, openPrivacyPolicy: { }, openPrivacyPolicy: {
openPrivacyPolicyImpl?() openPrivacyPolicyImpl?()

View File

@ -132,6 +132,8 @@ func generateChatInputTextEntities(_ text: NSAttributedString) -> [MessageTextEn
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Italic)) entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Italic))
} else if key == ChatTextInputAttributes.monospace { } else if key == ChatTextInputAttributes.monospace {
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Code)) 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.textMention, let value = value as? ChatTextInputTextMentionAttribute { } else if key == ChatTextInputAttributes.textMention, let value = value as? ChatTextInputTextMentionAttribute {
entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .TextMention(peerId: value.peerId))) entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .TextMention(peerId: value.peerId)))
} else if key == ChatTextInputAttributes.textUrl, let value = value as? ChatTextInputTextUrlAttribute { } else if key == ChatTextInputAttributes.textUrl, let value = value as? ChatTextInputTextUrlAttribute {

View File

@ -184,13 +184,14 @@ class ItemListAddressItemNode: ListViewItemNode {
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, 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 (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()))
var padding: CGFloat = !item.label.isEmpty ? 39.0 : 20.0 let padding: CGFloat = !item.label.isEmpty ? 39.0 : 20.0
let contentSize = CGSize(width: params.width, height: textLayout.size.height + padding)
let imageSide = min(90.0, contentSize.height - 18.0) let imageSide = min(90.0, max(46.0, textLayout.size.height + padding - 18.0))
let imageSize = CGSize(width: imageSide, height: imageSide) let imageSize = CGSize(width: imageSide, height: imageSide)
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: 4.0), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: 4.0), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
let contentSize = CGSize(width: params.width, height: max(textLayout.size.height + padding, imageSize.height + 18.0))
let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
return (nodeLayout, { [weak self] animation in return (nodeLayout, { [weak self] animation in
if let strongSelf = self { if let strongSelf = self {

View File

@ -19,15 +19,17 @@ class ItemListSectionHeaderItem: ListViewItem, ItemListItem {
let theme: PresentationTheme let theme: PresentationTheme
let text: String let text: String
let multiline: Bool let multiline: Bool
let activityIndicator: Bool
let accessoryText: ItemListSectionHeaderAccessoryText? let accessoryText: ItemListSectionHeaderAccessoryText?
let sectionId: ItemListSectionId let sectionId: ItemListSectionId
let isAlwaysPlain: Bool = true let isAlwaysPlain: Bool = true
init(theme: PresentationTheme, text: String, multiline: Bool = false, accessoryText: ItemListSectionHeaderAccessoryText? = nil, sectionId: ItemListSectionId) { init(theme: PresentationTheme, text: String, multiline: Bool = false, activityIndicator: Bool = false, accessoryText: ItemListSectionHeaderAccessoryText? = nil, sectionId: ItemListSectionId) {
self.theme = theme self.theme = theme
self.text = text self.text = text
self.multiline = multiline self.multiline = multiline
self.activityIndicator = activityIndicator
self.accessoryText = accessoryText self.accessoryText = accessoryText
self.sectionId = sectionId self.sectionId = sectionId
} }
@ -72,8 +74,11 @@ class ItemListSectionHeaderItem: ListViewItem, ItemListItem {
private let titleFont = Font.regular(14.0) private let titleFont = Font.regular(14.0)
class ItemListSectionHeaderItemNode: ListViewItemNode { class ItemListSectionHeaderItemNode: ListViewItemNode {
private var item: ItemListSectionHeaderItem?
private let titleNode: TextNode private let titleNode: TextNode
private let accessoryTextNode: TextNode private let accessoryTextNode: TextNode
private var activityIndicator: ActivityIndicator?
private let activateArea: AccessibilityAreaNode private let activateArea: AccessibilityAreaNode
@ -102,6 +107,8 @@ class ItemListSectionHeaderItemNode: ListViewItemNode {
let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeAccessoryTextLayout = TextNode.asyncLayout(self.accessoryTextNode) let makeAccessoryTextLayout = TextNode.asyncLayout(self.accessoryTextNode)
let previousItem = self.item
return { item, params, neighbors in return { item, params, neighbors in
let leftInset: CGFloat = 15.0 + params.leftInset let leftInset: CGFloat = 15.0 + params.leftInset
@ -136,6 +143,8 @@ class ItemListSectionHeaderItemNode: ListViewItemNode {
return (layout, { [weak self] in return (layout, { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.item = item
let _ = titleApply() let _ = titleApply()
let _ = accessoryApply() let _ = accessoryApply()
@ -144,6 +153,31 @@ class ItemListSectionHeaderItemNode: ListViewItemNode {
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 7.0), size: titleLayout.size) strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 7.0), size: titleLayout.size)
strongSelf.accessoryTextNode.frame = CGRect(origin: CGPoint(x: params.width - leftInset - accessoryLayout.size.width, y: 7.0), size: accessoryLayout.size) strongSelf.accessoryTextNode.frame = CGRect(origin: CGPoint(x: params.width - leftInset - accessoryLayout.size.width, y: 7.0), size: accessoryLayout.size)
if previousItem?.activityIndicator != item.activityIndicator {
if item.activityIndicator {
let activityIndicator: ActivityIndicator
if let currentActivityIndicator = strongSelf.activityIndicator {
activityIndicator = currentActivityIndicator
} else {
activityIndicator = ActivityIndicator(type: .custom(item.theme.list.sectionHeaderTextColor, 18.0, 1.0, false))
strongSelf.addSubnode(activityIndicator)
strongSelf.activityIndicator = activityIndicator
}
activityIndicator.isHidden = false
if previousItem != nil {
activityIndicator.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
} else if let activityIndicator = strongSelf.activityIndicator {
activityIndicator.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, completion: { finished in
if finished {
activityIndicator.isHidden = true
}
})
}
}
strongSelf.activityIndicator?.frame = CGRect(origin: CGPoint(x: strongSelf.titleNode.frame.maxX + 6.0, y: 7.0 - UIScreenPixel), size: CGSize(width: 18.0, height: 18.0))
} }
}) })
} }

View File

@ -1,31 +0,0 @@
import Foundation
import TelegramCore
import Display
import TelegramPresentationData
import TelegramUIPrivateModule
func legacyChannelIntroController(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings) -> ViewController {
let controller = LegacyController(presentation: .custom, theme: theme)
controller.bind(controller: TGChannelIntroController(context: controller.context, getLocalizedString: { string in
guard let string = string else {
return nil
}
if let value = strings.primaryComponent.dict[string] {
return value
} else if let value = strings.secondaryComponent?.dict[string] {
return value
} else {
return string
}
}, theme: TGChannelIntroControllerTheme(backgroundColor: theme.list.plainBackgroundColor, primaryColor: theme.list.itemPrimaryTextColor, secondaryColor: theme.list.itemSecondaryTextColor, accentColor: theme.list.itemAccentColor, backArrowImage: NavigationBarTheme.generateBackArrowImage(color: theme.list.itemAccentColor), introImage: UIImage(bundleImageName: "Chat/Intro/ChannelIntro")), dismiss: { [weak controller] in
if let navigationController = controller?.navigationController as? NavigationController {
_ = navigationController.popViewController(animated: true)
}
}, completion: { [weak controller] in
if let navigationController = controller?.navigationController as? NavigationController {
navigationController.replaceTopController(createChannelController(context: context), animated: true)
}
})!)
return controller
}

View File

@ -56,11 +56,11 @@ private enum PeersNearbySection: Int32 {
private enum PeersNearbyEntry: ItemListNodeEntry { private enum PeersNearbyEntry: ItemListNodeEntry {
case header(PresentationTheme, String) case header(PresentationTheme, String)
case usersHeader(PresentationTheme, String) case usersHeader(PresentationTheme, String, Bool)
case empty(PresentationTheme, String, Bool) case empty(PresentationTheme, String)
case user(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry) case user(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry)
case groupsHeader(PresentationTheme, String) case groupsHeader(PresentationTheme, String, Bool)
case createGroup(PresentationTheme, String, Double?, Double?, String?) case createGroup(PresentationTheme, String, Double?, Double?, String?)
case group(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry) case group(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry)
@ -111,14 +111,14 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .usersHeader(lhsTheme, lhsText): case let .usersHeader(lhsTheme, lhsText, lhsLoading):
if case let .usersHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { if case let .usersHeader(rhsTheme, rhsText, rhsLoading) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLoading == rhsLoading {
return true return true
} else { } else {
return false return false
} }
case let .empty(lhsTheme, lhsText, lhsLoading): case let .empty(lhsTheme, lhsText):
if case let .empty(rhsTheme, rhsText, rhsLoading) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLoading == rhsLoading { if case let .empty(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true return true
} else { } else {
return false return false
@ -129,8 +129,8 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .groupsHeader(lhsTheme, lhsText): case let .groupsHeader(lhsTheme, lhsText, lhsLoading):
if case let .groupsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { if case let .groupsHeader(rhsTheme, rhsText, rhsLoading) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLoading == rhsLoading {
return true return true
} else { } else {
return false return false
@ -181,16 +181,16 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
switch self { switch self {
case let .header(theme, text): case let .header(theme, text):
return PeersNearbyHeaderItem(theme: theme, text: text, sectionId: self.section) return PeersNearbyHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .usersHeader(theme, text): case let .usersHeader(theme, text, loading):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) return ItemListSectionHeaderItem(theme: theme, text: text, activityIndicator: loading, sectionId: self.section)
case let .empty(theme, text, _): case let .empty(theme, text):
return ItemListPlaceholderItem(theme: theme, text: text, sectionId: self.section, style: .blocks) return ItemListPlaceholderItem(theme: theme, text: text, sectionId: self.section, style: .blocks)
case let .user(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer): case let .user(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: { return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
arguments.openChat(peer.peer.0) arguments.openChat(peer.peer.0)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, hasTopGroupInset: false, tag: nil) }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, hasTopGroupInset: false, tag: nil)
case let .groupsHeader(theme, text): case let .groupsHeader(theme, text, loading):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) return ItemListSectionHeaderItem(theme: theme, text: text, activityIndicator: loading, sectionId: self.section)
case let .createGroup(theme, title, latitude, longitude, address): case let .createGroup(theme, title, latitude, longitude, address):
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.createGroupIcon(theme), title: title, alwaysPlain: false, sectionId: self.section, editing: false, action: { return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.createGroupIcon(theme), title: title, alwaysPlain: false, sectionId: self.section, editing: false, action: {
if let latitude = latitude, let longitude = longitude { if let latitude = latitude, let longitude = longitude {
@ -249,7 +249,7 @@ private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationDa
var entries: [PeersNearbyEntry] = [] var entries: [PeersNearbyEntry] = []
entries.append(.header(presentationData.theme, presentationData.strings.PeopleNearby_Description)) entries.append(.header(presentationData.theme, presentationData.strings.PeopleNearby_Description))
entries.append(.usersHeader(presentationData.theme, presentationData.strings.PeopleNearby_Users.uppercased())) entries.append(.usersHeader(presentationData.theme, presentationData.strings.PeopleNearby_Users.uppercased(), data == nil))
if let data = data, !data.users.isEmpty { if let data = data, !data.users.isEmpty {
var i: Int32 = 0 var i: Int32 = 0
for user in data.users { for user in data.users {
@ -257,10 +257,10 @@ private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationDa
i += 1 i += 1
} }
} else { } else {
entries.append(.empty(presentationData.theme, presentationData.strings.PeopleNearby_UsersEmpty, data == nil)) entries.append(.empty(presentationData.theme, presentationData.strings.PeopleNearby_UsersEmpty))
} }
entries.append(.groupsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Groups.uppercased())) entries.append(.groupsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Groups.uppercased(), data == nil))
entries.append(.createGroup(presentationData.theme, presentationData.strings.PeopleNearby_CreateGroup, data?.latitude, data?.longitude, data?.address)) entries.append(.createGroup(presentationData.theme, presentationData.strings.PeopleNearby_CreateGroup, data?.latitude, data?.longitude, data?.address))
if let data = data, !data.groups.isEmpty { if let data = data, !data.groups.isEmpty {
var i: Int32 = 0 var i: Int32 = 0

View File

@ -4,6 +4,20 @@ import Display
import AsyncDisplayKit import AsyncDisplayKit
import TelegramPresentationData import TelegramPresentationData
enum PermissionContentIcon {
case image(UIImage?)
case icon(PermissionControllerCustomIcon)
func imageForTheme(_ theme: PresentationTheme) -> UIImage? {
switch self {
case let .image(image):
return image
case let .icon(icon):
return theme.overallDarkAppearance ? (icon.dark ?? icon.light) : icon.light
}
}
}
final class PermissionContentNode: ASDisplayNode { final class PermissionContentNode: ASDisplayNode {
private var theme: PresentationTheme private var theme: PresentationTheme
let kind: Int32 let kind: Int32
@ -17,6 +31,7 @@ final class PermissionContentNode: ASDisplayNode {
private let footerNode: ImmediateTextNode private let footerNode: ImmediateTextNode
private let privacyPolicyButton: HighlightableButtonNode private let privacyPolicyButton: HighlightableButtonNode
private let icon: PermissionContentIcon
private var title: String private var title: String
private var text: String private var text: String
@ -25,13 +40,14 @@ final class PermissionContentNode: ASDisplayNode {
var validLayout: (CGSize, UIEdgeInsets)? var validLayout: (CGSize, UIEdgeInsets)?
init(theme: PresentationTheme, strings: PresentationStrings, kind: Int32, icon: UIImage?, title: String, subtitle: String? = nil, text: String, buttonTitle: String, footerText: String? = nil, buttonAction: @escaping () -> Void, openPrivacyPolicy: (() -> Void)?) { init(theme: PresentationTheme, strings: PresentationStrings, kind: Int32, icon: PermissionContentIcon, title: String, subtitle: String? = nil, text: String, buttonTitle: String, footerText: String? = nil, buttonAction: @escaping () -> Void, openPrivacyPolicy: (() -> Void)?) {
self.theme = theme self.theme = theme
self.kind = kind self.kind = kind
self.buttonAction = buttonAction self.buttonAction = buttonAction
self.openPrivacyPolicy = openPrivacyPolicy self.openPrivacyPolicy = openPrivacyPolicy
self.icon = icon
self.title = title self.title = title
self.text = text self.text = text
@ -75,7 +91,7 @@ final class PermissionContentNode: ASDisplayNode {
super.init() super.init()
self.iconNode.image = icon self.iconNode.image = icon.imageForTheme(theme)
self.title = title self.title = title
let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor) let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor)
@ -115,6 +131,8 @@ final class PermissionContentNode: ASDisplayNode {
let theme = presentationData.theme let theme = presentationData.theme
self.theme = theme self.theme = theme
self.iconNode.image = self.icon.imageForTheme(theme)
let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor) let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor)
let link = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemAccentColor, additionalAttributes: [TelegramTextAttributes.URL: ""]) let link = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemAccentColor, additionalAttributes: [TelegramTextAttributes.URL: ""])
self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text.replacingOccurrences(of: "]", with: "]()"), attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center) self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text.replacingOccurrences(of: "]", with: "]()"), attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center)

View File

@ -84,18 +84,14 @@ final class PermissionControllerNode: ASDisplayNode {
return UITracingLayerView() return UITracingLayerView()
}) })
self.applyPresentationData() self.updatePresentationData(self.presentationData)
} }
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
self.presentationData = presentationData self.presentationData = presentationData
self.applyPresentationData()
}
private func applyPresentationData() {
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.contentNode?.updatePresentationData(self.presentationData)
} }
func animateIn(completion: (() -> Void)? = nil) { func animateIn(completion: (() -> Void)? = nil) {
@ -208,7 +204,7 @@ final class PermissionControllerNode: ASDisplayNode {
hasPrivacyPolicy = false hasPrivacyPolicy = false
} }
let contentNode = PermissionContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, kind: dataState.kind.rawValue, icon: icon, title: title, text: text, buttonTitle: buttonTitle, buttonAction: { [weak self] in let contentNode = PermissionContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, kind: dataState.kind.rawValue, icon: .image(icon), title: title, text: text, buttonTitle: buttonTitle, buttonAction: { [weak self] in
self?.allow?() self?.allow?()
}, openPrivacyPolicy: hasPrivacyPolicy ? self.openPrivacyPolicy : nil) }, openPrivacyPolicy: hasPrivacyPolicy ? self.openPrivacyPolicy : nil)
self.insertSubnode(contentNode, at: 0) self.insertSubnode(contentNode, at: 0)
@ -238,14 +234,7 @@ final class PermissionControllerNode: ASDisplayNode {
transition.updateFrame(node: contentNode, frame: contentFrame) transition.updateFrame(node: contentNode, frame: contentFrame)
contentNode.updateLayout(size: contentFrame.size, insets: insets, transition: transition) contentNode.updateLayout(size: contentFrame.size, insets: insets, transition: transition)
} else { } else {
let iconImage: UIImage? let contentNode = PermissionContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, kind: 0, icon: .icon(icon), title: title, subtitle: subtitle, text: text, buttonTitle: buttonTitle, footerText: footerText, buttonAction: { [weak self] in
if self.presentationData.theme.overallDarkAppearance {
iconImage = icon.dark ?? icon.light
} else {
iconImage = icon.light
}
let contentNode = PermissionContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, kind: 0, icon: iconImage, title: title, subtitle: subtitle, text: text, buttonTitle: buttonTitle, footerText: footerText, buttonAction: { [weak self] in
self?.allow?() self?.allow?()
}, openPrivacyPolicy: nil) }, openPrivacyPolicy: nil)
self.insertSubnode(contentNode, at: 0) self.insertSubnode(contentNode, at: 0)

View File

@ -34,6 +34,8 @@ func chatInputStateStringWithAppliedEntities(_ text: String, entities: [MessageT
string.addAttribute(ChatTextInputAttributes.textUrl, value: ChatTextInputTextUrlAttribute(url: url), range: range) string.addAttribute(ChatTextInputAttributes.textUrl, value: ChatTextInputTextUrlAttribute(url: url), range: range)
case .Code, .Pre: case .Code, .Pre:
string.addAttribute(ChatTextInputAttributes.monospace, value: true as NSNumber, range: range) string.addAttribute(ChatTextInputAttributes.monospace, value: true as NSNumber, range: range)
case .Strikethrough:
string.addAttribute(ChatTextInputAttributes.strikethrough, value: true as NSNumber, range: range)
default: default:
break break
} }
@ -121,6 +123,8 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
nsString = text as NSString nsString = text as NSString
} }
string.addAttribute(NSAttributedStringKey(rawValue: TelegramTextAttributes.PeerTextMention), value: nsString!.substring(with: range), range: range) 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 let .TextMention(peerId): case let .TextMention(peerId):
string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range) string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range)
if underlineLinks && underlineAllLinks { if underlineLinks && underlineAllLinks {

View File

@ -1,20 +0,0 @@
#import <LegacyComponents/LegacyComponents.h>
@interface TGChannelIntroControllerTheme : NSObject
@property (nonatomic, strong, readonly) UIColor *backgroundColor;
@property (nonatomic, strong, readonly) UIColor *primaryColor;
@property (nonatomic, strong, readonly) UIColor *secondaryColor;
@property (nonatomic, strong, readonly) UIColor *accentColor;
@property (nonatomic, strong, readonly) UIImage *backArrowImage;
@property (nonatomic, strong, readonly) UIImage *introImage;
- (instancetype)initWithBackgroundColor:(UIColor *)backgroundColor primaryColor:(UIColor *)primaryColor secondaryColor:(UIColor *)secondaryColor accentColor:(UIColor *)accentColor backArrowImage:(UIImage *)backArrowImage introImage:(UIImage *)introImage;
@end
@interface TGChannelIntroController : TGViewController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context getLocalizedString:(NSString *(^)(NSString *))getLocalizedString theme:(TGChannelIntroControllerTheme *)theme dismiss:(void (^)(void))dismiss completion:(void (^)(void))completion;
@end

View File

@ -1,266 +0,0 @@
#import "TGChannelIntroController.h"
#import <LegacyComponents/LegacyComponents.h>
#import <LegacyComponents/TGModernButton.h>
@implementation TGChannelIntroControllerTheme
- (instancetype)initWithBackgroundColor:(UIColor *)backgroundColor primaryColor:(UIColor *)primaryColor secondaryColor:(UIColor *)secondaryColor accentColor:(UIColor *)accentColor backArrowImage:(UIImage *)backArrowImage introImage:(UIImage *)introImage {
self = [super init];
if (self != nil) {
_backgroundColor = backgroundColor;
_primaryColor = primaryColor;
_secondaryColor = secondaryColor;
_accentColor = accentColor;
_backArrowImage = backArrowImage;
_introImage = introImage;
}
return self;
}
@end
@interface TGChannelIntroController ()
{
TGModernButton *_backButton;
UIImageView *_phoneImageView;
UILabel *_titleLabel;
UILabel *_descriptionLabel;
TGModernButton *_createButton;
TGChannelIntroControllerTheme *_theme;
NSString *(^_getLocalizedString)(NSString *);
void (^_dismiss)(void);
void (^_completion)(void);
}
@end
@implementation TGChannelIntroController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context getLocalizedString:(NSString *(^)(NSString *))getLocalizedString theme:(TGChannelIntroControllerTheme *)theme dismiss:(void (^)(void))dismiss completion:(void (^)(void))completion {
self = [super initWithContext:context];
if (self != nil) {
_getLocalizedString = [getLocalizedString copy];
_theme = theme;
_dismiss = [dismiss copy];
_completion = [completion copy];
}
return self;
}
- (void)loadView
{
[super loadView];
self.view.backgroundColor = _theme.backgroundColor;
UIImage *image = _theme.backArrowImage;
UIGraphicsBeginImageContextWithOptions(image.size, false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
CGContextSetBlendMode (context, kCGBlendModeSourceAtop);
CGContextSetFillColorWithColor(context, _theme.accentColor.CGColor);
CGContextFillRect(context, CGRectMake(0, 0, image.size.width, image.size.height));
UIImage *arrowImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_backButton = [[TGModernButton alloc] initWithFrame:CGRectZero];
_backButton.exclusiveTouch = true;
_backButton.titleLabel.font = TGSystemFontOfSize(17);
[_backButton setTitle:_getLocalizedString(@"Common.Back") forState:UIControlStateNormal];
[_backButton setTitleColor:_theme.accentColor];
[_backButton addTarget:self action:@selector(backButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_backButton];
UIImageView *arrowView = [[UIImageView alloc] initWithFrame:CGRectMake(-19, 5.5f, 13, 22)];
arrowView.image = arrowImage;
[_backButton addSubview:arrowView];
_phoneImageView = [[UIImageView alloc] initWithImage:_theme.introImage];
_phoneImageView.frame = CGRectMake(0, 0, 154, 220);
[self.view addSubview:_phoneImageView];
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.font = TGSystemFontOfSize(21);
_titleLabel.textColor = _theme.primaryColor;
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.text = _getLocalizedString(@"ChannelIntro.Title");
[self.view addSubview:_titleLabel];
_descriptionLabel = [[UILabel alloc] init];
_descriptionLabel.backgroundColor = [UIColor clearColor];
_descriptionLabel.numberOfLines = 0;
_descriptionLabel.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:_descriptionLabel];
NSString *description = _getLocalizedString(@"ChannelIntro.Text");
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:description];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineSpacing = 2;
style.alignment = NSTextAlignmentCenter;
[attrString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, description.length)];
[attrString addAttribute:NSForegroundColorAttributeName value:_theme.secondaryColor range:NSMakeRange(0, description.length)];
[attrString addAttribute:NSFontAttributeName value:TGSystemFontOfSize(16) range:NSMakeRange(0, description.length)];
_descriptionLabel.attributedText = attrString;
_createButton = [[TGModernButton alloc] init];
_createButton.exclusiveTouch = true;
_createButton.backgroundColor = [UIColor clearColor];
_createButton.titleLabel.font = TGSystemFontOfSize(21);
[_createButton setTitleColor:_theme.accentColor];
[_createButton setTitle:_getLocalizedString(@"ChannelIntro.CreateChannel") forState:UIControlStateNormal];
[_createButton addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_createButton];
}
- (bool)navigationBarShouldBeHidden
{
return true;
}
- (void)backButtonPressed
{
if (_dismiss != nil)
_dismiss();
else
[self.navigationController popViewControllerAnimated:true];
}
- (void)buttonPressed
{
_completion();
}
- (void)viewWillLayoutSubviews
{
CGRect bounds = self.context.fullscreenBounds;
UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait;
if (bounds.size.width > bounds.size.height) {
orientation = UIInterfaceOrientationLandscapeLeft;
}
UIEdgeInsets safeAreaInset = [self calculatedSafeAreaInset];
int iosVersion = [[[UIDevice currentDevice] systemVersion] intValue];
if (UIEdgeInsetsEqualToEdgeInsets(safeAreaInset, UIEdgeInsetsZero) && (iosVersion < 11 || TGIsPad() || UIInterfaceOrientationIsPortrait(orientation)))
safeAreaInset.top = 20.0f;
[_backButton sizeToFit];
_backButton.frame = CGRectMake(27 + safeAreaInset.left, 5.0f + TGScreenPixel + safeAreaInset.top, ceil(_backButton.frame.size.width), ceil(_backButton.frame.size.height));
[_titleLabel sizeToFit];
[_descriptionLabel sizeToFit];
[_createButton sizeToFit];
int screenSize = (int)TGScreenSize().height;
CGFloat titleY = 0;
CGFloat imageY = 0;
CGFloat descY = 0;
CGFloat buttonY = 0;
if (UIInterfaceOrientationIsPortrait(orientation))
{
switch (screenSize)
{
case 812:
case 896:
titleY = 445 + 44;
imageY = 141 + 44;
descY = 490 + 44;
buttonY = 610 + 44;
break;
case 736:
titleY = 445;
imageY = 141;
descY = 490;
buttonY = 610;
break;
case 667:
titleY = 407;
imageY = 120;
descY = 448;
buttonY = 558;
break;
case 568:
titleY = 354;
imageY = 87;
descY = 397;
buttonY = 496;
break;
default:
titleY = 307;
imageY = 60;
descY = 344;
buttonY = 424;
break;
}
_phoneImageView.frame = CGRectMake((self.view.frame.size.width - _phoneImageView.frame.size.width) / 2, imageY, _phoneImageView.frame.size.width, _phoneImageView.frame.size.height);
_titleLabel.frame = CGRectMake((self.view.frame.size.width - _titleLabel.frame.size.width) / 2, titleY, ceil(_titleLabel.frame.size.width), ceil(_titleLabel.frame.size.height));
_descriptionLabel.frame = CGRectMake((self.view.frame.size.width - _descriptionLabel.frame.size.width) / 2, descY, ceil(_descriptionLabel.frame.size.width), ceil(_descriptionLabel.frame.size.height));
_createButton.frame = CGRectMake((self.view.frame.size.width - _createButton.frame.size.width) / 2, buttonY, ceil(_createButton.frame.size.width), ceil(_createButton.frame.size.height));
}
else
{
CGFloat leftX = 0;
CGFloat rightX = 0;
switch (screenSize)
{
case 812:
leftX = 190 + 44;
rightX = 448 + 44;
titleY = 103;
descY = 148;
buttonY = 237;
break;
case 736:
leftX = 209;
rightX = 504;
titleY = 115;
descY = 156;
buttonY = 278;
break;
case 667:
leftX = 190;
rightX = 448;
titleY = 103;
descY = 148;
buttonY = 237;
break;
case 568:
leftX = 164;
rightX = 388;
titleY = 78;
descY = 121;
buttonY = 217;
break;
default:
leftX = 125;
rightX = 328;
titleY = 78;
descY = 121;
buttonY = 219;
break;
}
_phoneImageView.frame = CGRectMake(leftX - _phoneImageView.frame.size.width / 2, (self.view.frame.size.height - _phoneImageView.frame.size.height) / 2, _phoneImageView.frame.size.width, _phoneImageView.frame.size.height);
_titleLabel.frame = CGRectMake(rightX - _titleLabel.frame.size.width / 2, titleY, ceil(_titleLabel.frame.size.width), ceil(_titleLabel.frame.size.height));
_descriptionLabel.frame = CGRectMake(rightX - _descriptionLabel.frame.size.width / 2, descY, ceil(_descriptionLabel.frame.size.width), ceil(_descriptionLabel.frame.size.height));
_createButton.frame = CGRectMake(rightX - _createButton.frame.size.width / 2, buttonY, ceil(_createButton.frame.size.width), ceil(_createButton.frame.size.height));
}
}
@end

View File

@ -12,7 +12,6 @@ module TelegramUIPrivateModule {
header "../DeviceProximityManager.h" header "../DeviceProximityManager.h"
header "../RaiseToListenActivator.h" header "../RaiseToListenActivator.h"
header "../TGMimeTypeMap.h" header "../TGMimeTypeMap.h"
header "../TGChannelIntroController.h"
header "../Bridge Audio/TGBridgeAudioDecoder.h" header "../Bridge Audio/TGBridgeAudioDecoder.h"
header "../Bridge Audio/TGBridgeAudioEncoder.h" header "../Bridge Audio/TGBridgeAudioEncoder.h"
header "../TGContactModel.h" header "../TGContactModel.h"

View File

@ -407,9 +407,6 @@
D0642EFC1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */; }; D0642EFC1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */; };
D064EF871F69A06F00AC0398 /* MessageContentKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = D064EF861F69A06F00AC0398 /* MessageContentKind.swift */; }; D064EF871F69A06F00AC0398 /* MessageContentKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = D064EF861F69A06F00AC0398 /* MessageContentKind.swift */; };
D0671F2D2145AB28000A8AE7 /* LegacyAvatarPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0671F2C2145AB28000A8AE7 /* LegacyAvatarPicker.swift */; }; D0671F2D2145AB28000A8AE7 /* LegacyAvatarPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0671F2C2145AB28000A8AE7 /* LegacyAvatarPicker.swift */; };
D067B4A5211C911C00796039 /* LegacyChannelIntroController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D067B4A4211C911C00796039 /* LegacyChannelIntroController.swift */; };
D067B4AA211C916300796039 /* TGChannelIntroController.h in Headers */ = {isa = PBXBuildFile; fileRef = D067B4A6211C916200796039 /* TGChannelIntroController.h */; };
D067B4AD211C916300796039 /* TGChannelIntroController.m in Sources */ = {isa = PBXBuildFile; fileRef = D067B4A9211C916200796039 /* TGChannelIntroController.m */; };
D0684A041F6C3AD50059F570 /* ChatListTypingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0684A031F6C3AD50059F570 /* ChatListTypingNode.swift */; }; D0684A041F6C3AD50059F570 /* ChatListTypingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0684A031F6C3AD50059F570 /* ChatListTypingNode.swift */; };
D06887F01F72DEE6000AB936 /* ShareInputFieldNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06887EF1F72DEE6000AB936 /* ShareInputFieldNode.swift */; }; D06887F01F72DEE6000AB936 /* ShareInputFieldNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06887EF1F72DEE6000AB936 /* ShareInputFieldNode.swift */; };
D069F5D0212700B90000565A /* StickerPanePeerSpecificSetupGridItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D069F5CF212700B90000565A /* StickerPanePeerSpecificSetupGridItem.swift */; }; D069F5D0212700B90000565A /* StickerPanePeerSpecificSetupGridItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D069F5CF212700B90000565A /* StickerPanePeerSpecificSetupGridItem.swift */; };
@ -1733,9 +1730,6 @@
D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryNavigationButtons.swift; sourceTree = "<group>"; }; D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryNavigationButtons.swift; sourceTree = "<group>"; };
D064EF861F69A06F00AC0398 /* MessageContentKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageContentKind.swift; sourceTree = "<group>"; }; D064EF861F69A06F00AC0398 /* MessageContentKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageContentKind.swift; sourceTree = "<group>"; };
D0671F2C2145AB28000A8AE7 /* LegacyAvatarPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyAvatarPicker.swift; sourceTree = "<group>"; }; D0671F2C2145AB28000A8AE7 /* LegacyAvatarPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyAvatarPicker.swift; sourceTree = "<group>"; };
D067B4A4211C911C00796039 /* LegacyChannelIntroController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyChannelIntroController.swift; sourceTree = "<group>"; };
D067B4A6211C916200796039 /* TGChannelIntroController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGChannelIntroController.h; sourceTree = "<group>"; };
D067B4A9211C916200796039 /* TGChannelIntroController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGChannelIntroController.m; sourceTree = "<group>"; };
D0684A031F6C3AD50059F570 /* ChatListTypingNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListTypingNode.swift; sourceTree = "<group>"; }; D0684A031F6C3AD50059F570 /* ChatListTypingNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListTypingNode.swift; sourceTree = "<group>"; };
D06879541DB8F1FC00424BBD /* CachedResourceRepresentations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedResourceRepresentations.swift; sourceTree = "<group>"; }; D06879541DB8F1FC00424BBD /* CachedResourceRepresentations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedResourceRepresentations.swift; sourceTree = "<group>"; };
D06879561DB8F22200424BBD /* FetchCachedRepresentations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchCachedRepresentations.swift; sourceTree = "<group>"; }; D06879561DB8F22200424BBD /* FetchCachedRepresentations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchCachedRepresentations.swift; sourceTree = "<group>"; };
@ -3267,16 +3261,6 @@
name = "Setup Two Step Verification"; name = "Setup Two Step Verification";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D067B4AE211C916D00796039 /* Channel Intro */ = {
isa = PBXGroup;
children = (
D067B4A4211C911C00796039 /* LegacyChannelIntroController.swift */,
D067B4A6211C916200796039 /* TGChannelIntroController.h */,
D067B4A9211C916200796039 /* TGChannelIntroController.m */,
);
name = "Channel Intro";
sourceTree = "<group>";
};
D0736F261DF4D2F300F2C02A /* Telegram Controller */ = { D0736F261DF4D2F300F2C02A /* Telegram Controller */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -3336,7 +3320,6 @@
D07551891DDA4C7C0073E051 /* Legacy Components */ = { D07551891DDA4C7C0073E051 /* Legacy Components */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D067B4AE211C916D00796039 /* Channel Intro */,
D0AE2FDB22B1D3610058D3BC /* Bridge Audio */, D0AE2FDB22B1D3610058D3BC /* Bridge Audio */,
D075518A1DDA4D7D0073E051 /* LegacyController.swift */, D075518A1DDA4D7D0073E051 /* LegacyController.swift */,
D075518C1DDA4E0B0073E051 /* LegacyControllerNode.swift */, D075518C1DDA4E0B0073E051 /* LegacyControllerNode.swift */,
@ -4899,7 +4882,6 @@
D0E9BACB1F05738600F079A4 /* STPAPIPostRequest.h in Headers */, D0E9BACB1F05738600F079A4 /* STPAPIPostRequest.h in Headers */,
D0E9BA561F055A0B00F079A4 /* STPFormTextField.h in Headers */, D0E9BA561F055A0B00F079A4 /* STPFormTextField.h in Headers */,
D008177C22B46B7E008A895F /* TGItemProviderSignals.h in Headers */, D008177C22B46B7E008A895F /* TGItemProviderSignals.h in Headers */,
D067B4AA211C916300796039 /* TGChannelIntroController.h in Headers */,
D0E9BABE1F05735F00F079A4 /* STPPaymentConfiguration+Private.h in Headers */, D0E9BABE1F05735F00F079A4 /* STPPaymentConfiguration+Private.h in Headers */,
D0E9BACA1F05738600F079A4 /* STPAPIClient+Private.h in Headers */, D0E9BACA1F05738600F079A4 /* STPAPIClient+Private.h in Headers */,
D0E9BA251F05578900F079A4 /* STPCardBrand.h in Headers */, D0E9BA251F05578900F079A4 /* STPCardBrand.h in Headers */,
@ -5232,7 +5214,6 @@
D0AB263321C3DFEA008F6685 /* CreatePollOptionActionItem.swift in Sources */, D0AB263321C3DFEA008F6685 /* CreatePollOptionActionItem.swift in Sources */,
09FFBCDB22849CB500C33B4B /* PDF.swift in Sources */, 09FFBCDB22849CB500C33B4B /* PDF.swift in Sources */,
09D968A1221F7FF100B1458A /* ChatTypingActivityContentNode.swift in Sources */, 09D968A1221F7FF100B1458A /* ChatTypingActivityContentNode.swift in Sources */,
D067B4AD211C916300796039 /* TGChannelIntroController.m in Sources */,
D0BE303220601FFC00FBE6D8 /* LocationBroadcastActionSheetItem.swift in Sources */, D0BE303220601FFC00FBE6D8 /* LocationBroadcastActionSheetItem.swift in Sources */,
090E778E22AA863A00CD99F5 /* PeersNearbyIconNode.swift in Sources */, 090E778E22AA863A00CD99F5 /* PeersNearbyIconNode.swift in Sources */,
D0EC6CF41EB9F58800EBF1C3 /* ManagedMediaId.swift in Sources */, D0EC6CF41EB9F58800EBF1C3 /* ManagedMediaId.swift in Sources */,
@ -5959,7 +5940,6 @@
091417F421EF4F5F00C8325A /* WallpaperGalleryItem.swift in Sources */, 091417F421EF4F5F00C8325A /* WallpaperGalleryItem.swift in Sources */,
D02F4AE91FCF370B004DFBAE /* ChatMessageInteractiveMediaBadge.swift in Sources */, D02F4AE91FCF370B004DFBAE /* ChatMessageInteractiveMediaBadge.swift in Sources */,
D0EC6E461EB9F58900EBF1C3 /* ItemListLoadingIndicatorEmptyStateItem.swift in Sources */, D0EC6E461EB9F58900EBF1C3 /* ItemListLoadingIndicatorEmptyStateItem.swift in Sources */,
D067B4A5211C911C00796039 /* LegacyChannelIntroController.swift in Sources */,
D00817CF22B47A14008A895F /* LegacyFileImport.swift in Sources */, D00817CF22B47A14008A895F /* LegacyFileImport.swift in Sources */,
D01A21AF1F39EA2E00DDA104 /* InstantPageTheme.swift in Sources */, D01A21AF1F39EA2E00DDA104 /* InstantPageTheme.swift in Sources */,
D0EC6E471EB9F58900EBF1C3 /* ItemListTextEmptyStateItem.swift in Sources */, D0EC6E471EB9F58900EBF1C3 /* ItemListTextEmptyStateItem.swift in Sources */,