mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge commit 'bf3d4cecf82886baa0c154954423e12c80984601'
This commit is contained in:
commit
d93a143501
@ -8930,3 +8930,7 @@ Sorry for the inconvenience.";
|
||||
"VoiceOver.Chat.NotPlayedByRecipients" = "Not played by recipients";
|
||||
|
||||
"VoiceOver.Chat.ReplyingToMessage" = "In reply to message: %@";
|
||||
|
||||
"MediaPicker.VoiceOver.Camera" = "Camera";
|
||||
|
||||
"ChatList.ReadAll" = "Read All";
|
||||
|
@ -277,6 +277,7 @@ public enum ResolvedUrl {
|
||||
case inaccessiblePeer
|
||||
case botStart(peer: Peer, payload: String)
|
||||
case groupBotStart(peerId: PeerId, payload: String, adminRights: ResolvedBotAdminRights?)
|
||||
case gameStart(peerId: PeerId, game: String)
|
||||
case channelMessage(peer: Peer, messageId: MessageId, timecode: Double?)
|
||||
case replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage, messageId: MessageId)
|
||||
case replyThread(messageId: MessageId)
|
||||
|
@ -623,6 +623,9 @@ public protocol ChatController: ViewController {
|
||||
func hintPlayNextOutgoingGift()
|
||||
|
||||
var isSendButtonVisible: Bool { get }
|
||||
|
||||
var isSelectingMessagesUpdated: ((Bool) -> Void)? { get set }
|
||||
func cancelSelectingMessages()
|
||||
}
|
||||
|
||||
public protocol ChatMessagePreviewItemNode: AnyObject {
|
||||
|
@ -96,7 +96,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
|
||||
let theme = self.presentationData.theme
|
||||
self.displayNode = AuthorizationSequenceSignUpControllerNode(theme: theme, strings: self.presentationData.strings, addPhoto: { [weak self] in
|
||||
presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: false, theme: theme, present: { c, a in
|
||||
presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: true, theme: theme, present: { c, a in
|
||||
self?.view.endEditing(true)
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, openCurrent: nil, completion: { image in
|
||||
|
@ -968,7 +968,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
strongSelf.setInlineChatList(location: .forum(peerId: channel.id))
|
||||
}
|
||||
} else {
|
||||
if let threadId = threadId {
|
||||
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId {
|
||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, keepStack: .never).start()
|
||||
strongSelf.chatListDisplayNode.clearHighlightAnimated(true)
|
||||
} else {
|
||||
@ -1069,7 +1069,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if case .chatList(.root) = strongSelf.location {
|
||||
navigationAnimationOptions = .removeOnMasterDetails
|
||||
}
|
||||
if let threadId = threadId {
|
||||
if case let .channel(channel) = actualPeer, channel.flags.contains(.isForum), let threadId {
|
||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: nil, keepStack: .never).start()
|
||||
} else {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeer), subject: .message(id: .id(messageId), highlight: true, timecode: nil), purposefulAction: {
|
||||
@ -1102,7 +1102,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if case .chatList(.root) = strongSelf.location {
|
||||
navigationAnimationOptions = .removeOnMasterDetails
|
||||
}
|
||||
if let threadId = threadId {
|
||||
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId {
|
||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, keepStack: .never).start()
|
||||
} else {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), purposefulAction: { [weak self] in
|
||||
@ -1513,6 +1513,26 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
})
|
||||
})))
|
||||
|
||||
if let filterEntries = strongSelf.tabContainerData?.0 {
|
||||
for filter in filterEntries {
|
||||
if case let .filter(filterId, _, unread) = filter, filterId == id {
|
||||
if unread.value > 0 {
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ReadAll, textColor: .primary, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { c, f in
|
||||
c.dismiss(completion: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.readAllInFilter(id: id)
|
||||
})
|
||||
})))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_RemoveFolder, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||
}, action: { c, f in
|
||||
@ -2579,6 +2599,25 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
})
|
||||
}
|
||||
|
||||
private func readAllInFilter(id: Int32) {
|
||||
guard case let .chatList(groupId) = self.chatListDisplayNode.effectiveContainerNode.location else {
|
||||
return
|
||||
}
|
||||
for filter in self.chatListDisplayNode.mainContainerNode.availableFilters {
|
||||
if case let .filter(filter) = filter, case let .filter(filterId, _, _, data) = filter, filterId == id {
|
||||
let filterPredicate = chatListFilterPredicate(filter: data)
|
||||
var markItems: [(groupId: EngineChatList.Group, filterPredicate: ChatListFilterPredicate?)] = []
|
||||
markItems.append((groupId, filterPredicate))
|
||||
for additionalGroupId in filterPredicate.includeAdditionalPeerGroupIds {
|
||||
markItems.append((EngineChatList.Group(additionalGroupId), filterPredicate))
|
||||
}
|
||||
|
||||
let _ = self.context.engine.messages.markAllChatsAsReadInteractively(items: markItems).start()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func askForFilterRemoval(id: Int32) {
|
||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
|
||||
|
@ -1381,14 +1381,18 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
savedMessages = true
|
||||
} else {
|
||||
if displayPeers.count == 1, let peer = displayPeers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
|
||||
} else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string
|
||||
} else if let peer = displayPeers.first {
|
||||
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
|
@ -2461,7 +2461,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
if let strongSelf = self {
|
||||
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)))
|
||||
|
||||
if strongSelf.backgroundColor != nil {
|
||||
strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor
|
||||
}
|
||||
strongSelf.listNode.forEachItemHeaderNode({ itemHeaderNode in
|
||||
if let itemHeaderNode = itemHeaderNode as? ChatListSearchItemHeaderNode {
|
||||
itemHeaderNode.updateTheme(theme: presentationData.theme)
|
||||
|
@ -1648,6 +1648,10 @@ public final class ChatListNode: ListView {
|
||||
guard !filter.contains(.onlyPrivateChats) || peer.peerId.namespace == Namespaces.Peer.CloudUser else { return false }
|
||||
|
||||
if let peer = peer.peer {
|
||||
if peer.id.isReplies {
|
||||
return false
|
||||
}
|
||||
|
||||
switch peer {
|
||||
case let .user(user):
|
||||
if user.botInfo != nil {
|
||||
|
@ -175,8 +175,9 @@ public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatL
|
||||
}
|
||||
for peerId in data.excludePeers {
|
||||
if let (tag, peerCount, _, groupIdValue, isMuted) = peerTagAndCount[peerId], peerCount != 0, let groupId = groupIdValue {
|
||||
var matches = true
|
||||
var matches = false
|
||||
if tags.contains(tag) {
|
||||
matches = true
|
||||
if isMuted && data.excludeMuted {
|
||||
matches = false
|
||||
}
|
||||
|
@ -789,10 +789,14 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
case .none:
|
||||
break
|
||||
case let .presence(presence, dateTimeFormat):
|
||||
userPresence = presence
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
let (string, activity) = stringAndActivityForUserPresence(strings: item.presentationData.strings, dateTimeFormat: dateTimeFormat, presence: presence, relativeTo: Int32(timestamp))
|
||||
statusAttributedString = NSAttributedString(string: string, font: statusFont, textColor: activity ? item.presentationData.theme.list.itemAccentColor : item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
if case let .peer(peer, _) = item.peer, let peer, case let .user(user) = peer, user.botInfo != nil {
|
||||
statusAttributedString = NSAttributedString(string: item.presentationData.strings.Bot_GenericBotStatus, font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
} else {
|
||||
userPresence = presence
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
let (string, activity) = stringAndActivityForUserPresence(strings: item.presentationData.strings, dateTimeFormat: dateTimeFormat, presence: presence, relativeTo: Int32(timestamp))
|
||||
statusAttributedString = NSAttributedString(string: string, font: statusFont, textColor: activity ? item.presentationData.theme.list.itemAccentColor : item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
}
|
||||
case let .addressName(suffix):
|
||||
if let addressName = peer.addressName {
|
||||
let addressNameString = NSAttributedString(string: "@" + addressName, font: statusFont, textColor: item.presentationData.theme.list.itemAccentColor)
|
||||
|
@ -994,15 +994,16 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if let _ = self.presentationNode {
|
||||
self.currentPresentationStateTransition = .animateOut(result: initialResult, completion: completion)
|
||||
if let validLayout = self.validLayout {
|
||||
if case .custom = initialResult {
|
||||
if case let .custom(transition) = initialResult {
|
||||
self.delayLayoutUpdate = true
|
||||
Queue.mainQueue().after(0.05) {
|
||||
Queue.mainQueue().after(0.1) {
|
||||
self.delayLayoutUpdate = false
|
||||
self.updateLayout(
|
||||
layout: validLayout,
|
||||
transition: .animated(duration: 0.35, curve: .easeInOut),
|
||||
transition: transition,
|
||||
previousActionsContainerNode: nil
|
||||
)
|
||||
self.isAnimatingOut = true
|
||||
}
|
||||
} else {
|
||||
self.updateLayout(
|
||||
|
@ -80,3 +80,7 @@ public func isBoldTextEnabled() -> Signal<Bool, NoError> {
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
}
|
||||
|
||||
public func isReduceTransparencyEnabled() -> Bool {
|
||||
UIAccessibility.isReduceTransparencyEnabled
|
||||
}
|
||||
|
@ -492,7 +492,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
|
||||
let effectiveWidth = gridLayout.size.width - itemInsets.left - itemInsets.right
|
||||
|
||||
let itemsInRow = Int(effectiveWidth / defaultItemSize.width)
|
||||
let itemsInRow = max(1, Int(effectiveWidth / defaultItemSize.width))
|
||||
let itemsInRowWidth = CGFloat(itemsInRow) * defaultItemSize.width
|
||||
let remainingWidth = max(0.0, effectiveWidth - itemsInRowWidth)
|
||||
|
||||
|
@ -713,6 +713,8 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
itemSize.width -= 60.0
|
||||
insets.left += 30.0
|
||||
insets.right += 30.0
|
||||
} else {
|
||||
itemSize.width -= layout.safeInsets.left + layout.safeInsets.right
|
||||
}
|
||||
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: UIEdgeInsets(top: insets.top, left: insets.left + layout.safeInsets.left, bottom: insets.bottom + layout.safeInsets.bottom, right: insets.right + layout.safeInsets.right), preloadSize: 300.0, type: .fixed(itemSize: itemSize, fillWidth: nil, lineSpacing: 0.0, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
@ -1405,14 +1405,18 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
@ -1473,14 +1477,18 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
@ -1621,14 +1629,18 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
|
@ -152,7 +152,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = HashtagSearchControllerNode(context: self.context, peer: self.peer, query: self.query, navigationBar: self.navigationBar, navigationController: self.navigationController as? NavigationController)
|
||||
self.displayNode = HashtagSearchControllerNode(context: self.context, controller: self, peer: self.peer, query: self.query, navigationBar: self.navigationBar, navigationController: self.navigationController as? NavigationController)
|
||||
if let chatController = self.controllerNode.chatController {
|
||||
chatController.parentController = self
|
||||
}
|
||||
|
@ -9,6 +9,9 @@ import SegmentedControlNode
|
||||
import ChatListSearchItemHeader
|
||||
|
||||
final class HashtagSearchControllerNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private weak var controller: HashtagSearchController?
|
||||
|
||||
private let navigationBar: NavigationBar?
|
||||
|
||||
private let segmentedControlNode: SegmentedControlNode
|
||||
@ -16,15 +19,16 @@ final class HashtagSearchControllerNode: ASDisplayNode {
|
||||
|
||||
let chatController: ChatController?
|
||||
|
||||
private let context: AccountContext
|
||||
|
||||
private let query: String
|
||||
|
||||
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
||||
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
|
||||
private var hasValidLayout = false
|
||||
|
||||
init(context: AccountContext, peer: EnginePeer?, query: String, navigationBar: NavigationBar?, navigationController: NavigationController?) {
|
||||
init(context: AccountContext, controller: HashtagSearchController, peer: EnginePeer?, query: String, navigationBar: NavigationBar?, navigationController: NavigationController?) {
|
||||
self.context = context
|
||||
self.controller = controller
|
||||
self.query = query
|
||||
self.navigationBar = navigationBar
|
||||
|
||||
@ -74,6 +78,17 @@ final class HashtagSearchControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.chatController?.isSelectingMessagesUpdated = { [weak self] isSelecting in
|
||||
if let strongSelf = self {
|
||||
let button: UIBarButtonItem? = isSelecting ? UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .done, target: self, action: #selector(strongSelf.cancelPressed)) : nil
|
||||
strongSelf.controller?.navigationItem.setRightBarButton(button, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.chatController?.cancelSelectingMessages()
|
||||
}
|
||||
|
||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
|
@ -544,122 +544,142 @@ public final class InviteLinkViewController: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
UIPasteboard.general.string = invite.link
|
||||
|
||||
self?.controller?.dismissAllTooltips()
|
||||
|
||||
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))
|
||||
})))
|
||||
var creatorIsBot: Signal<Bool, NoError>
|
||||
if case let .link(_, _, _, _, _, adminId, _, _, _, _, _, _) = invite {
|
||||
creatorIsBot = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: adminId))
|
||||
|> map { peer -> Bool in
|
||||
if let peer, case let .user(user) = peer, user.botInfo != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
creatorIsBot = .single(false)
|
||||
}
|
||||
|
||||
if invite.isRevoked {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextDelete, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
let _ = (creatorIsBot
|
||||
|> take(1)
|
||||
|> 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
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
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))
|
||||
UIPasteboard.general.string = invite.link
|
||||
|
||||
self?.controller?.dismissAllTooltips()
|
||||
|
||||
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))
|
||||
})))
|
||||
} else {
|
||||
if !invitationAvailability(invite).isZero {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
|
||||
if invite.isRevoked {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextDelete, 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: { [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 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: {
|
||||
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.revokePeerExportedInvitation(peerId: peerId, link: inviteLink) |> deliverOnMainQueue).start(next: { result in
|
||||
if case let .replace(_, newInvite) = result {
|
||||
self?.controller?.invitationsContext?.add(newInvite)
|
||||
}
|
||||
})
|
||||
let _ = (context.engine.peers.deletePeerExportedInvitation(peerId: peerId, link: inviteLink) |> deliverOnMainQueue).start()
|
||||
}
|
||||
|
||||
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))
|
||||
self?.controller?.revokedInvitationsContext?.remove(invite)
|
||||
})
|
||||
]),
|
||||
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)
|
||||
})))
|
||||
} else {
|
||||
if !invitationAvailability(invite).isZero {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, 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)
|
||||
|
@ -45,8 +45,6 @@
|
||||
|
||||
@property (nonatomic, copy) void(^recognizedQRCode)(NSString *value, AVMetadataMachineReadableCodeObject *object);
|
||||
|
||||
@property (nonatomic, assign) bool compressVideo;
|
||||
|
||||
- (instancetype)initWithMode:(PGCameraMode)mode position:(PGCameraPosition)position;
|
||||
|
||||
- (void)performInitialConfigurationWithCompletion:(void (^)(void))completion;
|
||||
|
@ -49,7 +49,7 @@
|
||||
@interface TGMediaVideoConversionPresetSettings : NSObject
|
||||
|
||||
+ (CGSize)maximumSizeForPreset:(TGMediaVideoConversionPreset)preset;
|
||||
+ (NSDictionary *)videoSettingsForPreset:(TGMediaVideoConversionPreset)preset dimensions:(CGSize)dimensions;
|
||||
+ (NSDictionary *)videoSettingsForPreset:(TGMediaVideoConversionPreset)preset dimensions:(CGSize)dimensions frameRate:(int32_t)frameRate;
|
||||
+ (NSDictionary *)audioSettingsForPreset:(TGMediaVideoConversionPreset)preset;
|
||||
|
||||
@end
|
||||
|
@ -86,7 +86,7 @@ typedef enum {
|
||||
|
||||
- (void)setToolbarHidden:(bool)hidden animated:(bool)animated;
|
||||
|
||||
+ (TGPhotoEditorTab)defaultTabsForAvatarIntent;
|
||||
+ (TGPhotoEditorTab)defaultTabsForAvatarIntent:(bool)hasStickers;
|
||||
|
||||
- (NSTimeInterval)currentTime;
|
||||
- (void)setMinimalVideoDuration:(NSTimeInterval)duration;
|
||||
|
@ -314,11 +314,13 @@ const NSInteger PGCameraFrameRate = 30;
|
||||
|
||||
- (void)switchToBestVideoFormatForDevice:(AVCaptureDevice *)device
|
||||
{
|
||||
bool preferZoomableFormat = [self hasTelephotoCamera] || [self hasUltrawideCamera];
|
||||
[self _reconfigureDevice:device withBlock:^(AVCaptureDevice *device)
|
||||
{
|
||||
NSArray *availableFormats = device.formats;
|
||||
AVCaptureDeviceFormat *preferredFormat = nil;
|
||||
NSMutableArray *maybeFormats = nil;
|
||||
bool hasSecondaryZoomLevels = false;
|
||||
int32_t maxWidth = 0;
|
||||
int32_t maxHeight = 0;
|
||||
for (AVCaptureDeviceFormat *format in availableFormats)
|
||||
@ -329,8 +331,10 @@ const NSInteger PGCameraFrameRate = 30;
|
||||
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
|
||||
if (dimensions.width >= maxWidth && dimensions.width <= 1920 && dimensions.height >= maxHeight && dimensions.height <= 1080)
|
||||
{
|
||||
if (dimensions.width > maxWidth)
|
||||
if (dimensions.width > maxWidth) {
|
||||
hasSecondaryZoomLevels = false;
|
||||
maybeFormats = [[NSMutableArray alloc] init];
|
||||
}
|
||||
FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(format.formatDescription);
|
||||
if (mediaSubType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
|
||||
{
|
||||
@ -347,8 +351,14 @@ const NSInteger PGCameraFrameRate = 30;
|
||||
}
|
||||
}
|
||||
|
||||
if (supportedRate)
|
||||
[maybeFormats addObject:format];
|
||||
if (supportedRate) {
|
||||
if (iosMajorVersion() >= 16 && format.secondaryNativeResolutionZoomFactors.count > 0) {
|
||||
hasSecondaryZoomLevels = true;
|
||||
[maybeFormats addObject:format];
|
||||
} else if (!hasSecondaryZoomLevels) {
|
||||
[maybeFormats addObject:format];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -394,7 +404,11 @@ const NSInteger PGCameraFrameRate = 30;
|
||||
{
|
||||
AVCaptureConnection *videoConnection = [_videoOutput connectionWithMediaType:AVMediaTypeVideo];
|
||||
if (videoConnection.supportsVideoStabilization) {
|
||||
videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard;
|
||||
if (iosMajorVersion() >= 13) {
|
||||
videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeCinematicExtended;
|
||||
} else {
|
||||
videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeCinematic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -976,12 +990,6 @@ const NSInteger PGCameraFrameRate = 30;
|
||||
NSDictionary *videoSettings = [_videoOutput recommendedVideoSettingsForAssetWriterWithOutputFileType:AVFileTypeMPEG4];
|
||||
NSDictionary *audioSettings = [_audioOutput recommendedAudioSettingsForAssetWriterWithOutputFileType:AVFileTypeMPEG4];
|
||||
|
||||
if (self.compressVideo)
|
||||
{
|
||||
videoSettings = [TGMediaVideoConversionPresetSettings videoSettingsForPreset:TGMediaVideoConversionPresetCompressedMedium dimensions:CGSizeMake(848, 480)];
|
||||
audioSettings = [TGMediaVideoConversionPresetSettings audioSettingsForPreset:TGMediaVideoConversionPresetCompressedMedium];
|
||||
}
|
||||
|
||||
_movieWriter = [[PGCameraMovieWriter alloc] initWithVideoTransform:TGTransformForVideoOrientation(orientation, mirrored) videoOutputSettings:videoSettings audioOutputSettings:audioSettings];
|
||||
_movieWriter.finishedWithMovieAtURL = completion;
|
||||
[_movieWriter startRecording];
|
||||
|
@ -919,7 +919,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
||||
intent |= TGPhotoEditorControllerSuggestingAvatarIntent;
|
||||
}
|
||||
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:intent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:intent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent:!_disableStickers] selectedTab:TGPhotoEditorCropTab];
|
||||
controller.editingContext = _editingContext;
|
||||
controller.stickersContext = _stickersContext;
|
||||
controller.dontHideStatusBar = true;
|
||||
|
@ -1986,7 +1986,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
||||
if (_intent == TGCameraControllerSignupAvatarIntent) {
|
||||
intent = TGPhotoEditorControllerSignupAvatarIntent;
|
||||
}
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:input intent:(TGPhotoEditorControllerFromCameraIntent | intent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:input intent:(TGPhotoEditorControllerFromCameraIntent | intent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent:_intent != TGCameraControllerSignupAvatarIntent] selectedTab:TGPhotoEditorCropTab];
|
||||
controller.stickersContext = _stickersContext;
|
||||
__weak TGPhotoEditorController *weakController = controller;
|
||||
controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
|
||||
|
@ -448,7 +448,7 @@
|
||||
editableItem = [[TGCameraCapturedVideo alloc] initWithAsset:asset livePhoto:false];
|
||||
}
|
||||
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:_context item:editableItem intent:intent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:_context item:editableItem intent:intent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent:_intent != TGMediaAssetsControllerSetSignupProfilePhotoIntent] selectedTab:TGPhotoEditorCropTab];
|
||||
controller.stickersContext = self.stickersContext;
|
||||
controller.editingContext = self.editingContext;
|
||||
controller.didFinishRenderingFullSizeImage = ^(UIImage *resultImage)
|
||||
|
@ -368,18 +368,18 @@
|
||||
CMTime frameDuration30FPS = CMTimeMake(1, 30);
|
||||
CMTime frameDuration = frameDuration30FPS;
|
||||
if (videoTrack.nominalFrameRate > 0)
|
||||
frameDuration = CMTimeMake(1, (int32_t)videoTrack.nominalFrameRate);
|
||||
frameDuration = CMTimeMake(1, (int32_t)ceil(videoTrack.nominalFrameRate));
|
||||
else if (CMTimeCompare(videoTrack.minFrameDuration, kCMTimeZero) == 1)
|
||||
frameDuration = videoTrack.minFrameDuration;
|
||||
|
||||
if (CMTimeCompare(frameDuration, kCMTimeZero) != 1 || !CMTIME_IS_VALID(frameDuration) || image != nil || entityRenderer != nil || adjustments.toolsApplied)
|
||||
frameDuration = frameDuration30FPS;
|
||||
|
||||
if (CMTimeCompare(frameDuration, frameDuration30FPS)) {
|
||||
if (CMTimeCompare(frameDuration, frameDuration30FPS) && preset != TGMediaVideoConversionPresetCompressedVeryHigh) {
|
||||
frameDuration = frameDuration30FPS;
|
||||
}
|
||||
|
||||
NSInteger fps = (NSInteger)(1.0 / CMTimeGetSeconds(frameDuration));
|
||||
NSInteger fps = (NSInteger)ceil(1.0 / CMTimeGetSeconds(frameDuration));
|
||||
|
||||
UIImage *overlayImage = nil;
|
||||
if (adjustments.paintingData.imagePath != nil)
|
||||
@ -536,7 +536,7 @@
|
||||
videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
|
||||
}
|
||||
|
||||
NSDictionary *settings = [TGMediaVideoConversionPresetSettings videoSettingsForPreset:preset dimensions:outputDimensions];
|
||||
NSDictionary *settings = [TGMediaVideoConversionPresetSettings videoSettingsForPreset:preset dimensions:outputDimensions frameRate:(int32_t)fps];
|
||||
*outputSettings = settings;
|
||||
*dimensions = outputDimensions;
|
||||
|
||||
@ -1303,7 +1303,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
|
||||
};
|
||||
}
|
||||
|
||||
+ (NSDictionary *)videoSettingsForPreset:(TGMediaVideoConversionPreset)preset dimensions:(CGSize)dimensions
|
||||
+ (NSDictionary *)videoSettingsForPreset:(TGMediaVideoConversionPreset)preset dimensions:(CGSize)dimensions frameRate:(int32_t)frameRate
|
||||
{
|
||||
NSDictionary *videoCleanApertureSettings = @
|
||||
{
|
||||
@ -1324,7 +1324,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
|
||||
AVVideoAverageBitRateKey: @([self _videoBitrateKbpsForPreset:preset] * 1000),
|
||||
AVVideoCleanApertureKey: videoCleanApertureSettings,
|
||||
AVVideoPixelAspectRatioKey: videoAspectRatioSettings,
|
||||
AVVideoExpectedSourceFrameRateKey: @30
|
||||
AVVideoExpectedSourceFrameRateKey: @(frameRate)
|
||||
};
|
||||
|
||||
NSDictionary *hdVideoProperties = @
|
||||
@ -1364,13 +1364,13 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
|
||||
return 700;
|
||||
|
||||
case TGMediaVideoConversionPresetCompressedMedium:
|
||||
return 1100;
|
||||
return 1600;
|
||||
|
||||
case TGMediaVideoConversionPresetCompressedHigh:
|
||||
return 2500;
|
||||
return 3000;
|
||||
|
||||
case TGMediaVideoConversionPresetCompressedVeryHigh:
|
||||
return 4000;
|
||||
return 6600;
|
||||
|
||||
case TGMediaVideoConversionPresetVideoMessage:
|
||||
return 1000;
|
||||
|
@ -93,10 +93,13 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
|
||||
[_wrapperView addSubview:_fullPaintingView];
|
||||
|
||||
_entitiesWrapperView = [[UIView alloc] init];
|
||||
_fullEntitiesView = fullEntitiesView;
|
||||
_fullEntitiesView.frame = CGRectMake(0.0, 0.0, _fullEntitiesView.frame.size.width, _fullEntitiesView.frame.size.height);
|
||||
_entitiesWrapperView.frame = _fullEntitiesView.frame;
|
||||
|
||||
if (fullEntitiesView != nil) {
|
||||
_fullEntitiesView = fullEntitiesView;
|
||||
_fullEntitiesView.frame = CGRectMake(0.0, 0.0, _fullEntitiesView.frame.size.width, _fullEntitiesView.frame.size.height);
|
||||
_entitiesWrapperView.frame = _fullEntitiesView.frame;
|
||||
} else {
|
||||
_entitiesWrapperView.frame = CGRectMake(0.0, 0.0, _fullPreviewView.frame.size.width, _fullPreviewView.frame.size.height);
|
||||
}
|
||||
CGFloat entitiesScale = _fullPreviewView.frame.size.width / _entitiesWrapperView.frame.size.width;
|
||||
_entitiesWrapperView.transform = CGAffineTransformMakeScale(entitiesScale, entitiesScale);
|
||||
_entitiesWrapperView.frame = _fullPreviewView.frame;
|
||||
|
@ -951,7 +951,11 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
|
||||
|
||||
- (TGPhotoEditorTab)availableTabs
|
||||
{
|
||||
return TGPhotoEditorRotateTab | TGPhotoEditorMirrorTab | TGPhotoEditorPaintTab | TGPhotoEditorToolsTab;
|
||||
TGPhotoEditorTab tabs = TGPhotoEditorRotateTab | TGPhotoEditorMirrorTab | TGPhotoEditorToolsTab;
|
||||
if (self.intent != TGPhotoEditorControllerSignupAvatarIntent) {
|
||||
tabs |= TGPhotoEditorPaintTab;
|
||||
}
|
||||
return tabs;
|
||||
}
|
||||
|
||||
- (TGPhotoEditorTab)activeTab
|
||||
|
@ -2604,15 +2604,12 @@
|
||||
}]];
|
||||
}
|
||||
|
||||
+ (TGPhotoEditorTab)defaultTabsForAvatarIntent
|
||||
+ (TGPhotoEditorTab)defaultTabsForAvatarIntent:(bool)hasStickers
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static TGPhotoEditorTab avatarTabs = TGPhotoEditorNoneTab;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
if (iosMajorVersion() >= 7)
|
||||
avatarTabs = TGPhotoEditorCropTab | TGPhotoEditorPaintTab | TGPhotoEditorToolsTab;
|
||||
});
|
||||
TGPhotoEditorTab avatarTabs = TGPhotoEditorCropTab | TGPhotoEditorToolsTab;
|
||||
if (hasStickers) {
|
||||
avatarTabs |= TGPhotoEditorPaintTab;
|
||||
}
|
||||
return avatarTabs;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
}
|
||||
|
||||
void (^present)(UIImage *) = ^(UIImage *screenImage) {
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:TGPhotoEditorControllerAvatarIntent | TGPhotoEditorControllerSuggestedAvatarIntent adjustments:nil caption:nil screenImage:screenImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:TGPhotoEditorControllerAvatarIntent | TGPhotoEditorControllerSuggestedAvatarIntent adjustments:nil caption:nil screenImage:screenImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent:true] selectedTab:TGPhotoEditorCropTab];
|
||||
controller.senderName = senderName;
|
||||
controller.stickersContext = stickersContext;
|
||||
|
||||
|
@ -695,7 +695,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16;
|
||||
_videoTransform = [self transformForOrientation:self.orientation];
|
||||
|
||||
CGSize size = [TGMediaVideoConversionPresetSettings maximumSizeForPreset:preset];
|
||||
NSDictionary *videoSettings = [TGMediaVideoConversionPresetSettings videoSettingsForPreset:preset dimensions:size];
|
||||
NSDictionary *videoSettings = [TGMediaVideoConversionPresetSettings videoSettingsForPreset:preset dimensions:size frameRate:30];
|
||||
[recorder addVideoTrackWithSourceFormatDescription:self.outputVideoFormatDescription transform:CGAffineTransformIdentity settings:videoSettings];
|
||||
_recorder = recorder;
|
||||
|
||||
|
@ -423,14 +423,14 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender
|
||||
self.isAvatar = ((adjustments as? TGVideoEditAdjustments)?.documentId ?? 0) != 0
|
||||
|
||||
var renderEntities: [LegacyPaintEntity] = []
|
||||
if let account = account, let paintingData = adjustments.paintingData, let entitiesData = paintingData.entitiesData {
|
||||
if let paintingData = adjustments.paintingData, let entitiesData = paintingData.entitiesData {
|
||||
let entities = decodeDrawingEntities(data: entitiesData)
|
||||
for entity in entities {
|
||||
if let sticker = entity as? DrawingStickerEntity {
|
||||
if let sticker = entity as? DrawingStickerEntity, let account {
|
||||
renderEntities.append(LegacyPaintStickerEntity(account: account, entity: sticker))
|
||||
} else if let text = entity as? DrawingTextEntity {
|
||||
renderEntities.append(LegacyPaintTextEntity(entity: text))
|
||||
if let renderSubEntities = text.renderSubEntities {
|
||||
if let renderSubEntities = text.renderSubEntities, let account {
|
||||
for entity in renderSubEntities {
|
||||
renderEntities.append(LegacyPaintStickerEntity(account: account, entity: entity))
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public extension EnginePeer {
|
||||
} else if let _ = user.phone {
|
||||
return "" //formatPhoneNumber("+\(phone)")
|
||||
} else {
|
||||
return ""
|
||||
return "Deleted Account"
|
||||
}
|
||||
case let .legacyGroup(group):
|
||||
return group.title
|
||||
|
@ -238,28 +238,29 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
|
||||
}
|
||||
|
||||
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
|
||||
model?.dismiss(true, false)
|
||||
dismissAll()
|
||||
}
|
||||
controller.send = {
|
||||
sheetController.send = {
|
||||
completed(item.asset, false, nil, {
|
||||
dismissImpl()
|
||||
})
|
||||
}
|
||||
controller.sendSilently = {
|
||||
sheetController.sendSilently = {
|
||||
completed(item.asset, true, nil, {
|
||||
dismissImpl()
|
||||
})
|
||||
}
|
||||
controller.schedule = {
|
||||
sheetController.schedule = {
|
||||
presentSchedulePicker(true, { time in
|
||||
completed(item.asset, false, time, {
|
||||
dismissImpl()
|
||||
})
|
||||
})
|
||||
}
|
||||
controller.sendWithTimer = {
|
||||
sheetController.sendWithTimer = {
|
||||
presentTimerPicker { time in
|
||||
var items = selectionContext.selectedItems() ?? []
|
||||
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.bind(controller: controller)
|
||||
legacySheetController.bind(controller: sheetController)
|
||||
present(legacySheetController, nil)
|
||||
|
||||
let hapticFeedback = HapticFeedback()
|
||||
|
@ -235,7 +235,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.scrollingArea = SparseItemGridScrollingArea()
|
||||
|
||||
self.cameraActivateAreaNode = AccessibilityAreaNode()
|
||||
self.cameraActivateAreaNode.accessibilityLabel = "Camera"
|
||||
self.cameraActivateAreaNode.accessibilityLabel = self.presentationData.strings.MediaPicker_VoiceOver_Camera
|
||||
self.cameraActivateAreaNode.accessibilityTraits = [.button]
|
||||
|
||||
super.init()
|
||||
|
@ -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 blurredBackgroundNode = NavigationBackgroundNode(color: blurredBackgroundColor.0, enableBlur: blurredBackgroundColor.1)
|
||||
self.blurredBackgroundNode = blurredBackgroundNode
|
||||
|
||||
if isReduceTransparencyEnabled() {
|
||||
blurredBackgroundNode.alpha = 0.1
|
||||
}
|
||||
}
|
||||
|
||||
self.backgroundNode = ASImageNode()
|
||||
|
@ -1100,11 +1100,11 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
||||
return
|
||||
}
|
||||
|
||||
if updateFlags != currentFlags {
|
||||
if let updateFlags, updateFlags != currentFlags {
|
||||
updateState { current in
|
||||
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
|
||||
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)
|
||||
}, completed: {
|
||||
updated(TelegramChatAdminRights(rights: updateFlags ?? []))
|
||||
updated(TelegramChatAdminRights(rights: updateFlags))
|
||||
dismissImpl?()
|
||||
}))
|
||||
} else if let updateRank = updateRank, let currentFlags = currentFlags {
|
||||
|
@ -908,7 +908,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
} else if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {
|
||||
selectedType = .publicChannel
|
||||
} else if case .initialSetup = mode {
|
||||
selectedType = .publicChannel
|
||||
selectedType = .privateChannel
|
||||
} else {
|
||||
selectedType = .privateChannel
|
||||
}
|
||||
@ -1803,6 +1803,15 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
|
||||
var footerItem: ItemListControllerFooterItem?
|
||||
|
||||
var isGroup = false
|
||||
if let peer = peer as? TelegramChannel {
|
||||
if case .group = peer.info {
|
||||
isGroup = true
|
||||
}
|
||||
} else if let _ = peer as? TelegramGroup {
|
||||
isGroup = true
|
||||
}
|
||||
|
||||
var rightNavigationButton: ItemListNavigationButton?
|
||||
if case .revokeNames = mode {
|
||||
let count = Int32(publicChannelsToRevoke?.count ?? 0)
|
||||
@ -1993,7 +2002,13 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
|
||||
_ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in
|
||||
if showAlert {
|
||||
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Group_Edit_PrivatePublicLinkAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil)
|
||||
let text: String
|
||||
if isGroup {
|
||||
text = presentationData.strings.Group_Edit_PrivatePublicLinkAlert
|
||||
} else {
|
||||
text = presentationData.strings.Channel_Edit_PrivatePublicLinkAlert
|
||||
}
|
||||
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil)
|
||||
} else {
|
||||
invokeAction()
|
||||
}
|
||||
@ -2013,16 +2028,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
if state.revokingPeerId != nil {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
||||
}
|
||||
|
||||
var isGroup = false
|
||||
if let peer = peer as? TelegramChannel {
|
||||
if case .group = peer.info {
|
||||
isGroup = true
|
||||
}
|
||||
} else if let _ = peer as? TelegramGroup {
|
||||
isGroup = true
|
||||
}
|
||||
|
||||
|
||||
let leftNavigationButton: ItemListNavigationButton?
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
|
@ -13,6 +13,9 @@ import AccountContext
|
||||
public func autodownloadDataSizeString(_ size: Int64, decimalSeparator: String = ".") -> String {
|
||||
if size >= 1024 * 1024 * 1024 {
|
||||
var remainder = (size % (1024 * 1024 * 1024)) / (1024 * 1024 * 102)
|
||||
if remainder == 10 {
|
||||
remainder = 9
|
||||
}
|
||||
while remainder != 0 && remainder % 10 == 0 {
|
||||
remainder /= 10
|
||||
}
|
||||
@ -24,6 +27,9 @@ public func autodownloadDataSizeString(_ size: Int64, decimalSeparator: String =
|
||||
}
|
||||
} else if size >= 1024 * 1024 {
|
||||
var remainder = (size % (1024 * 1024)) / (1024 * 102)
|
||||
if remainder == 10 {
|
||||
remainder = 9
|
||||
}
|
||||
while remainder != 0 && remainder % 10 == 0 {
|
||||
remainder /= 10
|
||||
}
|
||||
|
@ -400,7 +400,7 @@ public final class PendingMessageManager {
|
||||
return lhs.1.index < rhs.1.index
|
||||
}) {
|
||||
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
|
||||
switch contentToUpload {
|
||||
case let .immediate(result, type):
|
||||
|
@ -245,10 +245,11 @@ func cachedStickerPack(transaction: Transaction, reference: StickerPackReference
|
||||
}
|
||||
}
|
||||
case let .name(shortName):
|
||||
let shortName = shortName.lowercased()
|
||||
for namespace in namespaces {
|
||||
for info in transaction.getItemCollectionsInfos(namespace: namespace) {
|
||||
if let info = info.1 as? StickerPackCollectionInfo {
|
||||
if info.shortName == shortName {
|
||||
if info.shortName.lowercased() == shortName {
|
||||
let items = transaction.getItemCollectionItems(collectionId: info.id)
|
||||
if !items.isEmpty {
|
||||
return (info, items.compactMap { $0 as? StickerPackItem }, true)
|
||||
|
@ -623,7 +623,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
||||
|
||||
let inputPanel = PresentationThemeChatInputPanel(
|
||||
panelBackgroundColor: rootNavigationBar.blurredBackgroundColor,
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000, alpha: 0.94),
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000),
|
||||
panelSeparatorColor: UIColor(rgb: 0x545458, alpha: 0.55),
|
||||
panelControlAccentColor: UIColor(rgb: 0xffffff),
|
||||
panelControlColor: UIColor(rgb: 0x808080),
|
||||
|
@ -877,7 +877,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
|
||||
let inputPanel = PresentationThemeChatInputPanel(
|
||||
panelBackgroundColor: rootNavigationBar.blurredBackgroundColor,
|
||||
panelBackgroundColorNoWallpaper: rootNavigationBar.blurredBackgroundColor,
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff),
|
||||
panelSeparatorColor: UIColor(rgb: 0xb2b2b2),
|
||||
panelControlAccentColor: defaultDayAccentColor,
|
||||
panelControlColor: UIColor(rgb: 0x858e99),
|
||||
|
@ -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? {
|
||||
var attributedString: NSAttributedString?
|
||||
|
||||
@ -96,9 +124,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
let resultTitleString: PresentationStrings.FormattedString
|
||||
if peerIds.count == 1 {
|
||||
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 {
|
||||
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))
|
||||
@ -115,7 +143,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
if peerIds.count == 1 {
|
||||
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):
|
||||
if authorName.isEmpty || isChannel {
|
||||
@ -393,10 +421,18 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
string = strings.Conversation_AutoremoveTimerRemovedUser(authorString).string
|
||||
}
|
||||
} else if let _ = messagePeer as? TelegramGroup {
|
||||
string = strings.Conversation_AutoremoveTimerRemovedGroup
|
||||
if message.author?.id == accountPeerId {
|
||||
string = strings.Conversation_AutoremoveTimerRemovedUserYou
|
||||
} else {
|
||||
string = strings.Conversation_AutoremoveTimerRemovedGroup
|
||||
}
|
||||
} else if let channel = messagePeer as? TelegramChannel {
|
||||
if case .group = channel.info {
|
||||
string = strings.Conversation_AutoremoveTimerRemovedGroup
|
||||
if message.author?.id == accountPeerId {
|
||||
string = strings.Conversation_AutoremoveTimerRemovedUserYou
|
||||
} else {
|
||||
string = strings.Conversation_AutoremoveTimerRemovedGroup
|
||||
}
|
||||
} else {
|
||||
string = strings.Conversation_AutoremoveTimerRemovedChannel
|
||||
}
|
||||
@ -644,10 +680,10 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
resultTitleString = strings.Notification_VoiceChatInvitationForYou(authorName)
|
||||
} else {
|
||||
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 {
|
||||
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))
|
||||
|
@ -513,6 +513,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var updatedClosedPinnedMessageId: ((MessageId) -> Void)?
|
||||
var requestedUnpinAllMessages: ((Int, MessageId) -> Void)?
|
||||
|
||||
public var isSelectingMessagesUpdated: ((Bool) -> Void)?
|
||||
|
||||
private let scrolledToMessageId = ValuePromise<ScrolledToMessageId?>(nil, ignoreRepeated: true)
|
||||
private var scrolledToMessageIdValue: ScrolledToMessageId? = nil {
|
||||
didSet {
|
||||
@ -2625,14 +2627,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
@ -3057,6 +3063,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
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))
|
||||
|> deliverOnMainQueue).start(next: { message in
|
||||
guard let strongSelf = self, let message = message else {
|
||||
@ -4222,7 +4238,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: {
|
||||
|
||||
completion()
|
||||
@ -4234,15 +4250,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
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, {
|
||||
if case .user = peerType {
|
||||
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
|
||||
switch peerType {
|
||||
@ -11437,6 +11457,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.recordingActivityPromise.set(recordingActivityValue)
|
||||
}
|
||||
|
||||
if (self.presentationInterfaceState.interfaceState.selectionState == nil) != (updatedChatPresentationInterfaceState.interfaceState.selectionState == nil) {
|
||||
self.isSelectingMessagesUpdated?(updatedChatPresentationInterfaceState.interfaceState.selectionState != nil)
|
||||
}
|
||||
|
||||
self.presentationInterfaceState = updatedChatPresentationInterfaceState
|
||||
|
||||
self.updateSlowmodeStatus()
|
||||
@ -11660,6 +11684,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}), in: .current)
|
||||
}
|
||||
|
||||
public func cancelSelectingMessages() {
|
||||
self.navigationButtonAction(.cancelMessageSelection)
|
||||
}
|
||||
|
||||
private func navigationButtonAction(_ action: ChatNavigationButtonAction) {
|
||||
switch action {
|
||||
case .spacer, .toggleInfoPanel:
|
||||
@ -14587,7 +14615,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
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
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.collapseInput()
|
||||
@ -15943,14 +15971,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
savedMessages = true
|
||||
} else {
|
||||
if displayPeers.count == 1, let peer = displayPeers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
|
||||
} else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(firstPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(secondPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(firstPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(secondPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string
|
||||
} else if let peer = displayPeers.first {
|
||||
let peerName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
|
@ -2627,7 +2627,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
context: self.context,
|
||||
currentInputData: inputMediaNodeData,
|
||||
updatedInputData: self.inputMediaNodeDataPromise.get(),
|
||||
defaultToEmojiTab: !self.chatPresentationInterfaceState.interfaceState.effectiveInputState.inputText.string.isEmpty || self.openStickersBeginWithEmoji,
|
||||
defaultToEmojiTab: !self.chatPresentationInterfaceState.interfaceState.effectiveInputState.inputText.string.isEmpty || self.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || self.openStickersBeginWithEmoji,
|
||||
controllerInteraction: self.controllerInteraction,
|
||||
interfaceInteraction: self.interfaceInteraction,
|
||||
chatPeerId: peerId
|
||||
|
@ -354,47 +354,50 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
||||
|
||||
let isTextEmpty = chatPresentationInterfaceState.interfaceState.composeInputState.inputText.length == 0
|
||||
|
||||
if chatPresentationInterfaceState.interfaceState.forwardMessageIds == nil {
|
||||
if isTextEmpty && chatPresentationInterfaceState.hasScheduledMessages {
|
||||
accessoryItems.append(.scheduledMessages)
|
||||
let hasForward = chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil
|
||||
|
||||
if isTextEmpty && chatPresentationInterfaceState.hasScheduledMessages && !hasForward {
|
||||
accessoryItems.append(.scheduledMessages)
|
||||
}
|
||||
|
||||
var stickersEnabled = true
|
||||
var stickersAreEmoji = !isTextEmpty
|
||||
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
if isTextEmpty, case .broadcast = peer.info, canSendMessagesToPeer(peer) {
|
||||
accessoryItems.append(.silentPost(chatPresentationInterfaceState.interfaceState.silentPosting))
|
||||
}
|
||||
|
||||
var stickersEnabled = true
|
||||
|
||||
let stickersAreEmoji = !isTextEmpty
|
||||
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
if isTextEmpty, case .broadcast = peer.info, canSendMessagesToPeer(peer) {
|
||||
accessoryItems.append(.silentPost(chatPresentationInterfaceState.interfaceState.silentPosting))
|
||||
}
|
||||
if peer.hasBannedPermission(.banSendStickers) != nil {
|
||||
stickersEnabled = false
|
||||
}
|
||||
} else if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
|
||||
if peer.hasBannedPermission(.banSendStickers) {
|
||||
stickersEnabled = false
|
||||
}
|
||||
if peer.hasBannedPermission(.banSendStickers) != nil {
|
||||
stickersEnabled = false
|
||||
}
|
||||
if isTextEmpty && chatPresentationInterfaceState.hasBots && chatPresentationInterfaceState.hasBotCommands {
|
||||
accessoryItems.append(.commands)
|
||||
}
|
||||
|
||||
if !canSendTextMessages {
|
||||
if stickersEnabled && !stickersAreEmoji {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: .stickers))
|
||||
}
|
||||
} else {
|
||||
if stickersEnabled {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: stickersAreEmoji ? .emoji : .stickers))
|
||||
} else {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: .emoji))
|
||||
}
|
||||
}
|
||||
|
||||
if isTextEmpty, let message = chatPresentationInterfaceState.keyboardButtonsMessage, let _ = message.visibleButtonKeyboardMarkup, chatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId != message.id {
|
||||
accessoryItems.append(.botInput(isEnabled: true, inputMode: .bot))
|
||||
} else if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
|
||||
if peer.hasBannedPermission(.banSendStickers) {
|
||||
stickersEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if isTextEmpty && chatPresentationInterfaceState.hasBots && chatPresentationInterfaceState.hasBotCommands && !hasForward {
|
||||
accessoryItems.append(.commands)
|
||||
}
|
||||
|
||||
if !canSendTextMessages {
|
||||
if stickersEnabled && !stickersAreEmoji && !hasForward {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: .stickers))
|
||||
}
|
||||
} else {
|
||||
stickersAreEmoji = stickersAreEmoji || hasForward
|
||||
if stickersEnabled {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: stickersAreEmoji ? .emoji : .stickers))
|
||||
} else {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: .emoji))
|
||||
}
|
||||
}
|
||||
|
||||
if isTextEmpty, let message = chatPresentationInterfaceState.keyboardButtonsMessage, let _ = message.visibleButtonKeyboardMarkup, chatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId != message.id {
|
||||
accessoryItems.append(.botInput(isEnabled: true, inputMode: .bot))
|
||||
}
|
||||
|
||||
return ChatTextInputPanelState(accessoryItems: accessoryItems, contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,50 @@ private final class ChatMessageBubbleClippingNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func hasCommentButton(item: ChatMessageItem) -> Bool {
|
||||
let firstMessage = item.content.firstMessage
|
||||
|
||||
var hasDiscussion = false
|
||||
if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||
hasDiscussion = true
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == firstMessage.id {
|
||||
hasDiscussion = false
|
||||
}
|
||||
|
||||
if firstMessage.adAttribute != nil {
|
||||
hasDiscussion = false
|
||||
}
|
||||
|
||||
if hasDiscussion {
|
||||
var canComment = false
|
||||
if case .pinnedMessages = item.associatedData.subject {
|
||||
canComment = false
|
||||
} else if firstMessage.id.namespace == Namespaces.Message.Local {
|
||||
canComment = true
|
||||
} else {
|
||||
for attribute in firstMessage.attributes {
|
||||
if let attribute = attribute as? ReplyThreadMessageAttribute, let commentsPeerId = attribute.commentsPeerId {
|
||||
switch item.associatedData.channelDiscussionGroup {
|
||||
case .unknown:
|
||||
canComment = true
|
||||
case let .known(groupId):
|
||||
canComment = groupId == commentsPeerId
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if canComment {
|
||||
return true
|
||||
}
|
||||
} else if firstMessage.id.peerId.isReplies {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)], Bool, Bool) {
|
||||
var result: [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)] = []
|
||||
var skipText = false
|
||||
@ -65,7 +109,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
|
||||
var needReactions = true
|
||||
|
||||
var disableComments = false
|
||||
var hasSeparateCommentsButton = false
|
||||
|
||||
outer: for (message, itemAttributes) in item.content {
|
||||
for attribute in message.attributes {
|
||||
@ -87,7 +131,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
let isVideo = file.isVideo || (file.isAnimated && file.dimensions != nil)
|
||||
if isVideo {
|
||||
if file.isInstantVideo {
|
||||
disableComments = true
|
||||
hasSeparateCommentsButton = true
|
||||
result.append((message, ChatMessageInstantVideoBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||
} else {
|
||||
if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported), message.text.isEmpty {
|
||||
@ -222,43 +266,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
needReactions = false
|
||||
}
|
||||
|
||||
if !isAction && !disableComments && !Namespaces.Message.allScheduled.contains(firstMessage.id.namespace) {
|
||||
var hasDiscussion = false
|
||||
if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||
hasDiscussion = true
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == firstMessage.id {
|
||||
hasDiscussion = false
|
||||
}
|
||||
|
||||
if firstMessage.adAttribute != nil {
|
||||
hasDiscussion = false
|
||||
}
|
||||
|
||||
if hasDiscussion {
|
||||
var canComment = false
|
||||
if case .pinnedMessages = item.associatedData.subject {
|
||||
canComment = false
|
||||
} else if firstMessage.id.namespace == Namespaces.Message.Local {
|
||||
canComment = true
|
||||
} else {
|
||||
for attribute in firstMessage.attributes {
|
||||
if let attribute = attribute as? ReplyThreadMessageAttribute, let commentsPeerId = attribute.commentsPeerId {
|
||||
switch item.associatedData.channelDiscussionGroup {
|
||||
case .unknown:
|
||||
canComment = true
|
||||
case let .known(groupId):
|
||||
canComment = groupId == commentsPeerId
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if canComment {
|
||||
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)))
|
||||
}
|
||||
} else if firstMessage.id.peerId.isReplies {
|
||||
if !isAction && !hasSeparateCommentsButton && !Namespaces.Message.allScheduled.contains(firstMessage.id.namespace) {
|
||||
if hasCommentButton(item: item) {
|
||||
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)))
|
||||
}
|
||||
}
|
||||
@ -1292,6 +1301,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
if !needsShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty && !(item.message.media.first is TelegramMediaAction) {
|
||||
needsShareButton = true
|
||||
}
|
||||
var mayHaveSeparateCommentsButton = false
|
||||
if !needsShareButton {
|
||||
loop: for media in item.message.media {
|
||||
if media is TelegramMediaGame || media is TelegramMediaInvoice {
|
||||
@ -1307,12 +1317,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
if media is TelegramMediaAction {
|
||||
needsShareButton = false
|
||||
break loop
|
||||
} else if let media = media as? TelegramMediaFile, media.isInstantVideo {
|
||||
mayHaveSeparateCommentsButton = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
|
||||
needsShareButton = false
|
||||
if (item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected()) {
|
||||
if mayHaveSeparateCommentsButton && hasCommentButton(item: item) {
|
||||
} else {
|
||||
needsShareButton = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,7 +238,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
|
||||
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
||||
var replyInfoApply: (CGSize, (Bool) -> ChatMessageReplyInfoNode)?
|
||||
var updatedReplyBackgroundNode: NavigationBackgroundNode?
|
||||
|
||||
var updatedInstantVideoBackgroundImage: UIImage?
|
||||
let instantVideoBackgroundImage: UIImage?
|
||||
@ -556,17 +555,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
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
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
@ -575,6 +564,17 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
strongSelf.secretProgressIcon = secretProgressIcon
|
||||
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 {
|
||||
strongSelf.audioTranscriptionState = updatedAudioTranscriptionState
|
||||
strongSelf.updateTranscriptionExpanded?(strongSelf.audioTranscriptionState)
|
||||
@ -928,7 +928,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
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)
|
||||
|
||||
messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height)
|
||||
@ -1226,6 +1226,30 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
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 item = self.item {
|
||||
for attribute in item.message.attributes {
|
||||
@ -1315,6 +1339,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
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 {
|
||||
return self.view
|
||||
}
|
||||
|
@ -975,6 +975,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
break
|
||||
case .groupBotStart:
|
||||
break
|
||||
case .gameStart:
|
||||
break
|
||||
case let .channelMessage(peer, messageId, timecode):
|
||||
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)))
|
||||
|
@ -379,11 +379,11 @@ private func calculateTextFieldRealInsets(presentationInterfaceState: ChatPresen
|
||||
return UIEdgeInsets(top: 4.5 + top, left: 0.0, bottom: 5.5 + bottom, right: right)
|
||||
}
|
||||
|
||||
private var currentTextInputBackgroundImage: (UIColor, UIColor, CGFloat, UIImage)?
|
||||
private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackgroundColor: UIColor?, strokeColor: UIColor, diameter: CGFloat) -> UIImage? {
|
||||
private var currentTextInputBackgroundImage: (UIColor, UIColor, CGFloat, CGFloat, UIImage)?
|
||||
private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackgroundColor: UIColor?, strokeColor: UIColor, diameter: CGFloat, strokeWidth: CGFloat) -> UIImage? {
|
||||
if let backgroundColor = backgroundColor, let current = currentTextInputBackgroundImage {
|
||||
if current.0.isEqual(backgroundColor) && current.1.isEqual(strokeColor) && current.2.isEqual(to: diameter) {
|
||||
return current.3
|
||||
if current.0.isEqual(backgroundColor) && current.1.isEqual(strokeColor) && current.2.isEqual(to: diameter) && current.3.isEqual(to: strokeWidth) {
|
||||
return current.4
|
||||
}
|
||||
}
|
||||
|
||||
@ -401,13 +401,12 @@ private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackground
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setStrokeColor(strokeColor.cgColor)
|
||||
let strokeWidth: CGFloat = UIScreenPixel
|
||||
context.setLineWidth(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)
|
||||
if let image = image {
|
||||
if let backgroundColor = backgroundColor {
|
||||
currentTextInputBackgroundImage = (backgroundColor, strokeColor, diameter, image)
|
||||
currentTextInputBackgroundImage = (backgroundColor, strokeColor, diameter, strokeWidth, image)
|
||||
}
|
||||
return image
|
||||
} else {
|
||||
@ -1290,7 +1289,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
if self.theme == nil || !self.theme!.chat.inputPanel.inputTextColor.isEqual(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)
|
||||
|
||||
if let textInputNode = self.textInputNode {
|
||||
@ -1302,12 +1300,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
textInputNode.selectedRange = range
|
||||
}
|
||||
textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(baseFontSize), NSAttributedString.Key.foregroundColor.rawValue: textColor]
|
||||
textInputNode.tintColor = tintColor
|
||||
|
||||
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
|
||||
if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance, textInputNode.isFirstResponder() {
|
||||
if textInputNode.isCurrentlyEmoji() {
|
||||
@ -1332,15 +1335,18 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics)
|
||||
let minimalInputHeight: CGFloat = 2.0 + textFieldMinHeight
|
||||
|
||||
let strokeWidth: CGFloat
|
||||
let backgroundColor: UIColor
|
||||
if case let .color(color) = interfaceState.chatWallpaper, UIColor(rgb: color).isEqual(interfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
|
||||
backgroundColor = interfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
strokeWidth = 1.0 - UIScreenPixel
|
||||
} else {
|
||||
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.transparentTextInputBackgroundImage = textInputBackgroundImage(backgroundColor: nil, inputBackgroundColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor, 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, strokeWidth: strokeWidth)
|
||||
self.textInputContainerBackgroundNode.image = generateStretchableFilledCircleImage(diameter: minimalInputHeight, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor)
|
||||
|
||||
self.searchLayoutClearImageNode.image = PresentationResourcesChat.chatInputTextFieldClearImage(interfaceState.theme)
|
||||
|
@ -162,6 +162,28 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
}
|
||||
dismissInput()
|
||||
navigationController?.pushViewController(controller)
|
||||
case let .gameStart(botPeerId, 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 _ = peer.id
|
||||
let _ = botPeerId
|
||||
let _ = game
|
||||
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):
|
||||
openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil))
|
||||
case let .replyThreadMessage(replyThreadMessage, messageId):
|
||||
|
@ -86,14 +86,18 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
|
@ -2495,7 +2495,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
if actions.options.contains(.deleteGlobally) {
|
||||
let globalTitle: String
|
||||
if isChannel {
|
||||
globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe
|
||||
globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone
|
||||
} else if let personalPeerName = personalPeerName {
|
||||
globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesFor(personalPeerName).string
|
||||
} else {
|
||||
@ -8000,14 +8000,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
savedMessages = true
|
||||
} else {
|
||||
if displayPeers.count == 1, let peer = displayPeers.first {
|
||||
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
|
||||
} else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last {
|
||||
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(firstPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(secondPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(firstPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
|
||||
var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(secondPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string
|
||||
} else if let peer = displayPeers.first {
|
||||
let peerName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
var peerName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
@ -8921,7 +8925,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
let offsetY = self.scrollNode.view.contentOffset.y
|
||||
|
||||
if self.isSettings, !(self.controller?.movingInHierarchy == true) {
|
||||
let bottomOffsetY = max(0.0, self.scrollNode.view.contentSize.height + min(83.0, self.scrollNode.view.contentInset.bottom) - offsetY - self.scrollNode.frame.height)
|
||||
var bottomInset = self.scrollNode.view.contentInset.bottom
|
||||
if let layout = self.validLayout?.0, case .compact = layout.metrics.widthClass {
|
||||
bottomInset = min(83.0, bottomInset)
|
||||
}
|
||||
let bottomOffsetY = max(0.0, self.scrollNode.view.contentSize.height + bottomInset - offsetY - self.scrollNode.frame.height)
|
||||
let backgroundAlpha: CGFloat = min(30.0, bottomOffsetY) / 30.0
|
||||
|
||||
if let tabBarController = self.controller?.parent as? TabBarController {
|
||||
|
@ -67,6 +67,7 @@ public enum ParsedInternalPeerUrlParameter {
|
||||
case botStart(String)
|
||||
case groupBotStart(String, ResolvedBotAdminRights?)
|
||||
case attachBotStart(String, String?)
|
||||
case gameStart(String)
|
||||
case channelMessage(Int32, Double?)
|
||||
case replyThread(Int32, Int32)
|
||||
case voiceChat(String?)
|
||||
@ -212,7 +213,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
}
|
||||
return .peer(.name(peerName), .groupBotStart(value, botAdminRights))
|
||||
} else if queryItem.name == "game" {
|
||||
return nil
|
||||
return .peer(.name(peerName), .gameStart(value))
|
||||
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||
return .peer(.name(peerName), .voiceChat(value))
|
||||
} else if queryItem.name == "startattach" {
|
||||
@ -582,6 +583,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
return .single(.botStart(peer: peer, payload: payload))
|
||||
case let .groupBotStart(payload, 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):
|
||||
return context.engine.peers.resolvePeerByName(name: name)
|
||||
|> take(1)
|
||||
|
@ -36,7 +36,7 @@ func presentLegacyWebSearchEditor(context: AccountContext, theme: PresentationTh
|
||||
|
||||
let paintStickersContext = LegacyPaintStickersContext(context: context)
|
||||
|
||||
let controller = TGPhotoEditorController(context: legacyController.context, item: item, intent: TGPhotoEditorControllerAvatarIntent, adjustments: nil, caption: nil, screenImage: screenImage ?? UIImage(), availableTabs: TGPhotoEditorController.defaultTabsForAvatarIntent(), selectedTab: .cropTab)!
|
||||
let controller = TGPhotoEditorController(context: legacyController.context, item: item, intent: TGPhotoEditorControllerAvatarIntent, adjustments: nil, caption: nil, screenImage: screenImage ?? UIImage(), availableTabs: TGPhotoEditorController.defaultTabs(forAvatarIntent: true), selectedTab: .cropTab)!
|
||||
controller.stickersContext = paintStickersContext
|
||||
legacyController.bind(controller: controller)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user