mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '77b1547f847a072abe533f3008389d38921dcd47'
This commit is contained in:
commit
fa3ac0b61a
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,6 @@ class ContactListActionItem: ListViewItem {
|
||||
var selectable: Bool = true
|
||||
|
||||
func selected(listView: ListView){
|
||||
listView.clearHighlightAnimated(true)
|
||||
self.action()
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,6 @@ class ContactsPeerItem: ListViewItem {
|
||||
}
|
||||
|
||||
func selected(listView: ListView) {
|
||||
listView.clearHighlightAnimated(true)
|
||||
self.action(self.peer)
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ final class ContactsSectionHeaderAccessoryItem: ListViewAccessoryItem {
|
||||
}
|
||||
}
|
||||
|
||||
func node() -> ListViewAccessoryItemNode {
|
||||
func node(synchronous: Bool) -> ListViewAccessoryItemNode {
|
||||
return ContactsSectionHeaderAccessoryItemNode(sectionHeader: self.sectionHeader, theme: self.theme)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
})]
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
Binary file not shown.
@ -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()
|
||||
}
|
||||
})]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user