Merge commit 'f77f2f0dbf307a45c4f6efebaf844e104850e7a6'

This commit is contained in:
Peter 2019-06-20 19:10:25 +02:00
commit 11bcd1216c
74 changed files with 1469 additions and 1517 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
Telegram-iOS/BlackFilledIconIpad@2x.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

0
Telegram-iOS/BlackIconIpad.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
Telegram-iOS/BlackIconIpad@2x.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

0
Telegram-iOS/BlackIconLargeIpad@2x.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
Telegram-iOS/BlueIconIpad.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
Telegram-iOS/BlueIconIpad@2x.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
Telegram-iOS/BlueIconLargeIpad@2x.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1532,7 +1532,7 @@
"Forward.ChannelReadOnly" = "Sorry, you can't post to this channel."; "Forward.ChannelReadOnly" = "Sorry, you can't post to this channel.";
"Channel.ErrorAccessDenied" = "Sorry, this channel is private."; "Channel.ErrorAccessDenied" = "Sorry, this channel is private.";
"Group.ErrorAccessDenied" = "Sorry, this channel is private."; "Group.ErrorAccessDenied" = "Sorry, this group is private.";
"Conversation.InputTextBroadcastPlaceholder" = "Broadcast"; "Conversation.InputTextBroadcastPlaceholder" = "Broadcast";
"Channel.NotificationLoading" = "Loading..."; "Channel.NotificationLoading" = "Loading...";
@ -4409,7 +4409,7 @@ Any member of this group will be able to see messages in the channel.";
"PeopleNearby.UsersEmpty" = "Looking for users around you..."; "PeopleNearby.UsersEmpty" = "Looking for users around you...";
"PeopleNearby.Groups" = "Groups Nearby"; "PeopleNearby.Groups" = "Groups Nearby";
"PeopleNearby.CreateGroup" = "Create a Group Here"; "PeopleNearby.CreateGroup" = "Create a Group Here";
"PeopleNearby.Channels" = "Channels Nearby"; "PeopleNearby.NoMembers" = "no members";
"Channel.Management.LabelOwner" = "Owner"; "Channel.Management.LabelOwner" = "Owner";
"Channel.Management.LabelAdministrator" = "Administrator"; "Channel.Management.LabelAdministrator" = "Administrator";
@ -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,8 @@ 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.";
"GroupInfo.LabelOwner" = "owner";

View File

@ -158,6 +158,21 @@ public func updateAddressName(account: Account, domain: AddressNameDomain, name:
} |> mapError { _ -> UpdateAddressNameError in return .generic } |> switchToLatest } |> mapError { _ -> UpdateAddressNameError in return .generic } |> switchToLatest
} }
public func checkPublicChannelCreationAvailability(account: Account, location: Bool = false) -> Signal<Bool, NoError> {
var flags: Int32 = (1 << 1)
if location {
flags |= (1 << 0)
}
return account.network.request(Api.functions.channels.getAdminedPublicChannels(flags: flags))
|> map { _ -> Bool in
return true
}
|> `catch` { error -> Signal<Bool, NoError> in
return .single(false)
}
}
public func adminedPublicChannels(account: Account, location: Bool = false) -> Signal<[Peer], NoError> { public func adminedPublicChannels(account: Account, location: Bool = false) -> Signal<[Peer], NoError> {
var flags: Int32 = 0 var flags: Int32 = 0
if location { if location {

View File

@ -40,7 +40,7 @@ private func createChannel(account: Account, title: String, description: String?
return account.network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address), automaticFloodWait: false) return account.network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address), automaticFloodWait: false)
|> mapError { error -> CreateChannelError in |> mapError { error -> CreateChannelError in
if error.errorDescription == "" { if error.errorDescription == "CHANNELS_ADMIN_LOCATED_TOO_MUCH" {
return .tooMuchLocationBasedGroups return .tooMuchLocationBasedGroups
} else if error.errorDescription == "USER_RESTRICTED" { } else if error.errorDescription == "USER_RESTRICTED" {
return .restricted return .restricted

View File

@ -56,7 +56,7 @@ public func checkOwnershipTranfserAvailability(postbox: Postbox, network: Networ
} }
} else if error.errorDescription == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH" { } else if error.errorDescription == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH" {
return .userPublicChannelsTooMuch return .userPublicChannelsTooMuch
} else if error.errorDescription == "CHANNELS_ADMIN_LOCATED_TOO_MUCHs" { } else if error.errorDescription == "CHANNELS_ADMIN_LOCATED_TOO_MUCH" {
return .userLocatedGroupsTooMuch return .userLocatedGroupsTooMuch
} else if error.errorDescription == "ADMINS_TOO_MUCH" { } else if error.errorDescription == "ADMINS_TOO_MUCH" {
return .adminsTooMuch return .adminsTooMuch
@ -85,10 +85,17 @@ public func updateChannelOwnership(account: Account, accountStateManager: Accoun
} }
|> mapToSignal { currentCreator, currentParticipant -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in |> mapToSignal { currentCreator, currentParticipant -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in
return account.postbox.transaction { transaction -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in return account.postbox.transaction { transaction -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in
if let channel = transaction.getPeer(channelId), let inputChannel = apiInputChannel(channel), let accountUser = transaction.getPeer(account.peerId), let user = transaction.getPeer(memberId), let inputUser = apiInputUser(user) { if let channel = transaction.getPeer(channelId) as? TelegramChannel, let inputChannel = apiInputChannel(channel), let accountUser = transaction.getPeer(account.peerId), let user = transaction.getPeer(memberId), let inputUser = apiInputUser(user) {
var flags: TelegramChatAdminRightsFlags
if case .broadcast = channel.info {
flags = TelegramChatAdminRightsFlags.broadcastSpecific
} else {
flags = TelegramChatAdminRightsFlags.groupSpecific
}
let updatedParticipant = ChannelParticipant.creator(id: user.id) let updatedParticipant = ChannelParticipant.creator(id: user.id)
let updatedPreviousCreator = ChannelParticipant.member(id: accountUser.id, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(flags:[]), promotedBy: accountUser.id, canBeEditedByAccountPeer: false), banInfo: nil) let updatedPreviousCreator = ChannelParticipant.member(id: accountUser.id, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(flags: flags), promotedBy: accountUser.id, canBeEditedByAccountPeer: false), banInfo: nil)
let checkPassword = twoStepAuthData(account.network) let checkPassword = twoStepAuthData(account.network)
|> mapError { error -> ChannelOwnershipTransferError in |> mapError { error -> ChannelOwnershipTransferError in
@ -131,6 +138,8 @@ public func updateChannelOwnership(account: Account, accountStateManager: Accoun
} }
} else if error.errorDescription == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH" { } else if error.errorDescription == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH" {
return .userPublicChannelsTooMuch return .userPublicChannelsTooMuch
} else if error.errorDescription == "CHANNELS_ADMIN_LOCATED_TOO_MUCH" {
return .userLocatedGroupsTooMuch
} else if error.errorDescription == "ADMINS_TOO_MUCH" { } else if error.errorDescription == "ADMINS_TOO_MUCH" {
return .adminsTooMuch return .adminsTooMuch
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" { } else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {

View File

@ -31,12 +31,15 @@ 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)) {
self.disposable.set((network.request(Api.functions.contacts.getLocated(geoPoint: .inputGeoPoint(lat: coordinate.latitude, long: coordinate.longitude))) let expiryExtension: Double = 10.0
let poll = 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
return .single(nil) return .single(nil)
} }
|> mapToSignal { updates -> Signal<[PeerNearby], NoError> in |> introduceError(Void.self)
|> mapToSignal { updates -> Signal<[PeerNearby], Void> in
var peersNearby: [PeerNearby] = [] var peersNearby: [PeerNearby] = []
if let updates = updates { if let updates = updates {
switch updates { switch updates {
@ -54,16 +57,31 @@ public final class PeersNearbyContext {
accountStateManager.addUpdates(updates) accountStateManager.addUpdates(updates)
} }
return .single(peersNearby) return .single(peersNearby)
|> then(accountStateManager.updatedPeersNearby()) |> then(
accountStateManager.updatedPeersNearby()
|> introduceError(Void.self)
)
} }
let error: Signal<Void, Void> = .single(Void()) |> then(Signal.fail(Void()) |> suspendAwareDelay(25.0, queue: self.queue))
let combined = combineLatest(poll, error)
|> map { data, _ -> [PeerNearby] in
return data
}
|> restartIfError
|> `catch` { _ -> Signal<[PeerNearby], NoError> in
return .single([])
}
self.disposable.set((combined
|> deliverOn(self.queue)).start(next: { [weak self] updatedEntries in |> deliverOn(self.queue)).start(next: { [weak self] updatedEntries in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
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) + expiryExtension > timestamp } ?? []
let updatedEntries = updatedEntries.filter { Double($0.expires) > timestamp } let updatedEntries = updatedEntries.filter { Double($0.expires) + expiryExtension > timestamp }
var existingPeerIds: [PeerId: Int] = [:] var existingPeerIds: [PeerId: Int] = [:]
for i in 0 ..< entries.count { for i in 0 ..< entries.count {
@ -79,7 +97,6 @@ public final class PeersNearbyContext {
} }
strongSelf.entries = entries strongSelf.entries = entries
for subscriber in strongSelf.subscribers.copyItems() { for subscriber in strongSelf.subscribers.copyItems() {
subscriber(strongSelf.entries) subscriber(strongSelf.entries)
} }
@ -91,7 +108,10 @@ 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) + expiryExtension > timestamp }
for subscriber in strongSelf.subscribers.copyItems() {
subscriber(strongSelf.entries)
}
}, 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

@ -1641,8 +1641,8 @@ final class SharedApplicationContext {
} else if let sendMessageIntent = userActivity.interaction?.intent as? INSendMessageIntent { } else if let sendMessageIntent = userActivity.interaction?.intent as? INSendMessageIntent {
if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") { if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") {
let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2)) let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2))
if let id = Int32(string), let context = self.contextValue { if let id = Int32(string) {
navigateToChatController(navigationController: context.rootController, context: context.context, chatLocation: .peer(PeerId(namespace: Namespaces.Peer.CloudUser, id: id))) self.openChatWhenReady(accountId: nil, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: id), activateInput: true)
} }
} }
} }
@ -1689,7 +1689,7 @@ final class SharedApplicationContext {
}) })
} }
private func openChatWhenReady(accountId: AccountRecordId?, peerId: PeerId, messageId: MessageId? = nil) { private func openChatWhenReady(accountId: AccountRecordId?, peerId: PeerId, messageId: MessageId? = nil, activateInput: Bool = false) {
let signal = self.sharedContextPromise.get() let signal = self.sharedContextPromise.get()
|> take(1) |> take(1)
|> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext, NoError> in |> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext, NoError> in
@ -1707,7 +1707,7 @@ final class SharedApplicationContext {
} }
self.openChatWhenReadyDisposable.set((signal self.openChatWhenReadyDisposable.set((signal
|> deliverOnMainQueue).start(next: { context in |> deliverOnMainQueue).start(next: { context in
context.openChatWithPeerId(peerId: peerId, messageId: messageId) context.openChatWithPeerId(peerId: peerId, messageId: messageId, activateInput: activateInput)
})) }))
} }

View File

@ -58,7 +58,7 @@ final class AuthorizedApplicationContext {
let rootController: TelegramRootController let rootController: TelegramRootController
let notificationController: NotificationContainerController let notificationController: NotificationContainerController
private var scheduledOperChatWithPeerId: PeerId? private var scheduledOperChatWithPeerId: (PeerId, MessageId?, Bool)?
private var scheduledOpenExternalUrl: URL? private var scheduledOpenExternalUrl: URL?
private let passcodeStatusDisposable = MetaDisposable() private let passcodeStatusDisposable = MetaDisposable()
@ -268,9 +268,9 @@ final class AuthorizedApplicationContext {
strongSelf.notificationController.view.isHidden = false strongSelf.notificationController.view.isHidden = false
if strongSelf.rootController.rootTabController == nil { if strongSelf.rootController.rootTabController == nil {
strongSelf.rootController.addRootControllers(showCallsTab: strongSelf.showCallsTab) strongSelf.rootController.addRootControllers(showCallsTab: strongSelf.showCallsTab)
if let peerId = strongSelf.scheduledOperChatWithPeerId { if let (peerId, messageId, activateInput) = strongSelf.scheduledOperChatWithPeerId {
strongSelf.scheduledOperChatWithPeerId = nil strongSelf.scheduledOperChatWithPeerId = nil
strongSelf.openChatWithPeerId(peerId: peerId) strongSelf.openChatWithPeerId(peerId: peerId, messageId: messageId, activateInput: activateInput)
} }
if let url = strongSelf.scheduledOpenExternalUrl { if let url = strongSelf.scheduledOpenExternalUrl {
@ -782,7 +782,7 @@ final class AuthorizedApplicationContext {
self.permissionsDisposable.dispose() self.permissionsDisposable.dispose()
} }
func openChatWithPeerId(peerId: PeerId, messageId: MessageId? = nil) { func openChatWithPeerId(peerId: PeerId, messageId: MessageId? = nil, activateInput: Bool = false) {
var visiblePeerId: PeerId? var visiblePeerId: PeerId?
if let controller = self.rootController.topViewController as? ChatController, case let .peer(peerId) = controller.chatLocation { if let controller = self.rootController.topViewController as? ChatController, case let .peer(peerId) = controller.chatLocation {
visiblePeerId = peerId visiblePeerId = peerId
@ -790,9 +790,9 @@ final class AuthorizedApplicationContext {
if visiblePeerId != peerId || messageId != nil { if visiblePeerId != peerId || messageId != nil {
if self.rootController.rootTabController != nil { if self.rootController.rootTabController != nil {
navigateToChatController(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), messageId: messageId) navigateToChatController(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), messageId: messageId, activateInput: activateInput)
} else { } else {
self.scheduledOperChatWithPeerId = peerId self.scheduledOperChatWithPeerId = (peerId, messageId, activateInput)
} }
} }
} }

View File

@ -294,7 +294,9 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor
} }
switch self.category { switch self.category {
case let .admins(query): case let .admins(query):
if let updated = updated, let _ = updated.participant.adminInfo, (query == nil || updated.peer.indexName.matchesByTokens(query!)) { if let updated = updated, (query == nil || updated.peer.indexName.matchesByTokens(query!)) {
if case let .member(_, _, adminInfo, _) = updated.participant, adminInfo == nil {
} else {
var found = false var found = false
loop: for i in 0 ..< list.count { loop: for i in 0 ..< list.count {
if list[i].peer.id == updated.peer.id { if list[i].peer.id == updated.peer.id {
@ -308,6 +310,7 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor
list.insert(updated, at: 0) list.insert(updated, at: 0)
updatedList = true updatedList = true
} }
}
} else if let previous = previous, let _ = previous.adminInfo { } else if let previous = previous, let _ = previous.adminInfo {
loop: for i in 0 ..< list.count { loop: for i in 0 ..< list.count {
if list[i].peer.id == previous.peerId { if list[i].peer.id == previous.peerId {

View File

@ -1193,8 +1193,16 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
title = isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_TypeSetup_Title title = isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_TypeSetup_Title
} }
} }
let entries = channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, state: state)
var focusItemTag: ItemListItemTag?
if entries.count > 1, let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {
focusItemTag = ChannelVisibilityEntryTag.publicLink
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(entries: channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, state: state), style: .blocks, crossfadeState: crossfade, animateChanges: false) let listState = ItemListNodeState(entries: entries, style: .blocks, focusItemTag: focusItemTag, crossfadeState: crossfade, animateChanges: false)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} |> afterDisposed { } |> afterDisposed {

View File

@ -136,7 +136,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
} else { } else {
text = presentationInterfaceState.strings.Group_ErrorAccessDenied text = presentationInterfaceState.strings.Group_ErrorAccessDenied
} }
strongSelf.interfaceInteraction?.presentController(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationInterfaceState.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationInterfaceState.strings.Common_OK, action: {})]), nil) strongSelf.interfaceInteraction?.presentController(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationInterfaceState.strings.Common_OK, action: {})]), nil)
})) }))
case .kicked: case .kicked:
break break

View File

@ -223,6 +223,10 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
private var screenCaptureEventsDisposable: Disposable? private var screenCaptureEventsDisposable: Disposable?
private let chatAdditionalDataDisposable = MetaDisposable() private let chatAdditionalDataDisposable = MetaDisposable()
private var reportIrrelvantGeoNoticePromise = Promise<Bool?>()
private var reportIrrelvantGeoNotice: Bool?
private var reportIrrelvantGeoDisposable: Disposable?
private var volumeButtonsListener: VolumeButtonsListener? private var volumeButtonsListener: VolumeButtonsListener?
private var beginMediaRecordingRequestId: Int = 0 private var beginMediaRecordingRequestId: Int = 0
@ -1449,17 +1453,32 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
} }
onlineMemberCount = recentOnlineSignal onlineMemberCount = recentOnlineSignal
|> map(Optional.init) |> map(Optional.init)
self.reportIrrelvantGeoNoticePromise.set(context.account.postbox.transaction { transaction -> Bool? in
if let _ = transaction.getNoticeEntry(key: ApplicationSpecificNotice.irrelevantPeerGeoReportKey(peerId: peerId)) as? ApplicationSpecificBoolNotice {
return true
} else {
return false
} }
self.peerDisposable.set((combineLatest(queue: Queue.mainQueue(), peerView.get(), onlineMemberCount) })
|> deliverOnMainQueue).start(next: { [weak self] peerView, onlineMemberCount in } else {
self.reportIrrelvantGeoNoticePromise.set(.single(nil))
}
self.peerDisposable.set((combineLatest(queue: Queue.mainQueue(), peerView.get(), onlineMemberCount, self.reportIrrelvantGeoNoticePromise.get())
|> deliverOnMainQueue).start(next: { [weak self] peerView, onlineMemberCount, peerReportNotice in
if let strongSelf = self { if let strongSelf = self {
if let peer = peerViewMainPeer(peerView) { if let peer = peerViewMainPeer(peerView) {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, onlineMemberCount: onlineMemberCount) strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, onlineMemberCount: onlineMemberCount)
(strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.setPeer(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, peer: peer, overrideImage: peer.isDeleted ? .deletedIcon : .none) (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.setPeer(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, peer: peer, overrideImage: peer.isDeleted ? .deletedIcon : .none)
} }
if strongSelf.peerView === peerView {
if strongSelf.peerView === peerView && strongSelf.reportIrrelvantGeoNotice == peerReportNotice {
return return
} }
strongSelf.reportIrrelvantGeoNotice = peerReportNotice
var upgradedToPeerId: PeerId? var upgradedToPeerId: PeerId?
if let previous = strongSelf.peerView, let group = previous.peers[previous.peerId] as? TelegramGroup, group.migrationReference == nil, let updatedGroup = peerView.peers[peerView.peerId] as? TelegramGroup, let migrationReference = updatedGroup.migrationReference { if let previous = strongSelf.peerView, let group = previous.peers[previous.peerId] as? TelegramGroup, group.migrationReference == nil, let updatedGroup = peerView.peers[peerView.peerId] as? TelegramGroup, let migrationReference = updatedGroup.migrationReference {
upgradedToPeerId = migrationReference.peerId upgradedToPeerId = migrationReference.peerId
@ -1516,11 +1535,18 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
var contactStatus: ChatContactStatus? var contactStatus: ChatContactStatus?
if let peer = peerView.peers[peerView.peerId] { if let peer = peerView.peers[peerView.peerId] {
if let cachedData = peerView.cachedData as? CachedUserData { if let cachedData = peerView.cachedData as? CachedUserData {
contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, peerStatusSettings: cachedData.peerStatusSettings) contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings)
} else if let cachedData = peerView.cachedData as? CachedGroupData { } else if let cachedData = peerView.cachedData as? CachedGroupData {
contactStatus = ChatContactStatus(canAddContact: false, peerStatusSettings: cachedData.peerStatusSettings) contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings)
} else if let cachedData = peerView.cachedData as? CachedChannelData { } else if let cachedData = peerView.cachedData as? CachedChannelData {
contactStatus = ChatContactStatus(canAddContact: false, peerStatusSettings: cachedData.peerStatusSettings) var canReportIrrelevantLocation = true
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member {
canReportIrrelevantLocation = false
}
if let peerReportNotice = peerReportNotice, peerReportNotice {
canReportIrrelevantLocation = false
}
contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings)
} }
var peers = SimpleDictionary<PeerId, Peer>() var peers = SimpleDictionary<PeerId, Peer>()
@ -1581,7 +1607,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
didDisplayActionsPanel = true didDisplayActionsPanel = true
} else if peerStatusSettings.contains(.canShareContact) { } else if peerStatusSettings.contains(.canShareContact) {
didDisplayActionsPanel = true didDisplayActionsPanel = true
} else if peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) {
didDisplayActionsPanel = true didDisplayActionsPanel = true
} }
} }
@ -1596,7 +1622,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
displayActionsPanel = true displayActionsPanel = true
} else if peerStatusSettings.contains(.canShareContact) { } else if peerStatusSettings.contains(.canShareContact) {
displayActionsPanel = true displayActionsPanel = true
} else if peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) {
displayActionsPanel = true displayActionsPanel = true
} }
} }
@ -1932,6 +1958,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
self.shareStatusDisposable?.dispose() self.shareStatusDisposable?.dispose()
self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeTarget(self) self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeTarget(self)
self.preloadHistoryPeerIdDisposable.dispose() self.preloadHistoryPeerIdDisposable.dispose()
self.reportIrrelvantGeoDisposable?.dispose()
} }
public func updatePresentationMode(_ mode: ChatControllerPresentationMode) { public func updatePresentationMode(_ mode: ChatControllerPresentationMode) {
@ -3445,15 +3472,28 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInputMode({ _ in return .none }) }) strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInputMode({ _ in return .none }) })
} }
}, reportPeerIrrelevantGeoLocation: { [weak self] in }, reportPeerIrrelevantGeoLocation: { [weak self] in
if let strongSelf = self { guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else {
return
}
strongSelf.chatDisplayNode.dismissInput() strongSelf.chatDisplayNode.dismissInput()
let actions = [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { let actions = [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.ReportGroupLocation_Report, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.ReportGroupLocation_Report, action: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.reportIrrelvantGeoDisposable = (TelegramCore.reportPeer(account: strongSelf.context.account, peerId: peerId, reason: .irrelevantLocation)
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
strongSelf.reportIrrelvantGeoNoticePromise.set(.single(true))
let _ = ApplicationSpecificNotice.setIrrelevantPeerGeoReport(postbox: strongSelf.context.account.postbox, peerId: peerId).start()
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction(type: TextAlertActionType.defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
})
})] })]
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ReportGroupLocation_Title, text: strongSelf.presentationData.strings.ReportGroupLocation_Text, actions: actions), in: .window(.root)) strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ReportGroupLocation_Title, text: strongSelf.presentationData.strings.ReportGroupLocation_Text, actions: actions), in: .window(.root))
}
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get())) }, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get()))
switch self.chatLocation { switch self.chatLocation {
@ -6729,7 +6769,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
return state.updatedInterfaceState { interfaceState in return state.updatedInterfaceState { interfaceState in
return interfaceState.withUpdatedEffectiveInputState(interfaceState.effectiveInputState) return interfaceState.withUpdatedEffectiveInputState(interfaceState.effectiveInputState)
}.updatedInputMode({ _ in ChatInputMode.text }) }.updatedInputMode({ _ in .text })
}) })
} }
}), }),
@ -6740,7 +6780,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
return state.updatedInterfaceState { interfaceState in return state.updatedInterfaceState { interfaceState in
let effectiveInputState = ChatTextInputState(inputText: NSAttributedString(string: "/")) let effectiveInputState = ChatTextInputState(inputText: NSAttributedString(string: "/"))
return interfaceState.withUpdatedEffectiveInputState(effectiveInputState) return interfaceState.withUpdatedEffectiveInputState(effectiveInputState)
}.updatedInputMode({ _ in ChatInputMode.text }) }.updatedInputMode({ _ in .text })
} else { } else {
return state return state
} }
@ -6754,7 +6794,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
return state.updatedInterfaceState { interfaceState in return state.updatedInterfaceState { interfaceState in
let effectiveInputState = ChatTextInputState(inputText: NSAttributedString(string: "@")) let effectiveInputState = ChatTextInputState(inputText: NSAttributedString(string: "@"))
return interfaceState.withUpdatedEffectiveInputState(effectiveInputState) return interfaceState.withUpdatedEffectiveInputState(effectiveInputState)
}.updatedInputMode({ _ in ChatInputMode.text }) }.updatedInputMode({ _ in .text })
} else { } else {
return state return state
} }
@ -6768,7 +6808,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
return state.updatedInterfaceState { interfaceState in return state.updatedInterfaceState { interfaceState in
let effectiveInputState = ChatTextInputState(inputText: NSAttributedString(string: "#")) let effectiveInputState = ChatTextInputState(inputText: NSAttributedString(string: "#"))
return interfaceState.withUpdatedEffectiveInputState(effectiveInputState) return interfaceState.withUpdatedEffectiveInputState(effectiveInputState)
}.updatedInputMode({ _ in ChatInputMode.text }) }.updatedInputMode({ _ in .text })
} else { } else {
return state return state
} }
@ -6846,6 +6886,12 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
} }
} }
func activateInput() {
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
return state.updatedInputMode({ _ in .text })
})
}
private func clearInputText() { private func clearInputText() {
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
if !state.interfaceState.effectiveInputState.inputText.string.isEmpty { if !state.interfaceState.effectiveInputState.inputText.string.isEmpty {

View File

@ -83,7 +83,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
case .installed: case .installed:
scope = [.installed] scope = [.installed]
} }
return searchStickers(account: context.account, query: query, scope: scope) return searchStickers(account: context.account, query: query.trimmedEmoji, scope: scope)
} }
|> map { stickers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in |> map { stickers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
return { _ in return { _ in

View File

@ -37,7 +37,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
displayActionsPanel = true displayActionsPanel = true
} else if peerStatusSettings.contains(.canShareContact) { } else if peerStatusSettings.contains(.canShareContact) {
displayActionsPanel = true displayActionsPanel = true
} else if peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) {
displayActionsPanel = true displayActionsPanel = true
} }
} }

View File

@ -322,6 +322,7 @@ final class ChatRecordedMediaPreview: Equatable {
struct ChatContactStatus: Equatable { struct ChatContactStatus: Equatable {
var canAddContact: Bool var canAddContact: Bool
var canReportIrrelevantLocation: Bool
var peerStatusSettings: PeerStatusSettings? var peerStatusSettings: PeerStatusSettings?
var isEmpty: Bool { var isEmpty: Bool {
@ -331,6 +332,9 @@ struct ChatContactStatus: Equatable {
if !self.canAddContact { if !self.canAddContact {
peerStatusSettings.remove(.canAddContact) peerStatusSettings.remove(.canAddContact)
} }
if !self.canReportIrrelevantLocation {
peerStatusSettings.remove(.canReportIrrelevantGeoLocation)
}
return peerStatusSettings.isEmpty return peerStatusSettings.isEmpty
} }
} }

View File

@ -772,7 +772,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
strongSelf.openPeer(peerId: peerId, peer: nil) strongSelf.openPeer(peerId: peerId, peer: nil)
} }
case .inaccessiblePeer: case .inaccessiblePeer:
strongSelf.controllerInteraction.presentController(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) strongSelf.controllerInteraction.presentController(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil)
case .botStart: case .botStart:
break break
//strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)), fromMessage: nil) //strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)), fromMessage: nil)

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

@ -54,7 +54,7 @@ private func peerButtons(_ state: ChatPresentationInterfaceState) -> [ChatReport
} }
} }
} else if let _ = state.renderedPeer?.chatMainPeer { } else if let _ = state.renderedPeer?.chatMainPeer {
if let contactStatus = state.contactStatus, let peerStatusSettings = contactStatus.peerStatusSettings, peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { if let contactStatus = state.contactStatus, contactStatus.canReportIrrelevantLocation, let peerStatusSettings = contactStatus.peerStatusSettings, peerStatusSettings.contains(.canReportIrrelevantGeoLocation) {
buttons.append(.reportIrrelevantGeoLocation) buttons.append(.reportIrrelevantGeoLocation)
} else { } else {
buttons.append(.reportSpam) buttons.append(.reportSpam)

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,10 +503,10 @@ 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)
} }
} }
}
let result = NSMutableAttributedString(attributedString: state.inputText) let result = NSMutableAttributedString(attributedString: state.inputText)
for attribute in attributesToRemove { for attribute in attributesToRemove {
@ -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

@ -443,7 +443,7 @@ public func createGroupController(context: AccountContext, peerIds: [PeerId], in
case .tooMuchLocationBasedGroups: case .tooMuchLocationBasedGroups:
text = presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch text = presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch
} }
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
})) }))
} }
}, changeProfilePhoto: { }, changeProfilePhoto: {

View File

@ -135,4 +135,12 @@ extension String {
} }
return string return string
} }
var trimmedEmoji: String {
if self.unicodeScalars.count > 1, self.unicodeScalars.first?.value == 0x2764 {
return String(self.unicodeScalars.prefix(self.unicodeScalars.count - 1))
} else {
return self
}
}
} }

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

@ -94,6 +94,7 @@ private enum GroupInfoEntryTag {
private enum GroupInfoMemberStatus { private enum GroupInfoMemberStatus {
case member case member
case admin case admin
case owner
} }
private enum GroupEntryStableId: Hashable, Equatable { private enum GroupEntryStableId: Hashable, Equatable {
@ -529,6 +530,8 @@ private enum GroupInfoEntry: ItemListNodeEntry {
case let .member(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, peer, participant, presence, memberStatus, editing, actions, enabled, selectable): case let .member(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, peer, participant, presence, memberStatus, editing, actions, enabled, selectable):
let label: String? let label: String?
switch memberStatus { switch memberStatus {
case .owner:
label = strings.GroupInfo_LabelOwner
case .admin: case .admin:
label = strings.GroupInfo_LabelAdmin label = strings.GroupInfo_LabelAdmin
case .member: case .member:
@ -831,7 +834,9 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
if isCreator || (channel.adminRights != nil && channel.hasPermission(.pinMessages)) { if isCreator || (channel.adminRights != nil && channel.hasPermission(.pinMessages)) {
if cachedChannelData.peerGeoLocation != nil { if cachedChannelData.peerGeoLocation != nil {
if isCreator {
entries.append(GroupInfoEntry.groupTypeSetup(presentationData.theme, presentationData.strings.GroupInfo_PublicLink, channel.addressName ?? presentationData.strings.GroupInfo_PublicLinkAdd)) entries.append(GroupInfoEntry.groupTypeSetup(presentationData.theme, presentationData.strings.GroupInfo_PublicLink, channel.addressName ?? presentationData.strings.GroupInfo_PublicLinkAdd))
}
} else { } else {
if cachedChannelData.flags.contains(.canChangeUsername) { if cachedChannelData.flags.contains(.canChangeUsername) {
entries.append(GroupInfoEntry.groupTypeSetup(presentationData.theme, presentationData.strings.GroupInfo_GroupType, isPublic ? presentationData.strings.Channel_Setup_TypePublic : presentationData.strings.Channel_Setup_TypePrivate)) entries.append(GroupInfoEntry.groupTypeSetup(presentationData.theme, presentationData.strings.GroupInfo_GroupType, isPublic ? presentationData.strings.Channel_Setup_TypePublic : presentationData.strings.Channel_Setup_TypePrivate))
@ -1128,7 +1133,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
let memberStatus: GroupInfoMemberStatus let memberStatus: GroupInfoMemberStatus
switch participant.participant { switch participant.participant {
case .creator: case .creator:
memberStatus = .admin memberStatus = .owner
case let .member(_, _, adminInfo, _): case let .member(_, _, adminInfo, _):
if adminInfo != nil { if adminInfo != nil {
memberStatus = .admin memberStatus = .admin
@ -1563,7 +1568,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
} }
} else if let channel = groupPeer as? TelegramChannel { } else if let channel = groupPeer as? TelegramChannel {
if channel.hasPermission(.inviteMembers) { if channel.hasPermission(.inviteMembers) {
if channel.flags.contains(.isCreator) || channel.adminRights != nil { if channel.flags.contains(.isCreator) || (channel.adminRights != nil && channel.username == nil) {
canCreateInviteLink = true canCreateInviteLink = true
} }
} }
@ -2027,7 +2032,9 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
|> mapToSignal { address -> Signal<Bool, NoError> in |> mapToSignal { address -> Signal<Bool, NoError> in
return updateChannelGeoLocation(postbox: context.account.postbox, network: context.account.network, channelId: peer.id, coordinate: (coordinate.latitude, coordinate.longitude), address: address) return updateChannelGeoLocation(postbox: context.account.postbox, network: context.account.network, channelId: peer.id, coordinate: (coordinate.latitude, coordinate.longitude), address: address)
} }
|> deliverOnMainQueue).start() |> deliverOnMainQueue).start(error: { errror in
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), nil)
})
}, sendLiveLocation: { _, _ in }, theme: presentationData.theme, customLocationPicker: true, presentationCompleted: { }, sendLiveLocation: { _, _ in }, theme: presentationData.theme, customLocationPicker: true, presentationCompleted: {
clearHighlightImpl?() clearHighlightImpl?()
}) })

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 {
@ -251,6 +252,7 @@ class ItemListAddressItemNode: ListViewItemNode {
if let icon = strongSelf.iconNode.image { if let icon = strongSelf.iconNode.image {
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: imageFrame.minX + floorToScreenPixels((imageFrame.width - icon.size.width) / 2.0), y: imageFrame.minY + floorToScreenPixels((imageFrame.height - icon.size.height) / 2.0) - 7.0), size: icon.size) strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: imageFrame.minX + floorToScreenPixels((imageFrame.width - icon.size.width) / 2.0), y: imageFrame.minY + floorToScreenPixels((imageFrame.height - icon.size.height) / 2.0) - 7.0), size: icon.size)
strongSelf.iconNode.isHidden = imageSize.height < 50.0
} }
let leftInset: CGFloat let leftInset: CGFloat

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, removeOnCompletion: false)
}
} else if let activityIndicator = strongSelf.activityIndicator {
activityIndicator.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, 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

@ -10,7 +10,7 @@ public enum NavigateToChatKeepStack {
case never case never
} }
public func navigateToChatController(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) { public func navigateToChatController(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) {
var found = false var found = false
var isFirst = true var isFirst = true
for controller in navigationController.viewControllers.reversed() { for controller in navigationController.viewControllers.reversed() {
@ -35,6 +35,9 @@ public func navigateToChatController(navigationController: NavigationController,
completion() completion()
} }
controller.purposefulAction = purposefulAction controller.purposefulAction = purposefulAction
if activateInput {
controller.activateInput()
}
found = true found = true
break break
} }
@ -80,6 +83,9 @@ public func navigateToChatController(navigationController: NavigationController,
navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: animated, completion: completion) navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: animated, completion: completion)
} }
} }
if activateInput {
controller.activateInput()
}
} }
navigationController.currentWindow?.forEachController { controller in navigationController.currentWindow?.forEachController { controller in

View File

@ -141,11 +141,16 @@ private struct ApplicationSpecificNoticeKeys {
private static let botPaymentLiabilityNamespace: Int32 = 1 private static let botPaymentLiabilityNamespace: Int32 = 1
private static let globalNamespace: Int32 = 2 private static let globalNamespace: Int32 = 2
private static let permissionsNamespace: Int32 = 3 private static let permissionsNamespace: Int32 = 3
private static let peerReportNamespace: Int32 = 4
static func botPaymentLiabilityNotice(peerId: PeerId) -> NoticeEntryKey { static func botPaymentLiabilityNotice(peerId: PeerId) -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: botPaymentLiabilityNamespace), key: noticeKey(peerId: peerId, key: 0)) return NoticeEntryKey(namespace: noticeNamespace(namespace: botPaymentLiabilityNamespace), key: noticeKey(peerId: peerId, key: 0))
} }
static func irrelevantPeerGeoNotice(peerId: PeerId) -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: peerReportNamespace), key: noticeKey(peerId: peerId, key: 0))
}
static func secretChatInlineBotUsage() -> NoticeEntryKey { static func secretChatInlineBotUsage() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.secretChatInlineBotUsage.key) return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.secretChatInlineBotUsage.key)
} }
@ -200,6 +205,16 @@ private struct ApplicationSpecificNoticeKeys {
} }
public struct ApplicationSpecificNotice { public struct ApplicationSpecificNotice {
static func irrelevantPeerGeoReportKey(peerId: PeerId) -> NoticeEntryKey {
return ApplicationSpecificNoticeKeys.irrelevantPeerGeoNotice(peerId: peerId)
}
static func setIrrelevantPeerGeoReport(postbox: Postbox, peerId: PeerId) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Void in
transaction.setNoticeEntry(key: ApplicationSpecificNoticeKeys.irrelevantPeerGeoNotice(peerId: peerId), value: ApplicationSpecificBoolNotice())
}
}
static func getBotPaymentLiability(accountManager: AccountManager, peerId: PeerId) -> Signal<Bool, NoError> { static func getBotPaymentLiability(accountManager: AccountManager, peerId: PeerId) -> Signal<Bool, NoError> {
return accountManager.transaction { transaction -> Bool in return accountManager.transaction { transaction -> Bool in
if let _ = transaction.getNotice(ApplicationSpecificNoticeKeys.botPaymentLiabilityNotice(peerId: peerId)) as? ApplicationSpecificBoolNotice { if let _ = transaction.getNotice(ApplicationSpecificNoticeKeys.botPaymentLiabilityNotice(peerId: peerId)) as? ApplicationSpecificBoolNotice {

View File

@ -253,9 +253,7 @@ final class PeerChannelMemberCategoriesContextsManager {
strongSelf.impl.with { impl in strongSelf.impl.with { impl in
for (contextPeerId, context) in impl.contexts { for (contextPeerId, context) in impl.contexts {
if peerId == contextPeerId { if peerId == contextPeerId {
for (previous, updated) in results { context.replayUpdates(results.map { ($0.0, $0.1, nil) })
context.replayUpdates([(previous, updated, nil)])
}
} }
} }
} }

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 {
@ -200,7 +200,7 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
case let .group(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer): case let .group(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
var text: ItemListPeerItemText var text: ItemListPeerItemText
if let cachedData = peer.peer.1 as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount { if let cachedData = peer.peer.1 as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount {
text = .text("\(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), \(strings.Conversation_StatusMembers(memberCount))") text = .text("\(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), \(memberCount > 0 ? strings.Conversation_StatusMembers(memberCount) : strings.PeopleNearby_NoMembers)")
} else { } else {
text = .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0) text = .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0)
} }
@ -245,11 +245,11 @@ private struct PeersNearbyData: Equatable {
} }
} }
private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationData: PresentationData) -> [PeersNearbyEntry] { private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationData: PresentationData, displayLoading: Bool) -> [PeersNearbyEntry] {
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(), displayLoading && 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(), displayLoading && 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
@ -271,7 +271,6 @@ private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationDa
} }
if let data = data, !data.channels.isEmpty { if let data = data, !data.channels.isEmpty {
entries.append(.channelsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Channels.uppercased()))
var i: Int32 = 0 var i: Int32 = 0
for channel in data.channels { for channel in data.channels {
entries.append(.channel(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, channel)) entries.append(.channel(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, channel))
@ -290,6 +289,8 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
var navigateToChatImpl: ((Peer) -> Void)? var navigateToChatImpl: ((Peer) -> Void)?
let actionsDisposable = DisposableSet() let actionsDisposable = DisposableSet()
let checkCreationAvailabilityDisposable = MetaDisposable()
actionsDisposable.add(checkCreationAvailabilityDisposable)
let dataPromise = Promise<PeersNearbyData?>(nil) let dataPromise = Promise<PeersNearbyData?>(nil)
let addressPromise = Promise<String?>(nil) let addressPromise = Promise<String?>(nil)
@ -298,40 +299,66 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
navigateToChatImpl?(peer) navigateToChatImpl?(peer)
}, openCreateGroup: { latitude, longitude, address in }, openCreateGroup: { latitude, longitude, address in
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
cancelImpl?()
}))
presentControllerImpl?(controller, nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.5, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
cancelImpl = {
checkCreationAvailabilityDisposable.set(nil)
}
checkCreationAvailabilityDisposable.set((checkPublicChannelCreationAvailability(account: context.account, location: true)
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
|> deliverOnMainQueue).start(next: { available in
if available {
let controller = PermissionController(context: context, splashScreen: true) let controller = PermissionController(context: context, splashScreen: true)
controller.setState(.custom(icon: PermissionControllerCustomIcon(light: UIImage(bundleImageName: "Location/LocalGroupLightIcon"), dark: UIImage(bundleImageName: "Location/LocalGroupDarkIcon")), title: presentationData.strings.LocalGroup_Title, subtitle: address, text: presentationData.strings.LocalGroup_Text, buttonTitle: presentationData.strings.LocalGroup_ButtonTitle, footerText: presentationData.strings.LocalGroup_IrrelevantWarning), animated: false) controller.setState(.custom(icon: PermissionControllerCustomIcon(light: UIImage(bundleImageName: "Location/LocalGroupLightIcon"), dark: UIImage(bundleImageName: "Location/LocalGroupDarkIcon")), title: presentationData.strings.LocalGroup_Title, subtitle: address, text: presentationData.strings.LocalGroup_Text, buttonTitle: presentationData.strings.LocalGroup_ButtonTitle, footerText: presentationData.strings.LocalGroup_IrrelevantWarning), animated: false)
controller.proceed = { result in controller.proceed = { result in
replaceTopControllerImpl?(createGroupController(context: context, peerIds: [], mode: .locatedGroup(latitude: latitude, longitude: longitude, address: address))) replaceTopControllerImpl?(createGroupController(context: context, peerIds: [], mode: .locatedGroup(latitude: latitude, longitude: longitude, address: address)))
} }
pushControllerImpl?(controller) pushControllerImpl?(controller)
} else {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
}))
}) })
let dataSignal: Signal<PeersNearbyData?, Void> = currentLocationManagerCoordinate(manager: context.sharedContext.locationManager!, timeout: 5.0) let dataSignal: Signal<PeersNearbyData?, NoError> = currentLocationManagerCoordinate(manager: context.sharedContext.locationManager!, timeout: 5.0)
|> introduceError(Void.self) |> mapToSignal { coordinate -> Signal<PeersNearbyData?, NoError> in
|> mapToSignal { coordinate -> Signal<PeersNearbyData?, Void> in
guard let coordinate = coordinate else { guard let coordinate = coordinate else {
return .single(nil) return .single(nil)
} }
print("TTTTT: \(CFAbsoluteTimeGetCurrent())")
return Signal { subscriber in return Signal { subscriber in
let peersNearbyContext = PeersNearbyContext(network: context.account.network, accountStateManager: context.account.stateManager, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude)) let peersNearbyContext = PeersNearbyContext(network: context.account.network, accountStateManager: context.account.stateManager, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude))
let peersNearby: Signal<PeersNearbyData?, Void> = combineLatest(peersNearbyContext.get(), addressPromise.get()) let peersNearby: Signal<PeersNearbyData?, NoError> = combineLatest(peersNearbyContext.get(), addressPromise.get())
|> introduceError(Void.self) |> mapToSignal { peersNearby, address -> Signal<([PeerNearby]?, String?), NoError> in
|> mapToSignal { peersNearby, address -> Signal<([PeerNearby]?, String?), Void> in
if let address = address { if let address = address {
return .single((peersNearby, address)) return .single((peersNearby, address))
} else { } else {
return reverseGeocodeLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) return reverseGeocodeLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
|> introduceError(Void.self)
|> map { placemark in |> map { placemark in
return (peersNearby, placemark?.fullAddress) return (peersNearby, placemark?.fullAddress)
} }
} }
} }
|> mapToSignal { peersNearby, address -> Signal<PeersNearbyData?, Void> in |> mapToSignal { peersNearby, address -> Signal<PeersNearbyData?, NoError> in
guard let peersNearby = peersNearby else { guard let peersNearby = peersNearby else {
return .single(nil) return .single(nil)
} }
@ -350,7 +377,6 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
} }
return PeersNearbyData(latitude: coordinate.latitude, longitude: coordinate.longitude, address: address, users: users, groups: groups, channels: []) return PeersNearbyData(latitude: coordinate.latitude, longitude: coordinate.longitude, address: address, users: users, groups: groups, channels: [])
} }
|> introduceError(Void.self)
} }
let disposable = peersNearby.start(next: { data in let disposable = peersNearby.start(next: { data in
@ -363,25 +389,18 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
} }
} }
} }
dataPromise.set(dataSignal)
let errorSignal: Signal<Void, Void> = .single(Void()) |> then( Signal.fail(Void()) |> suspendAwareDelay(25.0, queue: Queue.concurrentDefaultQueue()) )
let combinedSignal = combineLatest(dataSignal, errorSignal) |> map { data, _ -> PeersNearbyData? in
return data
}
|> restartIfError
|> `catch` { _ -> Signal<PeersNearbyData?, NoError> in
return .single(nil)
} |> filter { value in
return value != nil
}
dataPromise.set(.single(nil) |> then(combinedSignal))
let previousData = Atomic<PeersNearbyData?>(value: nil) let previousData = Atomic<PeersNearbyData?>(value: nil)
let displayLoading: Signal<Bool, NoError> = .single(false)
|> then(
.single(true)
|> delay(1.0, queue: Queue.mainQueue())
)
let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get()) let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get(), displayLoading)
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { presentationData, data -> (ItemListControllerState, (ItemListNodeState<PeersNearbyEntry>, PeersNearbyEntry.ItemGenerationArguments)) in |> map { presentationData, data, displayLoading -> (ItemListControllerState, (ItemListNodeState<PeersNearbyEntry>, PeersNearbyEntry.ItemGenerationArguments)) in
let previous = previousData.swap(data) let previous = previousData.swap(data)
var crossfade = false var crossfade = false
@ -393,7 +412,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
} }
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.PeopleNearby_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.PeopleNearby_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(entries: peersNearbyControllerEntries(data: data, presentationData: presentationData), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: !crossfade, userInteractionEnabled: true) let listState = ItemListNodeState(entries: peersNearbyControllerEntries(data: data, presentationData: presentationData, displayLoading: displayLoading), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: !crossfade, userInteractionEnabled: true)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }

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,19 +31,25 @@ 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
var buttonAction: (() -> Void)? var buttonAction: (() -> Void)?
var openPrivacyPolicy: (() -> Void)? var openPrivacyPolicy: (() -> Void)?
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)?) { var validLayout: (CGSize, UIEdgeInsets)?
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.iconNode = ASImageNode() self.iconNode = ASImageNode()
self.iconNode.isLayerBacked = true self.iconNode.isLayerBacked = true
@ -71,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)
@ -107,11 +127,39 @@ final class PermissionContentNode: ASDisplayNode {
self.privacyPolicyButton.addTarget(self, action: #selector(self.privacyPolicyPressed), forControlEvents: .touchUpInside) self.privacyPolicyButton.addTarget(self, action: #selector(self.privacyPolicyPressed), forControlEvents: .touchUpInside)
} }
func updatePresentationData(_ presentationData: PresentationData) {
let theme = presentationData.theme
self.theme = theme
self.iconNode.image = self.icon.imageForTheme(theme)
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: ""])
self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text.replacingOccurrences(of: "]", with: "]()"), attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center)
if let subtitle = self.subtitleNode.attributedText?.string {
self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center)
}
if let footerText = self.footerNode.attributedText?.string {
self.footerNode.attributedText = NSAttributedString(string: footerText, font: Font.regular(13.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center)
}
if let privacyPolicyTitle = self.privacyPolicyButton.attributedTitle(for: .normal)?.string {
self.privacyPolicyButton.setTitle(privacyPolicyTitle, with: Font.regular(16.0), with: theme.list.itemAccentColor, for: .normal)
}
if let validLayout = self.validLayout {
self.updateLayout(size: validLayout.0, insets: validLayout.1, transition: .immediate)
}
}
@objc func privacyPolicyPressed() { @objc func privacyPolicyPressed() {
self.openPrivacyPolicy?() self.openPrivacyPolicy?()
} }
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) { func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, insets)
let sidePadding: CGFloat let sidePadding: CGFloat
let fontSize: CGFloat let fontSize: CGFloat
if min(size.width, size.height) > 330.0 { if min(size.width, size.height) > 330.0 {
@ -159,7 +207,12 @@ final class PermissionContentNode: ASDisplayNode {
let privacySpacing: CGFloat = max(30.0 + privacyButtonSize.height, (availableHeight - titleTextSpacing - buttonSpacing - imageSize.height - imageSpacing) / 2.0) let privacySpacing: CGFloat = max(30.0 + privacyButtonSize.height, (availableHeight - titleTextSpacing - buttonSpacing - imageSize.height - imageSpacing) / 2.0)
let contentOrigin = insets.top + floor((size.height - insets.top - insets.bottom - contentHeight) / 2.0) - availableHeight * 0.05 var verticalOffset: CGFloat = 0.0
if size.height >= 568.0 {
verticalOffset = availableHeight * 0.05
}
let contentOrigin = insets.top + floor((size.height - insets.top - insets.bottom - contentHeight) / 2.0) - verticalOffset
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: contentOrigin), size: imageSize) let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: contentOrigin), size: imageSize)
let nearbyIconFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: contentOrigin), size: imageSize) let nearbyIconFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: contentOrigin), size: imageSize)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: iconFrame.maxY + imageSpacing), size: titleSize) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: iconFrame.maxY + imageSpacing), size: titleSize)
@ -188,5 +241,7 @@ final class PermissionContentNode: ASDisplayNode {
transition.updateFrame(node: self.actionButton, frame: buttonFrame) transition.updateFrame(node: self.actionButton, frame: buttonFrame)
transition.updateFrame(node: self.footerNode, frame: footerFrame) transition.updateFrame(node: self.footerNode, frame: footerFrame)
transition.updateFrame(node: self.privacyPolicyButton, frame: privacyButtonFrame) transition.updateFrame(node: self.privacyPolicyButton, frame: privacyButtonFrame)
self.footerNode.isHidden = size.height < 568.0
} }
} }

View File

@ -84,17 +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) {
@ -207,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)
@ -237,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

@ -68,6 +68,14 @@ final class SolidRoundedButtonNode: ASDisplayNode {
} }
} }
func updateTheme(_ theme: PresentationTheme) {
self.theme = theme
self.buttonBackgroundNode.image = generateStretchableFilledCircleImage(radius: cornerRadius, color: theme.list.itemCheckColors.fillColor)
self.buttonGlossNode.color = theme.list.itemCheckColors.foregroundColor
self.labelNode.attributedText = NSAttributedString(string: self.title ?? "", font: Font.medium(17.0), textColor: theme.list.itemCheckColors.foregroundColor)
}
func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
self.validLayout = width self.validLayout = width

View File

@ -267,7 +267,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
let query = text.trimmingCharacters(in: .whitespacesAndNewlines) let query = text.trimmingCharacters(in: .whitespacesAndNewlines)
if query.isSingleEmoji { if query.isSingleEmoji {
signals = .single([searchStickers(account: account, query: text) signals = .single([searchStickers(account: account, query: text.trimmedEmoji)
|> take(1) |> take(1)
|> map { (nil, $0) }]) |> map { (nil, $0) }])
} else if query.count > 1, let languageCode = languageCode, !languageCode.isEmpty && languageCode != "emoji" { } else if query.count > 1, let languageCode = languageCode, !languageCode.isEmpty && languageCode != "emoji" {
@ -290,7 +290,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = [] var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
let emoticons = keywords.flatMap { $0.emoticons } let emoticons = keywords.flatMap { $0.emoticons }
for emoji in emoticons { for emoji in emoticons {
signals.append(searchStickers(account: self.context.account, query: emoji) signals.append(searchStickers(account: self.context.account, query: emoji.trimmedEmoji)
|> take(1) |> take(1)
|> map { (emoji, $0) }) |> map { (emoji, $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

@ -190,16 +190,19 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
self.scrollNode.view.showsHorizontalScrollIndicator = false self.scrollNode.view.showsHorizontalScrollIndicator = false
} }
private func scrollToNode(_ node: ThemeSettingsAppIconNode, animated: Bool) {
let bounds = self.scrollNode.view.bounds
let frame = node.frame.insetBy(dx: -48.0, dy: 0.0)
if frame.minX < bounds.minX || frame.maxX > bounds.maxX {
self.scrollNode.view.scrollRectToVisible(frame, animated: animated)
}
}
func asyncLayout() -> (_ item: ThemeSettingsAppIconItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { func asyncLayout() -> (_ item: ThemeSettingsAppIconItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentItem = self.item let currentItem = self.item
return { item, params, neighbors in return { item, params, neighbors in
var themeUpdated = false
if currentItem?.theme !== item.theme {
themeUpdated = true
}
let contentSize: CGSize let contentSize: CGSize
let insets: UIEdgeInsets let insets: UIEdgeInsets
let separatorHeight = UIScreenPixel let separatorHeight = UIScreenPixel
@ -255,6 +258,9 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
let nodeSize = CGSize(width: 80.0, height: 112.0) let nodeSize = CGSize(width: 80.0, height: 112.0)
var nodeOffset = nodeInset var nodeOffset = nodeInset
var updated = false
var selectedNode: ThemeSettingsAppIconNode?
var i = 0 var i = 0
for icon in item.icons { for icon in item.icons {
let imageNode: ThemeSettingsAppIconNode let imageNode: ThemeSettingsAppIconNode
@ -264,10 +270,14 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
imageNode = ThemeSettingsAppIconNode() imageNode = ThemeSettingsAppIconNode()
strongSelf.nodes.append(imageNode) strongSelf.nodes.append(imageNode)
strongSelf.scrollNode.addSubnode(imageNode) strongSelf.scrollNode.addSubnode(imageNode)
updated = true
} }
if let image = UIImage(named: icon.imageName, in: Bundle.main, compatibleWith: nil) { if let image = UIImage(named: icon.imageName, in: Bundle.main, compatibleWith: nil) {
let selected = icon.name == item.currentIconName let selected = icon.name == item.currentIconName
if selected {
selectedNode = imageNode
}
var name = "Icon" var name = "Icon"
var bordered = true var bordered = true
@ -292,8 +302,11 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
break break
} }
imageNode.setup(theme: item.theme, icon: image, title: NSAttributedString(string: name, font: textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: bordered, selected: selected, action: { imageNode.setup(theme: item.theme, icon: image, title: NSAttributedString(string: name, font: textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: bordered, selected: selected, action: { [weak self, weak imageNode] in
item.updated(icon.name) item.updated(icon.name)
if let imageNode = imageNode {
self?.scrollToNode(imageNode, animated: true)
}
}) })
} }
@ -309,6 +322,10 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.scrollNode.view.contentSize = contentSize strongSelf.scrollNode.view.contentSize = contentSize
} }
} }
if updated, let selectedNode = selectedNode {
strongSelf.scrollToNode(selectedNode, animated: false)
}
} }
}) })
} }

View File

@ -240,15 +240,19 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
self.scrollNode.view.showsHorizontalScrollIndicator = false self.scrollNode.view.showsHorizontalScrollIndicator = false
} }
private func scrollToNode(_ node: ThemeSettingsThemeItemIconNode, animated: Bool) {
let bounds = self.scrollNode.view.bounds
let frame = node.frame.insetBy(dx: -48.0, dy: 0.0)
if frame.minX < bounds.minX || frame.maxX > bounds.maxX {
self.scrollNode.view.scrollRectToVisible(frame, animated: animated)
}
}
func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentItem = self.item let currentItem = self.item
return { item, params, neighbors in return { item, params, neighbors in
var themeUpdated = false
if currentItem?.theme !== item.theme {
themeUpdated = true
}
let contentSize: CGSize let contentSize: CGSize
let insets: UIEdgeInsets let insets: UIEdgeInsets
let separatorHeight = UIScreenPixel let separatorHeight = UIScreenPixel
@ -304,6 +308,9 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
let nodeSize = CGSize(width: 116.0, height: 112.0) let nodeSize = CGSize(width: 116.0, height: 112.0)
var nodeOffset = nodeInset var nodeOffset = nodeInset
var updated = false
var selectedNode: ThemeSettingsThemeItemIconNode?
var i = 0 var i = 0
for (theme, accentColor) in item.themes { for (theme, accentColor) in item.themes {
let imageNode: ThemeSettingsThemeItemIconNode let imageNode: ThemeSettingsThemeItemIconNode
@ -313,9 +320,14 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
imageNode = ThemeSettingsThemeItemIconNode() imageNode = ThemeSettingsThemeItemIconNode()
strongSelf.nodes.append(imageNode) strongSelf.nodes.append(imageNode)
strongSelf.scrollNode.addSubnode(imageNode) strongSelf.scrollNode.addSubnode(imageNode)
updated = true
} }
let selected = theme == item.currentTheme let selected = theme == item.currentTheme
if selected {
selectedNode = imageNode
}
let name: String let name: String
switch theme { switch theme {
case .dayClassic: case .dayClassic:
@ -328,8 +340,11 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
name = item.strings.Appearance_ThemeCarouselNightBlue name = item.strings.Appearance_ThemeCarouselNightBlue
} }
imageNode.setup(theme: item.theme, icon: generateThemeIconImage(theme: theme, accentColor: accentColor), title: NSAttributedString(string: name, font: textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { imageNode.setup(theme: item.theme, icon: generateThemeIconImage(theme: theme, accentColor: accentColor), title: NSAttributedString(string: name, font: textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { [weak self, weak imageNode] in
item.updated(theme) item.updated(theme)
if let imageNode = imageNode {
self?.scrollToNode(imageNode, animated: true)
}
}) })
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize) imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize)
@ -344,6 +359,10 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.scrollNode.view.contentSize = contentSize strongSelf.scrollNode.view.contentSize = contentSize
} }
} }
if updated, let selectedNode = selectedNode {
strongSelf.scrollToNode(selectedNode, animated: false)
}
} }
}) })
} }

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 */,

View File

@ -6,7 +6,7 @@ public struct CallListSettings: PreferencesEntry, Equatable {
public var showTab: Bool public var showTab: Bool
public static var defaultSettings: CallListSettings { public static var defaultSettings: CallListSettings {
return CallListSettings(showTab: false) return CallListSettings(showTab: true)
} }
public init(showTab: Bool) { public init(showTab: Bool) {