Various fixes

This commit is contained in:
Ilya Laktyushin 2023-02-21 18:40:34 +04:00
parent 6de1267a1d
commit 1e6c6d6091
23 changed files with 290 additions and 151 deletions

View File

@ -8930,3 +8930,5 @@ Sorry for the inconvenience.";
"VoiceOver.Chat.NotPlayedByRecipients" = "Not played by recipients"; "VoiceOver.Chat.NotPlayedByRecipients" = "Not played by recipients";
"VoiceOver.Chat.ReplyingToMessage" = "In reply to message: %@"; "VoiceOver.Chat.ReplyingToMessage" = "In reply to message: %@";
"MediaPicker.VoiceOver.Camera" = "Camera";

View File

@ -277,6 +277,7 @@ public enum ResolvedUrl {
case inaccessiblePeer case inaccessiblePeer
case botStart(peer: Peer, payload: String) case botStart(peer: Peer, payload: String)
case groupBotStart(peerId: PeerId, payload: String, adminRights: ResolvedBotAdminRights?) case groupBotStart(peerId: PeerId, payload: String, adminRights: ResolvedBotAdminRights?)
case gameStart(peerId: PeerId, game: String)
case channelMessage(peer: Peer, messageId: MessageId, timecode: Double?) case channelMessage(peer: Peer, messageId: MessageId, timecode: Double?)
case replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage, messageId: MessageId) case replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage, messageId: MessageId)
case replyThread(messageId: MessageId) case replyThread(messageId: MessageId)

View File

@ -2461,7 +2461,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
if let strongSelf = self { if let strongSelf = self {
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData
strongSelf.presentationDataPromise.set(.single(ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true))) strongSelf.presentationDataPromise.set(.single(ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true)))
if strongSelf.backgroundColor != nil {
strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor
}
strongSelf.listNode.forEachItemHeaderNode({ itemHeaderNode in strongSelf.listNode.forEachItemHeaderNode({ itemHeaderNode in
if let itemHeaderNode = itemHeaderNode as? ChatListSearchItemHeaderNode { if let itemHeaderNode = itemHeaderNode as? ChatListSearchItemHeaderNode {
itemHeaderNode.updateTheme(theme: presentationData.theme) itemHeaderNode.updateTheme(theme: presentationData.theme)

View File

@ -1648,6 +1648,10 @@ public final class ChatListNode: ListView {
guard !filter.contains(.onlyPrivateChats) || peer.peerId.namespace == Namespaces.Peer.CloudUser else { return false } guard !filter.contains(.onlyPrivateChats) || peer.peerId.namespace == Namespaces.Peer.CloudUser else { return false }
if let peer = peer.peer { if let peer = peer.peer {
if peer.id.isReplies {
return false
}
switch peer { switch peer {
case let .user(user): case let .user(user):
if user.botInfo != nil { if user.botInfo != nil {

View File

@ -994,15 +994,16 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
if let _ = self.presentationNode { if let _ = self.presentationNode {
self.currentPresentationStateTransition = .animateOut(result: initialResult, completion: completion) self.currentPresentationStateTransition = .animateOut(result: initialResult, completion: completion)
if let validLayout = self.validLayout { if let validLayout = self.validLayout {
if case .custom = initialResult { if case let .custom(transition) = initialResult {
self.delayLayoutUpdate = true self.delayLayoutUpdate = true
Queue.mainQueue().after(0.05) { Queue.mainQueue().after(0.1) {
self.delayLayoutUpdate = false self.delayLayoutUpdate = false
self.updateLayout( self.updateLayout(
layout: validLayout, layout: validLayout,
transition: .animated(duration: 0.35, curve: .easeInOut), transition: transition,
previousActionsContainerNode: nil previousActionsContainerNode: nil
) )
self.isAnimatingOut = true
} }
} else { } else {
self.updateLayout( self.updateLayout(

View File

@ -80,3 +80,7 @@ public func isBoldTextEnabled() -> Signal<Bool, NoError> {
} }
|> runOn(Queue.mainQueue()) |> runOn(Queue.mainQueue())
} }
public func isReduceTransparencyEnabled() -> Bool {
UIAccessibility.isReduceTransparencyEnabled
}

View File

@ -544,122 +544,142 @@ public final class InviteLinkViewController: ViewController {
return return
} }
let presentationData = context.sharedContext.currentPresentationData.with { $0 } var creatorIsBot: Signal<Bool, NoError>
var items: [ContextMenuItem] = [] if case let .link(_, _, _, _, _, adminId, _, _, _, _, _, _) = invite {
creatorIsBot = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: adminId))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextCopy, icon: { theme in |> map { peer -> Bool in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) if let peer, case let .user(user) = peer, user.botInfo != nil {
}, action: { [weak self] _, f in return true
f(.dismissWithoutContent) } else {
return false
UIPasteboard.general.string = invite.link }
}
self?.controller?.dismissAllTooltips() } else {
creatorIsBot = .single(false)
let presentationData = context.sharedContext.currentPresentationData.with { $0 } }
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
})))
if invite.isRevoked { let _ = (creatorIsBot
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextDelete, textColor: .destructive, icon: { theme in |> take(1)
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) |> deliverOnMainQueue).start(next: { creatorIsBot in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextCopy, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
let controller = ActionSheetController(presentationData: presentationData) UIPasteboard.general.string = invite.link
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() self?.controller?.dismissAllTooltips()
}
controller.setItemGroups([ let presentationData = context.sharedContext.currentPresentationData.with { $0 }
ActionSheetItemGroup(items: [ self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
ActionSheetTextItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Action, color: .destructive, action: {
dismissAction()
self?.controller?.dismiss()
if let inviteLink = invite.link {
let _ = (context.engine.peers.deletePeerExportedInvitation(peerId: peerId, link: inviteLink) |> deliverOnMainQueue).start()
}
self?.controller?.revokedInvitationsContext?.remove(invite)
})
]),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
self?.controller?.present(controller, in: .window(.root))
}))) })))
} else {
if !invitationAvailability(invite).isZero { if invite.isRevoked {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextDelete, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { [weak self] peer in
guard let strongSelf = self, let parentController = strongSelf.controller else {
return
}
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let updatedPresentationData = (strongSelf.presentationData, parentController.presentationDataPromise.get())
strongSelf.controller?.present(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), in: .window(.root))
})
})))
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = ActionSheetController(presentationData: presentationData) let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() controller?.dismissAnimated()
} }
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text), ActionSheetTextItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Action, color: .destructive, action: {
dismissAction() dismissAction()
self?.controller?.dismiss() self?.controller?.dismiss()
if let inviteLink = invite.link { if let inviteLink = invite.link {
let _ = (context.engine.peers.revokePeerExportedInvitation(peerId: peerId, link: inviteLink) |> deliverOnMainQueue).start(next: { result in let _ = (context.engine.peers.deletePeerExportedInvitation(peerId: peerId, link: inviteLink) |> deliverOnMainQueue).start()
if case let .replace(_, newInvite) = result {
self?.controller?.invitationsContext?.add(newInvite)
}
})
} }
self?.controller?.invitationsContext?.remove(invite) self?.controller?.revokedInvitationsContext?.remove(invite)
let revokedInvite = invite.withUpdated(isRevoked: true)
self?.controller?.revokedInvitationsContext?.add(revokedInvite)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: presentationData.strings.InviteLink_InviteLinkRevoked), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}) })
]), ]),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
]) ])
self?.controller?.present(controller, in: .window(.root)) self?.controller?.present(controller, in: .window(.root))
}) })))
}))) } else {
} if !invitationAvailability(invite).isZero {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor)
self?.controller?.presentInGlobalOverlay(contextController) }, action: { [weak self] _, f in
f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { [weak self] peer in
guard let strongSelf = self, let parentController = strongSelf.controller else {
return
}
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let updatedPresentationData = (strongSelf.presentationData, parentController.presentationDataPromise.get())
strongSelf.controller?.present(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), in: .window(.root))
})
})))
}
if !creatorIsBot {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
}
controller.setItemGroups([
ActionSheetItemGroup(items: [
ActionSheetTextItem(title: isGroup ? presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text : presentationData.strings.ChannelInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction()
self?.controller?.dismiss()
if let inviteLink = invite.link {
let _ = (context.engine.peers.revokePeerExportedInvitation(peerId: peerId, link: inviteLink) |> deliverOnMainQueue).start(next: { result in
if case let .replace(_, newInvite) = result {
self?.controller?.invitationsContext?.add(newInvite)
}
})
}
self?.controller?.invitationsContext?.remove(invite)
let revokedInvite = invite.withUpdated(isRevoked: true)
self?.controller?.revokedInvitationsContext?.add(revokedInvite)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: presentationData.strings.InviteLink_InviteLinkRevoked), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
})
]),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
self?.controller?.present(controller, in: .window(.root))
})
})))
}
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
self?.controller?.presentInGlobalOverlay(contextController)
})
}) })
let previousEntries = Atomic<[InviteLinkViewEntry]?>(value: nil) let previousEntries = Atomic<[InviteLinkViewEntry]?>(value: nil)

View File

@ -15,7 +15,7 @@ public extension EnginePeer {
} else if let _ = user.phone { } else if let _ = user.phone {
return "" //formatPhoneNumber("+\(phone)") return "" //formatPhoneNumber("+\(phone)")
} else { } else {
return "" return "Deleted Account"
} }
case let .legacyGroup(group): case let .legacyGroup(group):
return group.title return group.title

View File

@ -238,28 +238,29 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
} }
let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
let controller = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: hasTimer) let sheetController = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: hasTimer)
let dismissImpl = { [weak model] in let dismissImpl = { [weak model] in
model?.dismiss(true, false) model?.dismiss(true, false)
dismissAll()
} }
controller.send = { sheetController.send = {
completed(item.asset, false, nil, { completed(item.asset, false, nil, {
dismissImpl() dismissImpl()
}) })
} }
controller.sendSilently = { sheetController.sendSilently = {
completed(item.asset, true, nil, { completed(item.asset, true, nil, {
dismissImpl() dismissImpl()
}) })
} }
controller.schedule = { sheetController.schedule = {
presentSchedulePicker(true, { time in presentSchedulePicker(true, { time in
completed(item.asset, false, time, { completed(item.asset, false, time, {
dismissImpl() dismissImpl()
}) })
}) })
} }
controller.sendWithTimer = { sheetController.sendWithTimer = {
presentTimerPicker { time in presentTimerPicker { time in
var items = selectionContext.selectedItems() ?? [] var items = selectionContext.selectedItems() ?? []
items.append(item.asset as Any) items.append(item.asset as Any)
@ -273,10 +274,10 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
}) })
} }
} }
controller.customDismissBlock = { [weak legacySheetController] in sheetController.customDismissBlock = { [weak legacySheetController] in
legacySheetController?.dismiss() legacySheetController?.dismiss()
} }
legacySheetController.bind(controller: controller) legacySheetController.bind(controller: sheetController)
present(legacySheetController, nil) present(legacySheetController, nil)
let hapticFeedback = HapticFeedback() let hapticFeedback = HapticFeedback()

View File

@ -235,7 +235,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.scrollingArea = SparseItemGridScrollingArea() self.scrollingArea = SparseItemGridScrollingArea()
self.cameraActivateAreaNode = AccessibilityAreaNode() self.cameraActivateAreaNode = AccessibilityAreaNode()
self.cameraActivateAreaNode.accessibilityLabel = "Camera" self.cameraActivateAreaNode.accessibilityLabel = self.presentationData.strings.MediaPicker_VoiceOver_Camera
self.cameraActivateAreaNode.accessibilityTraits = [.button] self.cameraActivateAreaNode.accessibilityTraits = [.button]
super.init() super.init()

View File

@ -119,6 +119,10 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode {
let blurredBackgroundColor = (background.inverted ? UIColor(rgb: 0xffffff, alpha: 0.1) : UIColor(rgb: 0x000000, alpha: 0.2), dateFillNeedsBlur(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)) let blurredBackgroundColor = (background.inverted ? UIColor(rgb: 0xffffff, alpha: 0.1) : UIColor(rgb: 0x000000, alpha: 0.2), dateFillNeedsBlur(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper))
let blurredBackgroundNode = NavigationBackgroundNode(color: blurredBackgroundColor.0, enableBlur: blurredBackgroundColor.1) let blurredBackgroundNode = NavigationBackgroundNode(color: blurredBackgroundColor.0, enableBlur: blurredBackgroundColor.1)
self.blurredBackgroundNode = blurredBackgroundNode self.blurredBackgroundNode = blurredBackgroundNode
if isReduceTransparencyEnabled() {
blurredBackgroundNode.alpha = 0.1
}
} }
self.backgroundNode = ASImageNode() self.backgroundNode = ASImageNode()

View File

@ -1100,11 +1100,11 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
return return
} }
if updateFlags != currentFlags { if let updateFlags, updateFlags != currentFlags {
updateState { current in updateState { current in
return current.withUpdatedUpdating(true) return current.withUpdatedUpdating(true)
} }
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(engine: context.engine, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(rights: updateFlags ?? []), rank: effectiveRank) |> deliverOnMainQueue).start(error: { error in updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(engine: context.engine, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(rights: updateFlags), rank: effectiveRank) |> deliverOnMainQueue).start(error: { error in
updateState { current in updateState { current in
return current.withUpdatedUpdating(false) return current.withUpdatedUpdating(false)
} }
@ -1145,7 +1145,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
} }
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}, completed: { }, completed: {
updated(TelegramChatAdminRights(rights: updateFlags ?? [])) updated(TelegramChatAdminRights(rights: updateFlags))
dismissImpl?() dismissImpl?()
})) }))
} else if let updateRank = updateRank, let currentFlags = currentFlags { } else if let updateRank = updateRank, let currentFlags = currentFlags {

View File

@ -400,7 +400,7 @@ public final class PendingMessageManager {
return lhs.1.index < rhs.1.index return lhs.1.index < rhs.1.index
}) { }) {
if case let .collectingInfo(message) = messageContext.state { if case let .collectingInfo(message) = messageContext.state {
let contentToUpload = messageContentToUpload(network: strongSelf.network, postbox: strongSelf.postbox, auxiliaryMethods: strongSelf.auxiliaryMethods, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, messageMediaPreuploadManager: strongSelf.messageMediaPreuploadManager, revalidationContext: strongSelf.revalidationContext, forceReupload: messageContext.forcedReuploadOnce, isGrouped: message.groupingKey != nil, message: message) let contentToUpload = messageContentToUpload(network: strongSelf.network, postbox: strongSelf.postbox, auxiliaryMethods: strongSelf.auxiliaryMethods, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, messageMediaPreuploadManager: strongSelf.messageMediaPreuploadManager, revalidationContext: strongSelf.revalidationContext, forceReupload: messageContext.forcedReuploadOnce, isGrouped: message.groupingKey != nil, message: message)
messageContext.contentType = contentToUpload.type messageContext.contentType = contentToUpload.type
switch contentToUpload { switch contentToUpload {
case let .immediate(result, type): case let .immediate(result, type):

View File

@ -623,7 +623,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
let inputPanel = PresentationThemeChatInputPanel( let inputPanel = PresentationThemeChatInputPanel(
panelBackgroundColor: rootNavigationBar.blurredBackgroundColor, panelBackgroundColor: rootNavigationBar.blurredBackgroundColor,
panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000, alpha: 0.94), panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000),
panelSeparatorColor: UIColor(rgb: 0x545458, alpha: 0.55), panelSeparatorColor: UIColor(rgb: 0x545458, alpha: 0.55),
panelControlAccentColor: UIColor(rgb: 0xffffff), panelControlAccentColor: UIColor(rgb: 0xffffff),
panelControlColor: UIColor(rgb: 0x808080), panelControlColor: UIColor(rgb: 0x808080),

View File

@ -877,7 +877,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
let inputPanel = PresentationThemeChatInputPanel( let inputPanel = PresentationThemeChatInputPanel(
panelBackgroundColor: rootNavigationBar.blurredBackgroundColor, panelBackgroundColor: rootNavigationBar.blurredBackgroundColor,
panelBackgroundColorNoWallpaper: rootNavigationBar.blurredBackgroundColor, panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff),
panelSeparatorColor: UIColor(rgb: 0xb2b2b2), panelSeparatorColor: UIColor(rgb: 0xb2b2b2),
panelControlAccentColor: defaultDayAccentColor, panelControlAccentColor: defaultDayAccentColor,
panelControlColor: UIColor(rgb: 0x858e99), panelControlColor: UIColor(rgb: 0x858e99),

View File

@ -51,6 +51,34 @@ public func plainServiceMessageString(strings: PresentationStrings, nameDisplayO
} }
} }
private func peerDisplayTitles(_ peerIds: [PeerId], _ dict: SimpleDictionary<PeerId, Peer>, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
var peers: [Peer] = []
for id in peerIds {
if let peer = dict[id] {
peers.append(peer)
}
}
return peerDisplayTitles(peers, strings: strings, nameDisplayOrder: nameDisplayOrder)
}
private func peerDisplayTitles(_ peers: [Peer], strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
if peers.count == 0 {
return ""
} else {
var string = ""
var first = true
for peer in peers {
if first {
first = false
} else {
string.append(", ")
}
string.append(EnginePeer(peer).displayTitle(strings: strings, displayOrder: nameDisplayOrder))
}
return string
}
}
public func universalServiceMessageString(presentationData: (PresentationTheme, TelegramWallpaper)?, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id, forChatList: Bool, forForumOverview: Bool) -> NSAttributedString? { public func universalServiceMessageString(presentationData: (PresentationTheme, TelegramWallpaper)?, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id, forChatList: Bool, forForumOverview: Bool) -> NSAttributedString? {
var attributedString: NSAttributedString? var attributedString: NSAttributedString?
@ -96,9 +124,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
let resultTitleString: PresentationStrings.FormattedString let resultTitleString: PresentationStrings.FormattedString
if peerIds.count == 1 { if peerIds.count == 1 {
attributePeerIds.append((1, peerIds.first)) attributePeerIds.append((1, peerIds.first))
resultTitleString = strings.Notification_Invited(authorName, peerDebugDisplayTitles(peerIds, message.peers)) resultTitleString = strings.Notification_Invited(authorName, peerDisplayTitles(peerIds, message.peers, strings: strings, nameDisplayOrder: nameDisplayOrder))
} else { } else {
resultTitleString = strings.Notification_InvitedMultiple(authorName, peerDebugDisplayTitles(peerIds, message.peers)) resultTitleString = strings.Notification_InvitedMultiple(authorName, peerDisplayTitles(peerIds, message.peers, strings: strings, nameDisplayOrder: nameDisplayOrder))
} }
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
@ -115,7 +143,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
if peerIds.count == 1 { if peerIds.count == 1 {
attributePeerIds.append((1, peerIds.first)) attributePeerIds.append((1, peerIds.first))
} }
attributedString = addAttributesToStringWithRanges(strings.Notification_Kicked(authorName, peerDebugDisplayTitles(peerIds, message.peers))._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) attributedString = addAttributesToStringWithRanges(strings.Notification_Kicked(authorName, peerDisplayTitles(peerIds, message.peers, strings: strings, nameDisplayOrder: nameDisplayOrder))._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
} }
case let .photoUpdated(image): case let .photoUpdated(image):
if authorName.isEmpty || isChannel { if authorName.isEmpty || isChannel {
@ -652,10 +680,10 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
resultTitleString = strings.Notification_VoiceChatInvitationForYou(authorName) resultTitleString = strings.Notification_VoiceChatInvitationForYou(authorName)
} else { } else {
attributePeerIds.append((1, peerIds.first)) attributePeerIds.append((1, peerIds.first))
resultTitleString = strings.Notification_VoiceChatInvitation(authorName, peerDebugDisplayTitles(peerIds, message.peers)) resultTitleString = strings.Notification_VoiceChatInvitation(authorName, peerDisplayTitles(peerIds, message.peers, strings: strings, nameDisplayOrder: nameDisplayOrder))
} }
} else { } else {
resultTitleString = strings.Notification_VoiceChatInvitation(authorName, peerDebugDisplayTitles(peerIds, message.peers)) resultTitleString = strings.Notification_VoiceChatInvitation(authorName, peerDisplayTitles(peerIds, message.peers, strings: strings, nameDisplayOrder: nameDisplayOrder))
} }
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))

View File

@ -3061,6 +3061,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
strongSelf.commitPurposefulAction() strongSelf.commitPurposefulAction()
var isScheduledMessages = false
if case .scheduledMessages = strongSelf.presentationInterfaceState.subject {
isScheduledMessages = true
}
guard !isScheduledMessages else {
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return
}
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|> deliverOnMainQueue).start(next: { message in |> deliverOnMainQueue).start(next: { message in
guard let strongSelf = self, let message = message else { guard let strongSelf = self, let message = message else {
@ -4226,7 +4236,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
} }
let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationSend, action: { let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationSend, action: {
completion() completion()
@ -4238,15 +4248,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
var isChannel = false if case .user = peerType {
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
isChannel = true
}
let peerName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
presentConfirmation(peerName, isChannel, {
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).start() let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).start()
controller?.dismiss() } else {
}) var isChannel = false
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
isChannel = true
}
let peerName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
presentConfirmation(peerName, isChannel, {
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).start()
controller?.dismiss()
})
}
} }
createNewGroupImpl = { [weak controller] in createNewGroupImpl = { [weak controller] in
switch peerType { switch peerType {
@ -14591,7 +14605,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
let replyMessageId = self.presentationInterfaceState.interfaceState.replyMessageId let replyMessageId = self.presentationInterfaceState.interfaceState.replyMessageId
if self.context.engine.messages.enqueueOutgoingMessageWithChatContextResult(to: peerId, threadId: self.chatLocation.threadId, botId: results.botId, result: result, replyToMessageId: replyMessageId, hideVia: hideVia, silentPosting: silentPosting) { if self.context.engine.messages.enqueueOutgoingMessageWithChatContextResult(to: peerId, threadId: self.chatLocation.threadId, botId: results.botId, result: result, replyToMessageId: replyMessageId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime) {
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput() strongSelf.chatDisplayNode.collapseInput()

View File

@ -238,7 +238,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
var viaBotApply: (TextNodeLayout, () -> TextNode)? var viaBotApply: (TextNodeLayout, () -> TextNode)?
var replyInfoApply: (CGSize, (Bool) -> ChatMessageReplyInfoNode)? var replyInfoApply: (CGSize, (Bool) -> ChatMessageReplyInfoNode)?
var updatedReplyBackgroundNode: NavigationBackgroundNode?
var updatedInstantVideoBackgroundImage: UIImage? var updatedInstantVideoBackgroundImage: UIImage?
let instantVideoBackgroundImage: UIImage? let instantVideoBackgroundImage: UIImage?
@ -556,17 +555,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} }
let effectiveAudioTranscriptionState = updatedAudioTranscriptionState ?? audioTranscriptionState let effectiveAudioTranscriptionState = updatedAudioTranscriptionState ?? audioTranscriptionState
if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil {
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
updatedReplyBackgroundNode = currentReplyBackgroundNode
} else {
updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
}
updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
}
return (result, { [weak self] layoutData, animation in return (result, { [weak self] layoutData, animation in
if let strongSelf = self { if let strongSelf = self {
strongSelf.item = item strongSelf.item = item
@ -575,6 +564,17 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
strongSelf.secretProgressIcon = secretProgressIcon strongSelf.secretProgressIcon = secretProgressIcon
strongSelf.automaticDownload = automaticDownload strongSelf.automaticDownload = automaticDownload
var updatedReplyBackgroundNode: NavigationBackgroundNode?
if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil {
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
updatedReplyBackgroundNode = currentReplyBackgroundNode
} else {
updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
}
updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
}
if let updatedAudioTranscriptionState = updatedAudioTranscriptionState { if let updatedAudioTranscriptionState = updatedAudioTranscriptionState {
strongSelf.audioTranscriptionState = updatedAudioTranscriptionState strongSelf.audioTranscriptionState = updatedAudioTranscriptionState
strongSelf.updateTranscriptionExpanded?(strongSelf.audioTranscriptionState) strongSelf.updateTranscriptionExpanded?(strongSelf.audioTranscriptionState)
@ -928,7 +928,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
strongSelf.addSubnode(viaBotNode) strongSelf.addSubnode(viaBotNode)
} }
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? 11.0 : (width - messageInfoSize.width - bubbleEdgeInset - 9.0 + 10.0)), y: 8.0), size: viaBotLayout.size) let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 6.0) : (width - messageInfoSize.width - bubbleEdgeInset - 9.0 + 10.0)), y: 8.0), size: viaBotLayout.size)
animation.animator.updateFrame(layer: viaBotNode.layer, frame: viaBotFrame, completion: nil) animation.animator.updateFrame(layer: viaBotNode.layer, frame: viaBotFrame, completion: nil)
messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height) messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height)
@ -1226,6 +1226,30 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
switch gesture { switch gesture {
case .tap: case .tap:
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
var botAddressName: String?
if let peerId = attribute.peerId, let botPeer = item.message.peers[peerId], let addressName = botPeer.addressName {
botAddressName = addressName
} else {
botAddressName = attribute.title
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
return
}
}
}
}
}
if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) { if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) {
if let item = self.item { if let item = self.item {
for attribute in item.message.attributes { for attribute in item.message.attributes {
@ -1315,6 +1339,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
return playbackNode.view return playbackNode.view
} }
} }
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(point), !viaBotNode.alpha.isZero {
return self.view
}
if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(point), !forwardInfoNode.alpha.isZero { if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(point), !forwardInfoNode.alpha.isZero {
return self.view return self.view
} }

View File

@ -975,6 +975,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
break break
case .groupBotStart: case .groupBotStart:
break break
case .gameStart:
break
case let .channelMessage(peer, messageId, timecode): case let .channelMessage(peer, messageId, timecode):
if let navigationController = strongSelf.getNavigationController() { if let navigationController = strongSelf.getNavigationController() {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(EnginePeer(peer)), subject: .message(id: .id(messageId), highlight: true, timecode: timecode))) strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(EnginePeer(peer)), subject: .message(id: .id(messageId), highlight: true, timecode: timecode)))

View File

@ -379,11 +379,11 @@ private func calculateTextFieldRealInsets(presentationInterfaceState: ChatPresen
return UIEdgeInsets(top: 4.5 + top, left: 0.0, bottom: 5.5 + bottom, right: right) return UIEdgeInsets(top: 4.5 + top, left: 0.0, bottom: 5.5 + bottom, right: right)
} }
private var currentTextInputBackgroundImage: (UIColor, UIColor, CGFloat, UIImage)? private var currentTextInputBackgroundImage: (UIColor, UIColor, CGFloat, CGFloat, UIImage)?
private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackgroundColor: UIColor?, strokeColor: UIColor, diameter: CGFloat) -> UIImage? { private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackgroundColor: UIColor?, strokeColor: UIColor, diameter: CGFloat, strokeWidth: CGFloat) -> UIImage? {
if let backgroundColor = backgroundColor, let current = currentTextInputBackgroundImage { if let backgroundColor = backgroundColor, let current = currentTextInputBackgroundImage {
if current.0.isEqual(backgroundColor) && current.1.isEqual(strokeColor) && current.2.isEqual(to: diameter) { if current.0.isEqual(backgroundColor) && current.1.isEqual(strokeColor) && current.2.isEqual(to: diameter) && current.3.isEqual(to: strokeWidth) {
return current.3 return current.4
} }
} }
@ -401,13 +401,12 @@ private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackground
context.setBlendMode(.normal) context.setBlendMode(.normal)
context.setStrokeColor(strokeColor.cgColor) context.setStrokeColor(strokeColor.cgColor)
let strokeWidth: CGFloat = UIScreenPixel
context.setLineWidth(strokeWidth) context.setLineWidth(strokeWidth)
context.strokeEllipse(in: CGRect(x: strokeWidth / 2.0, y: strokeWidth / 2.0, width: diameter - strokeWidth, height: diameter - strokeWidth)) context.strokeEllipse(in: CGRect(x: strokeWidth / 2.0, y: strokeWidth / 2.0, width: diameter - strokeWidth, height: diameter - strokeWidth))
})?.stretchableImage(withLeftCapWidth: Int(diameter) / 2, topCapHeight: Int(diameter) / 2) })?.stretchableImage(withLeftCapWidth: Int(diameter) / 2, topCapHeight: Int(diameter) / 2)
if let image = image { if let image = image {
if let backgroundColor = backgroundColor { if let backgroundColor = backgroundColor {
currentTextInputBackgroundImage = (backgroundColor, strokeColor, diameter, image) currentTextInputBackgroundImage = (backgroundColor, strokeColor, diameter, strokeWidth, image)
} }
return image return image
} else { } else {
@ -1290,7 +1289,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
if self.theme == nil || !self.theme!.chat.inputPanel.inputTextColor.isEqual(interfaceState.theme.chat.inputPanel.inputTextColor) { if self.theme == nil || !self.theme!.chat.inputPanel.inputTextColor.isEqual(interfaceState.theme.chat.inputPanel.inputTextColor) {
let textColor = interfaceState.theme.chat.inputPanel.inputTextColor let textColor = interfaceState.theme.chat.inputPanel.inputTextColor
let tintColor = interfaceState.theme.list.itemAccentColor
let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize)
if let textInputNode = self.textInputNode { if let textInputNode = self.textInputNode {
@ -1302,12 +1300,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
textInputNode.selectedRange = range textInputNode.selectedRange = range
} }
textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(baseFontSize), NSAttributedString.Key.foregroundColor.rawValue: textColor] textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(baseFontSize), NSAttributedString.Key.foregroundColor.rawValue: textColor]
textInputNode.tintColor = tintColor
self.updateSpoiler() self.updateSpoiler()
} }
} }
let tintColor = interfaceState.theme.list.itemAccentColor
if let textInputNode = self.textInputNode, tintColor != textInputNode.tintColor {
textInputNode.tintColor = tintColor
textInputNode.tintColorDidChange()
}
let keyboardAppearance = interfaceState.theme.rootController.keyboardColor.keyboardAppearance let keyboardAppearance = interfaceState.theme.rootController.keyboardColor.keyboardAppearance
if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance, textInputNode.isFirstResponder() { if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance, textInputNode.isFirstResponder() {
if textInputNode.isCurrentlyEmoji() { if textInputNode.isCurrentlyEmoji() {
@ -1332,15 +1335,18 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics) let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics)
let minimalInputHeight: CGFloat = 2.0 + textFieldMinHeight let minimalInputHeight: CGFloat = 2.0 + textFieldMinHeight
let strokeWidth: CGFloat
let backgroundColor: UIColor let backgroundColor: UIColor
if case let .color(color) = interfaceState.chatWallpaper, UIColor(rgb: color).isEqual(interfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper) { if case let .color(color) = interfaceState.chatWallpaper, UIColor(rgb: color).isEqual(interfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
backgroundColor = interfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper backgroundColor = interfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper
strokeWidth = 1.0 - UIScreenPixel
} else { } else {
backgroundColor = interfaceState.theme.chat.inputPanel.panelBackgroundColor backgroundColor = interfaceState.theme.chat.inputPanel.panelBackgroundColor
strokeWidth = UIScreenPixel
} }
self.textInputBackgroundNode.image = textInputBackgroundImage(backgroundColor: backgroundColor, inputBackgroundColor: nil, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight) self.textInputBackgroundNode.image = textInputBackgroundImage(backgroundColor: backgroundColor, inputBackgroundColor: nil, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight, strokeWidth: strokeWidth)
self.transparentTextInputBackgroundImage = textInputBackgroundImage(backgroundColor: nil, inputBackgroundColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight) self.transparentTextInputBackgroundImage = textInputBackgroundImage(backgroundColor: nil, inputBackgroundColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight, strokeWidth: strokeWidth)
self.textInputContainerBackgroundNode.image = generateStretchableFilledCircleImage(diameter: minimalInputHeight, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor) self.textInputContainerBackgroundNode.image = generateStretchableFilledCircleImage(diameter: minimalInputHeight, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor)
self.searchLayoutClearImageNode.image = PresentationResourcesChat.chatInputTextFieldClearImage(interfaceState.theme) self.searchLayoutClearImageNode.image = PresentationResourcesChat.chatInputTextFieldClearImage(interfaceState.theme)

View File

@ -162,6 +162,26 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
} }
dismissInput() dismissInput()
navigationController?.pushViewController(controller) navigationController?.pushViewController(controller)
case let .gameStart(peerId, game):
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyManageable, .excludeDisabled, .excludeRecent, .doNotSearchMessages], hasContactSelector: false, title: presentationData.strings.Bot_AddToChat_Title, selectForumThreads: true))
controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
if let peer = peer as? TelegramUser {
text = presentationData.strings.Target_ShareGameConfirmationPrivate(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
} else {
text = presentationData.strings.Target_ShareGameConfirmationGroup(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
}
let alertController = textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.RequestPeer_SelectionConfirmationSend, action: {
controller?.dismiss()
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
})])
present(alertController, nil)
}
dismissInput()
navigationController?.pushViewController(controller)
case let .channelMessage(peer, messageId, timecode): case let .channelMessage(peer, messageId, timecode):
openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil)) openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil))
case let .replyThreadMessage(replyThreadMessage, messageId): case let .replyThreadMessage(replyThreadMessage, messageId):

View File

@ -2495,7 +2495,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if actions.options.contains(.deleteGlobally) { if actions.options.contains(.deleteGlobally) {
let globalTitle: String let globalTitle: String
if isChannel { if isChannel {
globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone
} else if let personalPeerName = personalPeerName { } else if let personalPeerName = personalPeerName {
globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesFor(personalPeerName).string globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesFor(personalPeerName).string
} else { } else {

View File

@ -67,6 +67,7 @@ public enum ParsedInternalPeerUrlParameter {
case botStart(String) case botStart(String)
case groupBotStart(String, ResolvedBotAdminRights?) case groupBotStart(String, ResolvedBotAdminRights?)
case attachBotStart(String, String?) case attachBotStart(String, String?)
case gameStart(String)
case channelMessage(Int32, Double?) case channelMessage(Int32, Double?)
case replyThread(Int32, Int32) case replyThread(Int32, Int32)
case voiceChat(String?) case voiceChat(String?)
@ -212,7 +213,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
} }
return .peer(.name(peerName), .groupBotStart(value, botAdminRights)) return .peer(.name(peerName), .groupBotStart(value, botAdminRights))
} else if queryItem.name == "game" { } else if queryItem.name == "game" {
return nil return .peer(.name(peerName), .gameStart(value))
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) { } else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
return .peer(.name(peerName), .voiceChat(value)) return .peer(.name(peerName), .voiceChat(value))
} else if queryItem.name == "startattach" { } else if queryItem.name == "startattach" {
@ -582,6 +583,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .single(.botStart(peer: peer, payload: payload)) return .single(.botStart(peer: peer, payload: payload))
case let .groupBotStart(payload, adminRights): case let .groupBotStart(payload, adminRights):
return .single(.groupBotStart(peerId: peer.id, payload: payload, adminRights: adminRights)) return .single(.groupBotStart(peerId: peer.id, payload: payload, adminRights: adminRights))
case let .gameStart(game):
return .single(.gameStart(peerId: peer.id, game: game))
case let .attachBotStart(name, payload): case let .attachBotStart(name, payload):
return context.engine.peers.resolvePeerByName(name: name) return context.engine.peers.resolvePeerByName(name: name)
|> take(1) |> take(1)