Merge commit '77b1547f847a072abe533f3008389d38921dcd47'

This commit is contained in:
Peter 2019-06-11 18:35:14 +01:00
commit fa3ac0b61a
33 changed files with 3421 additions and 3243 deletions

View File

@ -388,6 +388,14 @@ private func canEditAdminRights(accountPeerId: PeerId, channelView: PeerView, in
}
}
private func areAllAdminRightsEnabled(_ flags: TelegramChatAdminRightsFlags, group: Bool) -> Bool {
if group {
return TelegramChatAdminRightsFlags.groupSpecific.intersection(flags) == TelegramChatAdminRightsFlags.groupSpecific
} else {
return TelegramChatAdminRightsFlags.broadcastSpecific.intersection(flags) == TelegramChatAdminRightsFlags.broadcastSpecific
}
}
private func channelAdminControllerEntries(presentationData: PresentationData, state: ChannelAdminControllerState, accountPeerId: PeerId, channelView: PeerView, adminView: PeerView, initialParticipant: ChannelParticipant?) -> [ChannelAdminEntry] {
var entries: [ChannelAdminEntry] = []
@ -456,7 +464,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
entries.append(.addAdminsInfo(presentationData.theme, currentRightsFlags.contains(.canAddAdmins) ? presentationData.strings.Channel_EditAdmin_PermissinAddAdminOn : presentationData.strings.Channel_EditAdmin_PermissinAddAdminOff))
}
if channel.flags.contains(.isCreator) && currentRightsFlags.contains(.canAddAdmins) {
if let admin = admin as? TelegramUser, admin.botInfo == nil && channel.flags.contains(.isCreator) && areAllAdminRightsEnabled(currentRightsFlags, group: isGroup) {
entries.append(.transfer(presentationData.theme, isGroup ? presentationData.strings.Group_EditAdmin_TransferOwnership : presentationData.strings.Channel_EditAdmin_TransferOwnership))
}
@ -526,7 +534,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
entries.append(.addAdminsInfo(presentationData.theme, currentRightsFlags.contains(.canAddAdmins) ? presentationData.strings.Channel_EditAdmin_PermissinAddAdminOn : presentationData.strings.Channel_EditAdmin_PermissinAddAdminOff))
}
if group.role == .creator && currentRightsFlags.contains(.canAddAdmins) {
if let admin = admin as? TelegramUser, admin.botInfo == nil && group.role == .creator && areAllAdminRightsEnabled(currentRightsFlags, group: true) {
entries.append(.transfer(presentationData.theme, presentationData.strings.Group_EditAdmin_TransferOwnership))
}
@ -573,10 +581,6 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
return current.withUpdatedUpdatedFlags(updated)
}
}, transferOwnership: {
updateState { current in
return current.withUpdatedUpdating(true)
}
let _ = (context.account.postbox.transaction { transaction -> (peer: Peer?, member: Peer?) in
return (peer: transaction.getPeer(peerId), member: transaction.getPeer(adminId))
} |> deliverOnMainQueue).start(next: { peer, member in
@ -602,10 +606,6 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
}
transferOwnershipDisposable.set((signal |> deliverOnMainQueue).start(error: { error in
updateState { current in
return current.withUpdatedUpdating(false)
}
let currentPeerId = actualPeerId.with { $0 }
let channel: Signal<Peer?, NoError>
if currentPeerId == peerId {
@ -769,8 +769,14 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
updateState { current in
return current.withUpdatedUpdating(true)
}
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(flags: updateFlags)) |> deliverOnMainQueue).start(error: { _ in
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(flags: updateFlags)) |> deliverOnMainQueue).start(error: { error in
if case let .addMemberError(error) = error, case .restricted = error, let admin = adminView.peers[adminView.peerId] {
var text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
if case .group = channel.info {
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
}
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
}, completed: {
updated(TelegramChatAdminRights(flags: updateFlags))
dismissImpl?()
@ -803,15 +809,15 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
} else if updateFlags != defaultFlags {
let signal = convertGroupToSupergroup(account: context.account, peerId: peerId)
|> map(Optional.init)
|> `catch` { _ -> Signal<PeerId?, NoError> in
return .single(nil)
|> `catch` { _ -> Signal<PeerId?, UpdateChannelAdminRightsError> in
return .fail(.generic)
}
|> mapToSignal { upgradedPeerId -> Signal<PeerId?, NoError> in
|> mapToSignal { upgradedPeerId -> Signal<PeerId?, UpdateChannelAdminRightsError> in
guard let upgradedPeerId = upgradedPeerId else {
return .single(nil)
return .fail(.generic)
}
return context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: upgradedPeerId, memberId: adminId, adminRights: TelegramChatAdminRights(flags: updateFlags))
|> mapToSignal { _ -> Signal<PeerId?, NoError> in
|> mapToSignal { _ -> Signal<PeerId?, UpdateChannelAdminRightsError> in
return .complete()
}
|> then(.single(upgradedPeerId))
@ -827,7 +833,11 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
dismissImpl?()
})
}
}, error: { _ in
}, error: { error in
if case let .addMemberError(error) = error, case .restricted = error, let admin = adminView.peers[adminView.peerId] {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(admin.compactDisplayTitle, admin.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
updateState { current in
return current.withUpdatedUpdating(false)
}

View File

@ -365,6 +365,9 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
if peerId.namespace == Namespaces.Peer.CloudChannel {
if case .searchAdmins = mode {
return context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: memberId, adminRights: TelegramChatAdminRights(flags: []))
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}
|> afterDisposed {
Queue.mainQueue().async {
updateState { state in

View File

@ -11,6 +11,10 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI
private let backgroundNode: ASImageNode
private let textInputNode: TextFieldNode
private let placeholderNode: ASTextNode
private var clearOnce: Bool = false
private let inputActivityNode: ActivityIndicator
private var isChecking = false
var complete: (() -> Void)?
var textChanged: ((String) -> Void)?
@ -50,11 +54,16 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI
self.placeholderNode.displaysAsynchronously = false
self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(14.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
self.inputActivityNode = ActivityIndicator(type: .custom(theme.list.itemAccentColor, 18.0, 1.5, false))
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.textInputNode)
self.addSubnode(self.placeholderNode)
self.addSubnode(self.inputActivityNode)
self.inputActivityNode.isHidden = true
}
override func didLoad() {
@ -82,6 +91,15 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(14.0), textColor: theme.actionSheet.inputPlaceholderColor)
}
func updateIsChecking(_ isChecking: Bool) {
self.isChecking = isChecking
self.inputActivityNode.isHidden = !isChecking
}
func updateIsInvalid() {
self.clearOnce = true
}
func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
let backgroundInsets = self.backgroundInsets
let inputInsets = self.inputInsets
@ -97,6 +115,9 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI
transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right, height: backgroundFrame.size.height)))
let activitySize = CGSize(width: 18.0, height: 18.0)
transition.updateFrame(node: self.inputActivityNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX - activitySize.width - 6.0, y: backgroundFrame.minY + floor((backgroundFrame.height - activitySize.height) / 2.0)), size: activitySize))
return panelHeight
}
@ -120,10 +141,23 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if self.isChecking {
return false
}
if string == "\n" {
self.complete?()
return false
}
if self.clearOnce {
self.clearOnce = false
if range.length > string.count {
textField.text = ""
return false
}
}
return true
}
}
@ -221,6 +255,10 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
return self.inputFieldNode.password
}
func updateIsChecking(_ checking: Bool) {
self.inputFieldNode.updateIsChecking(checking)
}
override func updateTheme(_ theme: AlertControllerTheme) {
self.titleNode.attributedText = NSAttributedString(string: self.strings.Channel_OwnershipTransfer_EnterPassword, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.textNode.attributedText = NSAttributedString(string: self.strings.Channel_OwnershipTransfer_EnterPasswordText, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center)
@ -352,6 +390,7 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
}
func animateError() {
self.inputFieldNode.updateIsInvalid()
self.inputFieldNode.layer.addShakeAnimation()
self.hapticFeedback.error()
}
@ -388,7 +427,9 @@ private func commitChannelOwnershipTransferController(context: AccountContext, c
guard let contentNode = contentNode else {
return
}
contentNode.updateIsChecking(true)
disposable.set((updateChannelOwnership(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, channelId: channel.id, memberId: member.id, password: contentNode.password) |> deliverOnMainQueue).start(error: { [weak contentNode] error in
contentNode?.updateIsChecking(false)
contentNode?.animateError()
}, completed: {
dismissImpl?()
@ -438,6 +479,10 @@ func channelOwnershipTransferController(context: AccountContext, channel: Telegr
var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.medium(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
var text = presentationData.strings.OwnershipTransfer_SecurityRequirements
var isGroup = false
if case .group = channel.info {
isGroup = true
}
var dismissImpl: (() -> Void)?
var actions: [TextAlertAction] = []
@ -447,9 +492,7 @@ func channelOwnershipTransferController(context: AccountContext, channel: Telegr
return confirmChannelOwnershipTransferController(context: context, channel: channel, member: member, present: present, completion: completion)
case .twoStepAuthTooFresh, .authSessionTooFresh:
text = text + presentationData.strings.OwnershipTransfer_ComeBackLater
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
dismissImpl?()
})]
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
case .twoStepAuthMissing:
actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: {
let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in
@ -458,15 +501,23 @@ func channelOwnershipTransferController(context: AccountContext, channel: Telegr
}
})
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
dismissImpl?()
})]
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})]
case .adminsTooMuch:
title = nil
text = isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorAdminsTooMuch : presentationData.strings.Channel_OwnershipTransfer_ErrorAdminsTooMuch
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
case .userPublicChannelsTooMuch:
title = nil
text = presentationData.strings.Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
case .userBlocked, .restricted:
title = nil
text = isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted : presentationData.strings.Channel_OwnershipTransfer_ErrorPrivacyRestricted
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
default:
title = nil
text = presentationData.strings.Login_UnknownError
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
dismissImpl?()
})]
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
}
let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor)

View File

@ -1078,7 +1078,6 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
return state.withUpdatedUpdatingAddressName(false)
}
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}))
}

View File

@ -7,6 +7,7 @@ import AsyncDisplayKit
import TelegramCore
import SafariServices
import MobileCoreServices
import Intents
import LegacyComponents
public enum ChatControllerPeekActions {
@ -83,7 +84,7 @@ private func isTopmostChatController(_ controller: ChatController) -> Bool {
let ChatControllerCount = Atomic<Int32>(value: 0)
public final class ChatController: TelegramController, KeyShortcutResponder, GalleryHiddenMediaTarget, UIDropInteractionDelegate {
public final class ChatController: TelegramController, GalleryHiddenMediaTarget, UIDropInteractionDelegate {
private var validLayout: ContainerViewLayout?
weak var parentController: ViewController?
@ -2272,6 +2273,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
}
})
strongSelf.donateIntent()
}
}
@ -4896,6 +4899,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|> deliverOnMainQueue).start(next: { [weak self] _ in
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
})
self.donateIntent()
}
}
@ -6613,7 +6618,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
}
}
public var keyShortcuts: [KeyShortcut] {
public override var keyShortcuts: [KeyShortcut] {
let strings = self.presentationData.strings
var inputShortcuts: [KeyShortcut]
@ -6790,4 +6795,29 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
}
})
}
private func donateIntent() {
guard case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser else {
return
}
if #available(iOSApplicationExtension 10.0, *) {
let _ = (self.context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
if let peer = peer as? TelegramUser {
let recipientHandle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown)
let recipient = INPerson(personHandle: recipientHandle, nameComponents: nil, displayName: peer.displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)")
let intent = INSendMessageIntent(recipients: [recipient], content: nil, groupName: nil, serviceName: nil, sender: nil)
let interaction = INInteraction(intent: intent, response: nil)
interaction.direction = .outgoing
interaction.donate { error in
if let error = error {
print(error.localizedDescription)
}
}
}
})
}
}
}

View File

@ -1129,6 +1129,21 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
return nil
}
public func firstMessageForEditInCurrentHistoryView() -> Message? {
if let historyView = self.historyView {
if historyView.originalView.laterId == nil {
for entry in historyView.filteredEntries.reversed() {
if case let .MessageEntry(message, _, _, _, _, _) = entry {
if canEditMessage(context: context, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, message: message) {
return message
}
}
}
}
}
return nil
}
public func messageInCurrentHistoryView(_ id: MessageId) -> Message? {
if let historyView = self.historyView {
for entry in historyView.filteredEntries {

View File

@ -17,6 +17,81 @@ private struct MessageContextMenuData {
let messageActions: ChatAvailableMessageActions
}
func canEditMessage(context: AccountContext, limitsConfiguration: LimitsConfiguration, message: Message) -> Bool {
var hasEditRights = false
var unlimitedInterval = false
if message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.id.namespace != Namespaces.Message.Cloud {
hasEditRights = false
} else if let author = message.author, author.id == context.account.peerId {
hasEditRights = true
} else if message.author?.id == message.id.peerId, let peer = message.peers[message.id.peerId] {
if let peer = peer as? TelegramChannel {
switch peer.info {
case .broadcast:
if peer.hasPermission(.editAllMessages) {
hasEditRights = true
}
case .group:
if peer.hasPermission(.pinMessages) {
unlimitedInterval = true
hasEditRights = true
}
}
}
}
var hasUneditableAttributes = false
if let peer = message.peers[message.id.peerId] as? TelegramChannel {
if !peer.hasPermission(.sendMessages) {
//hasUneditableAttributes = true
}
}
if hasEditRights {
for attribute in message.attributes {
if let _ = attribute as? InlineBotMessageAttribute {
hasUneditableAttributes = true
break
}
}
if message.forwardInfo != nil {
hasUneditableAttributes = true
}
for media in message.media {
if let file = media as? TelegramMediaFile {
if file.isSticker || file.isInstantVideo {
hasUneditableAttributes = true
break
}
} else if let _ = media as? TelegramMediaContact {
hasUneditableAttributes = true
break
} else if let _ = media as? TelegramMediaExpiredContent {
hasUneditableAttributes = true
break
} else if let _ = media as? TelegramMediaMap {
hasUneditableAttributes = true
break
} else if let _ = media as? TelegramMediaPoll {
hasUneditableAttributes = true
break
}
}
if !hasUneditableAttributes {
if canPerformEditingActions(limits: limitsConfiguration, accountPeerId: context.account.peerId, message: message, unlimitedInterval: unlimitedInterval) {
return true
}
}
}
return false
}
private let starIconEmpty = UIImage(bundleImageName: "Chat/Context Menu/StarIconEmpty")?.precomposed()
private let starIconFilled = UIImage(bundleImageName: "Chat/Context Menu/StarIconFilled")?.precomposed()
@ -262,77 +337,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
dataSignal = combineLatest(loadLimits, loadStickerSaveStatusSignal, loadResourceStatusSignal, chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: Set(messages.map { $0.id })))
|> map { limitsConfiguration, stickerSaveStatus, resourceStatus, messageActions -> MessageContextMenuData in
var canEdit = false
if messages[0].id.namespace == Namespaces.Message.Cloud && !isAction {
if !isAction {
let message = messages[0]
var hasEditRights = false
var unlimitedInterval = false
if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
hasEditRights = false
} else if let author = message.author, author.id == context.account.peerId {
hasEditRights = true
} else if message.author?.id == message.id.peerId, let peer = message.peers[message.id.peerId] {
if let peer = peer as? TelegramChannel {
switch peer.info {
case .broadcast:
if peer.hasPermission(.editAllMessages) {
hasEditRights = true
}
case .group:
if peer.hasPermission(.pinMessages) {
unlimitedInterval = true
hasEditRights = true
}
}
}
}
var hasUneditableAttributes = false
if let peer = message.peers[message.id.peerId] as? TelegramChannel {
if !peer.hasPermission(.sendMessages) {
//hasUneditableAttributes = true
}
}
if hasEditRights {
for attribute in message.attributes {
if let _ = attribute as? InlineBotMessageAttribute {
hasUneditableAttributes = true
break
}
}
if message.forwardInfo != nil {
hasUneditableAttributes = true
}
for media in message.media {
if let file = media as? TelegramMediaFile {
if file.isSticker || file.isInstantVideo {
hasUneditableAttributes = true
break
}
} else if let _ = media as? TelegramMediaContact {
hasUneditableAttributes = true
break
} else if let _ = media as? TelegramMediaExpiredContent {
hasUneditableAttributes = true
break
} else if let _ = media as? TelegramMediaMap {
hasUneditableAttributes = true
break
} else if let _ = media as? TelegramMediaPoll {
hasUneditableAttributes = true
break
}
}
if !hasUneditableAttributes {
if canPerformEditingActions(limits: limitsConfiguration, accountPeerId: context.account.peerId, message: message, unlimitedInterval: unlimitedInterval) {
canEdit = true
}
}
}
canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
}
return MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions)
@ -618,7 +625,7 @@ struct ChatAvailableMessageActions {
let banAuthor: Peer?
}
private func canPerformEditingActions(limits: LimitsConfiguration, accountPeerId: PeerId, message: Message, unlimitedInterval: Bool) -> Bool {
func canPerformEditingActions(limits: LimitsConfiguration, accountPeerId: PeerId, message: Message, unlimitedInterval: Bool) -> Bool {
if message.id.peerId == accountPeerId {
return true
}

View File

@ -53,7 +53,7 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
return false
}
public class ChatListController: TelegramController, KeyShortcutResponder, UIViewControllerPreviewingDelegate {
public class ChatListController: TelegramController, UIViewControllerPreviewingDelegate {
private var validLayout: ContainerViewLayout?
let context: AccountContext
@ -683,7 +683,10 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass {
scrollToEndIfExists = true
}
navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: scrollToEndIfExists, animated: animated, completion: { [weak self] in
let animated: Bool = !scrollToEndIfExists || strongSelf.groupId != PeerGroupId.root
navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: animated, animated: animated, parentGroupId: strongSelf.groupId, completion: { [weak self] in
self?.chatListDisplayNode.chatListNode.clearHighlightAnimated(true)
})
}
@ -1269,7 +1272,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
}
}
public var keyShortcuts: [KeyShortcut] {
public override var keyShortcuts: [KeyShortcut] {
let strings = self.presentationData.strings
let toggleSearch: () -> Void = { [weak self] in

View File

@ -493,7 +493,7 @@ final class ChatListNode: ListView {
let currentRemovingPeerId = self.currentRemovingPeerId
let savedMessagesPeer: Signal<Peer?, NoError>
if case let .peers(filter) = mode, filter == [.onlyWriteable] {
if case let .peers(filter) = mode, filter.contains(.onlyWriteable) {
savedMessagesPeer = context.account.postbox.loadedPeerWithId(context.account.peerId)
|> map(Optional.init)
} else {

View File

@ -338,7 +338,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
var textCutout = TextNodeCutout()
var initialWidth: CGFloat = CGFloat.greatestFiniteMagnitude
var refineContentImageLayout: ((CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> ChatMessageInteractiveMediaNode)))?
var refineContentFileLayout: ((CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode)))?
var refineContentFileLayout: ((CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> ChatMessageInteractiveFileNode)))?
var contentInstantVideoSizeAndApply: (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> ChatMessageInteractiveInstantVideoNode)?
@ -604,7 +604,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
boundingSize.width = max(boundingSize.width, refinedWidth)
}
var finalizeContentFileLayout: ((CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode))?
var finalizeContentFileLayout: ((CGFloat) -> (CGSize, (Bool) -> ChatMessageInteractiveFileNode))?
if let refineContentFileLayout = refineContentFileLayout {
let (refinedWidth, finalizeFileLayout) = refineContentFileLayout(textConstrainedSize)
finalizeContentFileLayout = finalizeFileLayout
@ -689,7 +689,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
}
}
var contentFileSizeAndApply: (CGSize, () -> ChatMessageInteractiveFileNode)?
var contentFileSizeAndApply: (CGSize, (Bool) -> ChatMessageInteractiveFileNode)?
if let finalizeContentFileLayout = finalizeContentFileLayout {
let (size, apply) = finalizeContentFileLayout(boundingWidth - insets.left - insets.right)
contentFileSizeAndApply = (size, apply)
@ -824,7 +824,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
if let (contentFileSize, contentFileApply) = contentFileSizeAndApply {
contentMediaHeight = contentFileSize.height
let contentFileNode = contentFileApply()
let contentFileNode = contentFileApply(synchronousLoads)
if strongSelf.contentFileNode !== contentFileNode {
strongSelf.contentFileNode = contentFileNode
strongSelf.addSubnode(contentFileNode)

View File

@ -39,11 +39,11 @@ final class ChatMessageAvatarAccessoryItem: ListViewAccessoryItem {
return false
}
func node() -> ListViewAccessoryItemNode {
func node(synchronous: Bool) -> ListViewAccessoryItemNode {
let node = ChatMessageAvatarAccessoryItemNode()
node.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0))
if let peer = self.peer {
node.setPeer(account: self.context.account, theme: self.context.sharedContext.currentPresentationData.with({ $0 }).theme, peer: peer, authorOfMessage: self.messageReference, emptyColor: self.emptyColor)
node.setPeer(account: self.context.account, theme: self.context.sharedContext.currentPresentationData.with({ $0 }).theme, synchronousLoad: synchronous, peer: peer, authorOfMessage: self.messageReference, emptyColor: self.emptyColor)
}
return node
}
@ -65,7 +65,7 @@ final class ChatMessageAvatarAccessoryItemNode: ListViewAccessoryItemNode {
self.addSubnode(self.avatarNode)
}
func setPeer(account: Account, theme: PresentationTheme, peer: Peer, authorOfMessage: MessageReference?, emptyColor: UIColor) {
self.avatarNode.setPeer(account: account, theme: theme, peer: peer, authorOfMessage: authorOfMessage, emptyColor: emptyColor)
func setPeer(account: Account, theme: PresentationTheme, synchronousLoad:Bool, peer: Peer, authorOfMessage: MessageReference?, emptyColor: UIColor) {
self.avatarNode.setPeer(account: account, theme: theme, peer: peer, authorOfMessage: authorOfMessage, emptyColor: emptyColor, synchronousLoad: synchronousLoad)
}
}

View File

@ -243,7 +243,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
}
}
return (layoutSize, { [weak self] animation, _ in
return (layoutSize, { [weak self] animation, synchronousLoads in
if let strongSelf = self {
strongSelf.item = item
strongSelf.contact = selectedContact
@ -290,7 +290,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
}
if let peerId = selectedContact?.peerId, let peer = item.message.peers[peerId] {
strongSelf.avatarNode.setPeer(account: item.context.account, theme: item.presentationData.theme.theme, peer: peer, emptyColor: avatarPlaceholderColor)
strongSelf.avatarNode.setPeer(account: item.context.account, theme: item.presentationData.theme.theme, peer: peer, emptyColor: avatarPlaceholderColor, synchronousLoad: synchronousLoads)
} else {
strongSelf.avatarNode.setCustomLetters(customLetters)
}

View File

@ -79,13 +79,13 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
return (refinedWidth + layoutConstants.file.bubbleInsets.left + layoutConstants.file.bubbleInsets.right, { boundingWidth in
let (fileSize, fileApply) = finishLayout(boundingWidth - layoutConstants.file.bubbleInsets.left - layoutConstants.file.bubbleInsets.right)
return (CGSize(width: fileSize.width + layoutConstants.file.bubbleInsets.left + layoutConstants.file.bubbleInsets.right, height: fileSize.height + layoutConstants.file.bubbleInsets.top + layoutConstants.file.bubbleInsets.bottom), { [weak self] _, _ in
return (CGSize(width: fileSize.width + layoutConstants.file.bubbleInsets.left + layoutConstants.file.bubbleInsets.right, height: fileSize.height + layoutConstants.file.bubbleInsets.top + layoutConstants.file.bubbleInsets.bottom), { [weak self] _, synchronousLoads in
if let strongSelf = self {
strongSelf.item = item
strongSelf.interactiveFileNode.frame = CGRect(origin: CGPoint(x: layoutConstants.file.bubbleInsets.left, y: layoutConstants.file.bubbleInsets.top), size: fileSize)
fileApply()
fileApply(synchronousLoads)
}
})
})

View File

@ -45,7 +45,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
} else {
self.stopTimer()
}
self.updateStatus()
self.updateStatus(animated: true)
}
}
}
@ -182,7 +182,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
}
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> Void))) {
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void))) {
let currentFile = self.file
let titleAsyncLayout = TextNode.asyncLayout(self.titleNode)
@ -493,7 +493,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
streamingCacheStatusFrame = CGRect()
}
return (fittedLayoutSize, { [weak self] in
return (fittedLayoutSize, { [weak self] synchronousLoads in
if let strongSelf = self {
strongSelf.context = context
strongSelf.themeAndStrings = (presentationData.theme, presentationData.strings, presentationData.dateTimeFormat.decimalSeparator)
@ -593,7 +593,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if let strongSelf = strongSelf {
strongSelf.resourceStatus = status
strongSelf.actualFetchStatus = actualFetchStatus
strongSelf.updateStatus()
strongSelf.updateStatus(animated: !synchronousLoads)
}
}
}))
@ -624,7 +624,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
}
strongSelf.updateStatus()
strongSelf.updateStatus(animated: !synchronousLoads)
}
})
})
@ -632,7 +632,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
}
private func updateStatus() {
private func updateStatus(animated: Bool) {
guard let resourceStatus = self.resourceStatus else {
return
}
@ -805,7 +807,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if state == .none {
self.statusNode = nil
}
statusNode.transitionToState(state, completion: { [weak statusNode] in
statusNode.transitionToState(state, animated: animated, completion: { [weak statusNode] in
if state == .none {
statusNode?.removeFromSupernode()
}
@ -856,12 +858,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize)
}
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode))) {
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> ChatMessageInteractiveFileNode))) {
let currentAsyncLayout = node?.asyncLayout()
return { context, presentationData, message, file, automaticDownload, incoming, isRecentActions, dateAndStatusType, constrainedSize in
var fileNode: ChatMessageInteractiveFileNode
var fileLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> Void)))
var fileLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void)))
if let node = node, let currentAsyncLayout = currentAsyncLayout {
fileNode = node
@ -879,8 +881,8 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
return (finalWidth, { boundingWidth in
let (finalSize, apply) = finalLayout(boundingWidth)
return (finalSize, {
apply()
return (finalSize, { synchronousLoads in
apply(synchronousLoads)
return fileNode
})
})
@ -915,7 +917,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
private func ensureHasTimer() {
if self.playerUpdateTimer == nil {
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
self?.updateStatus()
self?.updateStatus(animated: true)
}, queue: Queue.mainQueue())
self.playerUpdateTimer = timer
timer.start()

View File

@ -75,7 +75,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
} else {
self.stopTimer()
}
self.updateStatus()
self.updateStatus(animated: false)
}
}
}
@ -667,7 +667,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if let strongSelf = strongSelf {
strongSelf.fetchStatus = status
strongSelf.actualFetchStatus = actualFetchStatus
strongSelf.updateStatus()
strongSelf.updateStatus(animated: synchronousLoads)
}
}
}))
@ -738,7 +738,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
strongSelf.fetchControls.with({ $0 })?.fetch(false)
}
strongSelf.updateStatus()
strongSelf.updateStatus(animated: synchronousLoads)
}
})
})
@ -749,7 +749,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
private func ensureHasTimer() {
if self.playerUpdateTimer == nil {
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
self?.updateStatus()
self?.updateStatus(animated: false)
}, queue: Queue.mainQueue())
self.playerUpdateTimer = timer
timer.start()
@ -761,7 +761,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
self.playerUpdateTimer = nil
}
private func updateStatus() {
private func updateStatus(animated: Bool) {
guard let (theme, strings, decimalSeparator) = self.themeAndStrings, let sizeCalculation = self.sizeCalculation, let message = self.message, var automaticPlayback = self.automaticPlayback, let wideLayout = self.wideLayout else {
return
}
@ -874,8 +874,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
}
badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: string)
}
var animated = true
var animated: Bool = animated
if var fetchStatus = self.fetchStatus {
var playerPosition: Int32?
var playerDuration: Int32 = 0
@ -1082,7 +1081,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
self.statusNode = nil
removeStatusNode = true
}
statusNode.transitionToState(state, completion: { [weak statusNode] in
statusNode.transitionToState(state, animated: animated, completion: { [weak statusNode] in
if removeStatusNode {
statusNode?.removeFromSupernode()
}
@ -1117,7 +1116,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if isSecretMedia, secretBeginTimeAndTimeout?.0 != nil {
if self.secretTimer == nil {
self.secretTimer = SwiftSignalKit.Timer(timeout: 0.3, repeat: true, completion: { [weak self] in
self?.updateStatus()
self?.updateStatus(animated: false)
}, queue: Queue.mainQueue())
self.secretTimer?.start()
}

View File

@ -113,20 +113,24 @@ public class ComposeController: ViewController {
self.contactsNode.openCreateNewGroup = { [weak self] in
if let strongSelf = self {
let controller = ContactMultiselectionController(context: strongSelf.context, mode: .groupCreation, options: [])
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller, completion: { [weak self] in
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
})
strongSelf.createActionDisposable.set((controller.result
|> deliverOnMainQueue).start(next: { [weak controller] peerIds in
if let strongSelf = self, let controller = controller {
let createGroup = createGroupController(context: strongSelf.context, peerIds: peerIds.compactMap({ peerId in
if case let .peer(peerId) = peerId {
return peerId
} else {
return nil
}
}))
(controller.navigationController as? NavigationController)?.pushViewController(createGroup)
}
}))
|> deliverOnMainQueue).start(next: { [weak controller] peerIds in
if let strongSelf = self, let controller = controller {
let createGroup = createGroupController(context: strongSelf.context, peerIds: peerIds.compactMap({ peerId in
if case let .peer(peerId) = peerId {
return peerId
} else {
return nil
}
}))
(controller.navigationController as? NavigationController)?.pushViewController(createGroup)
}
}))
}
}
@ -154,13 +158,21 @@ public class ComposeController: ViewController {
}))
}
}))
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller, completion: { [weak self] in
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
})
}
}
self.contactsNode.openCreateNewChannel = { [weak self] in
if let strongSelf = self {
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyChannelIntroController(context: strongSelf.context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings))
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyChannelIntroController(context: strongSelf.context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings), completion: { [weak self] in
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
})
}
}

View File

@ -109,7 +109,6 @@ class ContactListActionItem: ListViewItem {
var selectable: Bool = true
func selected(listView: ListView){
listView.clearHighlightAnimated(true)
self.action()
}

View File

@ -192,7 +192,6 @@ public class ContactsController: ViewController {
let openPeer: (ContactListPeer, Bool) -> Void = { [weak self] peer, fromSearch in
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
switch peer {
case let .peer(peer, _, _):
if let navigationController = strongSelf.navigationController as? NavigationController {
@ -201,6 +200,10 @@ public class ContactsController: ViewController {
self?.deactivateSearch(animated: false)
self?.switchToChatsController?()
}
}, completion: { [weak self] in
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
})
}
case let .deviceContact(id, _):
@ -210,7 +213,11 @@ public class ContactsController: ViewController {
guard let strongSelf = self, let value = value else {
return
}
(strongSelf.navigationController as? NavigationController)?.pushViewController(deviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, id, value)))
(strongSelf.navigationController as? NavigationController)?.pushViewController(deviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, id, value)), completion: { [weak self] in
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
})
})
}
}
@ -249,13 +256,21 @@ public class ContactsController: ViewController {
self.contactsNode.openPeopleNearby = { [weak self] in
if let strongSelf = self {
let controller = peopleNearbyController(context: strongSelf.context)
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller, completion: { [weak self] in
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
})
}
}
self.contactsNode.openInvite = { [weak self] in
if let strongSelf = self {
(strongSelf.navigationController as? NavigationController)?.pushViewController(InviteContactsController(context: strongSelf.context))
(strongSelf.navigationController as? NavigationController)?.pushViewController(InviteContactsController(context: strongSelf.context), completion: {
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
})
}
}

View File

@ -242,7 +242,6 @@ class ContactsPeerItem: ListViewItem {
}
func selected(listView: ListView) {
listView.clearHighlightAnimated(true)
self.action(self.peer)
}

View File

@ -42,7 +42,7 @@ final class ContactsSectionHeaderAccessoryItem: ListViewAccessoryItem {
}
}
func node() -> ListViewAccessoryItemNode {
func node(synchronous: Bool) -> ListViewAccessoryItemNode {
return ContactsSectionHeaderAccessoryItemNode(sectionHeader: self.sectionHeader, theme: self.theme)
}
}

View File

@ -106,6 +106,8 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr
let shareController = ShareController(context: strongSelf.context, subject: .text(body), externalShare: true, immediateExternalShare: true)
strongSelf.present(shareController, in: .window(.root))
strongSelf.contactsNode.listNode.clearHighlightAnimated(true)
}
}

View File

@ -132,7 +132,7 @@ struct ItemListControllerState {
}
}
class ItemListController<Entry: ItemListNodeEntry>: ViewController, PresentableController {
class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShortcutResponder, PresentableController {
private let state: Signal<(ItemListControllerState, (ItemListNodeState<Entry>, Entry.ItemGenerationArguments)), NoError>
private var leftNavigationButtonTitleAndStyle: (ItemListNavigationButtonContent, ItemListNavigationButtonStyle)?
@ -592,4 +592,11 @@ class ItemListController<Entry: ItemListNodeEntry>: ViewController, PresentableC
func previewingCommit(_ viewControllerToCommit: UIViewController) {
self.commitPreview?(viewControllerToCommit)
}
public var keyShortcuts: [KeyShortcut] {
return [KeyShortcut(input: UIKeyInputEscape, action: { [weak self] in
if !(self?.navigationController?.topViewController is TabBarController) {
_ = self?.navigationBar?.executeBack()
}
})]
}
}

View File

@ -10,7 +10,7 @@ public enum NavigateToChatKeepStack {
case never
}
public func navigateToChatController(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, completion: @escaping () -> Void = {}) {
public func navigateToChatController(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) {
var found = false
var isFirst = true
for controller in navigationController.viewControllers.reversed() {
@ -58,7 +58,19 @@ public func navigateToChatController(navigationController: NavigationController,
if resolvedKeepStack {
navigationController.pushViewController(controller, animated: animated, completion: completion)
} else {
let viewControllers = navigationController.viewControllers.filter({ $0 is ChatListController || $0 is TabBarController })
let viewControllers = navigationController.viewControllers.filter({ controller in
if controller is ChatListController {
if let parentGroupId = parentGroupId {
return parentGroupId != .root
} else {
return true
}
} else if controller is TabBarController {
return true
} else {
return false
}
})
if viewControllers.isEmpty {
navigationController.replaceAllButRootController(controller, animated: animated, completion: completion)
} else {

View File

@ -224,12 +224,9 @@ final class PeerChannelMemberCategoriesContextsManager {
}
}
func updateMemberAdminRights(account: Account, peerId: PeerId, memberId: PeerId, adminRights: TelegramChatAdminRights) -> Signal<Void, NoError> {
func updateMemberAdminRights(account: Account, peerId: PeerId, memberId: PeerId, adminRights: TelegramChatAdminRights) -> Signal<Void, UpdateChannelAdminRightsError> {
return updateChannelAdminRights(account: account, peerId: peerId, adminId: memberId, rights: adminRights)
|> map(Optional.init)
|> `catch` { _ -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, NoError> in
return .single(nil)
}
|> deliverOnMainQueue
|> beforeNext { [weak self] result in
if let strongSelf = self, let (previous, updated) = result {
@ -242,7 +239,7 @@ final class PeerChannelMemberCategoriesContextsManager {
}
}
}
|> mapToSignal { _ -> Signal<Void, NoError> in
|> mapToSignal { _ -> Signal<Void, UpdateChannelAdminRightsError> in
return .complete()
}
}

View File

@ -174,6 +174,7 @@ private enum PeopleNearbyEntry: ItemListNodeEntry {
return ItemListPlaceholderItem(theme: theme, text: text, sectionId: self.section, style: .blocks)
case let .user(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
func distance(_ distance: Int32) -> String {
var distance = max(1, distance)
let formatter = MKDistanceFormatter()
formatter.unitStyle = .abbreviated
return formatter.string(fromDistance: Double(distance))
@ -239,23 +240,23 @@ private func peopleNearbyControllerEntries(state: PeopleNearbyControllerState, d
entries.append(.empty(presentationData.theme, presentationData.strings.PeopleNearby_UsersEmpty))
}
entries.append(.groupsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Groups.uppercased()))
entries.append(.createGroup(presentationData.theme, presentationData.strings.PeopleNearby_CreateGroup))
if let data = data, !data.groups.isEmpty {
var i: Int32 = 0
for group in data.groups {
entries.append(.group(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, group))
i += 1
}
}
if let data = data, !data.channels.isEmpty {
var i: Int32 = 0
for channel in data.channels {
entries.append(.channel(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, channel))
i += 1
}
}
// entries.append(.groupsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Groups.uppercased()))
// entries.append(.createGroup(presentationData.theme, presentationData.strings.PeopleNearby_CreateGroup))
// if let data = data, !data.groups.isEmpty {
// var i: Int32 = 0
// for group in data.groups {
// entries.append(.group(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, group))
// i += 1
// }
// }
//
// if let data = data, !data.channels.isEmpty {
// var i: Int32 = 0
// for channel in data.channels {
// entries.append(.channel(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, channel))
// i += 1
// }
// }
return entries
}
@ -285,7 +286,7 @@ public func peopleNearbyController(context: AccountContext) -> ViewController {
guard let coordinate = coordinate else {
return .single(nil)
}
return peersNearby(network: context.account.network, accountStateManager: context.account.stateManager, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude), radius: 200)
let poll = peersNearby(network: context.account.network, accountStateManager: context.account.stateManager, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude), radius: 100)
|> mapToSignal { peersNearby -> Signal<PeopleNearbyData?, NoError> in
return context.account.postbox.transaction { transaction -> PeopleNearbyData? in
var result: [PeerNearbyEntry] = []
@ -297,6 +298,7 @@ public func peopleNearbyController(context: AccountContext) -> ViewController {
return PeopleNearbyData(users: result, groups: [], channels: [])
}
}
return (poll |> then(.complete() |> suspendAwareDelay(25.0, queue: Queue.concurrentDefaultQueue()))) |> restart
}
dataPromise.set(dataSignal)
@ -315,7 +317,7 @@ public func peopleNearbyController(context: AccountContext) -> ViewController {
let controller = ItemListController(context: context, state: signal)
navigateToChatImpl = { [weak controller] peer in
if let navigationController = controller?.navigationController as? NavigationController {
navigateToChatController(navigationController: navigationController, context: context, chatLocation: .peer(peer.id))
navigateToChatController(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always)
}
}
presentControllerImpl = { [weak controller] c, p in

View File

@ -42,6 +42,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
} else {
let _ = options.insert(.LowLatency)
let _ = options.insert(.Synchronous)
let _ = options.insert(.PreferSynchronousResourceLoading)
}
case .InteractiveChanges:
let _ = options.insert(.AnimateAlpha)

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@ private func presentLiveLocationController(context: AccountContext, peerId: Peer
}
}
public class TelegramController: ViewController {
public class TelegramController: ViewController, KeyShortcutResponder {
private let context: AccountContext
let mediaAccessoryPanelVisibility: MediaAccessoryPanelVisibility
@ -633,4 +633,12 @@ public class TelegramController: ViewController {
})
}
}
public var keyShortcuts: [KeyShortcut] {
return [KeyShortcut(input: UIKeyInputEscape, action: { [weak self] in
if !(self?.navigationController?.topViewController is TabBarController) {
_ = self?.navigationBar?.executeBack()
}
})]
}
}

View File

@ -17,7 +17,11 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
let lineWidth: CGFloat
if selected {
context.setStrokeColor(theme.list.itemAccentColor.cgColor)
var accentColor = theme.list.itemAccentColor
if accentColor.rgb == UIColor.white.rgb {
accentColor = UIColor(rgb: 0x999999)
}
context.setStrokeColor(accentColor.cgColor)
lineWidth = 2.0
} else {
context.setStrokeColor(theme.list.disclosureArrowColor.withAlphaComponent(0.4).cgColor)

View File

@ -390,10 +390,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: disabled)
}).start()
}, selectAppIcon: { name in
context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { succeed in
if succeed {
currentAppIconName.set(name)
}
currentAppIconName.set(name)
context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { _ in
})
})
@ -431,11 +429,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeAccentColor: settings.themeAccentColor, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
if previousTheme.swap(theme)?.name != theme.name {
//presentControllerImpl?(ThemeSettingsCrossfadeController())
}
return (controllerState, (listState, arguments))
}

View File

@ -17,7 +17,11 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
let lineWidth: CGFloat
if selected {
context.setStrokeColor(theme.list.itemAccentColor.cgColor)
var accentColor = theme.list.itemAccentColor
if accentColor.rgb == UIColor.white.rgb {
accentColor = UIColor(rgb: 0x999999)
}
context.setStrokeColor(accentColor.cgColor)
lineWidth = 2.0
} else {
context.setStrokeColor(theme.list.disclosureArrowColor.withAlphaComponent(0.4).cgColor)
@ -77,9 +81,7 @@ private func generateThemeIconImage(theme: PresentationBuiltinThemeReference, ac
context.scaleBy(x: -1.0, y: 1.0)
context.translateBy(x: -bounds.width / 2.0, y: -bounds.height / 2.0)
context.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
})!.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
})!
}
class ThemeSettingsThemeItem: ListViewItem, ItemListItem {

View File

@ -108,7 +108,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 3
self.originalRemainingSeconds = 5
}
self.remainingSeconds = self.originalRemainingSeconds