mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-31 23:47:01 +00:00
Merge commit '0b1e72044aeb8a1286d90bb0ac5d61cf6e4c13d9'
This commit is contained in:
commit
b13a0e9a06
@ -5922,6 +5922,8 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Conversation.ImportedMessageHint" = "The messages was imported from another app. We can't guarantee it's real.";
|
||||
|
||||
"Conversation.GreetingText" = "Send a message or tap on the greeting below.";
|
||||
|
||||
"CallList.DeleteAllForMe" = "Delete for me";
|
||||
"CallList.DeleteAllForEveryone" = "Delete for me and Others";
|
||||
"Conversation.ImportProgress" = "Importing Messages... %@%";
|
||||
|
@ -583,7 +583,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
strongSelf.deletePeerChat(peerId: peerId, joined: joined)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.containerNode.peerSelected = { [weak self] peer, animated, promoInfo in
|
||||
self.chatListDisplayNode.containerNode.peerSelected = { [weak self] peer, animated, activateInput, promoInfo in
|
||||
if let strongSelf = self {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var scrollToEndIfExists = false
|
||||
@ -591,7 +591,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
scrollToEndIfExists = true
|
||||
}
|
||||
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] controller in
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] controller in
|
||||
self?.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true)
|
||||
if let promoInfo = promoInfo {
|
||||
switch promoInfo {
|
||||
|
@ -475,8 +475,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
itemNode.listNode.deletePeerChat = { [weak self] peerId, joined in
|
||||
self?.deletePeerChat?(peerId, joined)
|
||||
}
|
||||
itemNode.listNode.peerSelected = { [weak self] peerId, a, b in
|
||||
self?.peerSelected?(peerId, a, b)
|
||||
itemNode.listNode.peerSelected = { [weak self] peerId, animated, activateInput, promoInfo in
|
||||
self?.peerSelected?(peerId, animated, activateInput, promoInfo)
|
||||
}
|
||||
itemNode.listNode.groupSelected = { [weak self] groupId in
|
||||
self?.groupSelected?(groupId)
|
||||
@ -522,7 +522,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
var toggleArchivedFolderHiddenByDefault: (() -> Void)?
|
||||
var hidePsa: ((PeerId) -> Void)?
|
||||
var deletePeerChat: ((PeerId, Bool) -> Void)?
|
||||
var peerSelected: ((Peer, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
|
||||
var peerSelected: ((Peer, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
|
||||
var groupSelected: ((PeerGroupId) -> Void)?
|
||||
var updatePeerGrouping: ((PeerId, Bool) -> Void)?
|
||||
var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
|
||||
|
@ -440,7 +440,7 @@ public final class ChatListNode: ListView {
|
||||
return _contentsReady.get()
|
||||
}
|
||||
|
||||
public var peerSelected: ((Peer, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
|
||||
public var peerSelected: ((Peer, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
|
||||
public var disabledPeerSelected: ((Peer) -> Void)?
|
||||
public var additionalCategorySelected: ((Int) -> Void)?
|
||||
public var groupSelected: ((PeerGroupId) -> Void)?
|
||||
@ -565,7 +565,7 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}, peerSelected: { [weak self] peer, promoInfo in
|
||||
if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
|
||||
peerSelected(peer, true, promoInfo)
|
||||
peerSelected(peer, true, true, promoInfo)
|
||||
}
|
||||
}, disabledPeerSelected: { [weak self] peer in
|
||||
if let strongSelf = self, let disabledPeerSelected = strongSelf.disabledPeerSelected {
|
||||
@ -594,7 +594,18 @@ public final class ChatListNode: ListView {
|
||||
self?.additionalCategorySelected?(id)
|
||||
}, messageSelected: { [weak self] peer, message, promoInfo in
|
||||
if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
|
||||
peerSelected(peer, true, promoInfo)
|
||||
var activateInput = false
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case .peerJoined, .groupCreated, .channelMigratedFromGroup, .historyCleared:
|
||||
activateInput = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
peerSelected(peer, true, activateInput, promoInfo)
|
||||
}
|
||||
}, groupSelected: { [weak self] groupId in
|
||||
if let strongSelf = self, let groupSelected = strongSelf.groupSelected {
|
||||
@ -1750,7 +1761,7 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: strongSelf.currentlyVisibleLatestChatListIndex() ?? .absoluteUpperBound, scrollPosition: .center(.top), animated: true, filter: strongSelf.chatListFilter)
|
||||
strongSelf.setChatListLocation(location)
|
||||
strongSelf.peerSelected?(peer, false, nil)
|
||||
strongSelf.peerSelected?(peer, false, false, nil)
|
||||
})
|
||||
case .previous(unread: false), .next(unread: false):
|
||||
var target: (ChatListIndex, Peer)? = nil
|
||||
@ -1774,7 +1785,7 @@ public final class ChatListNode: ListView {
|
||||
if let target = target {
|
||||
let location: ChatListNodeLocation = .scroll(index: target.0, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true, filter: self.chatListFilter)
|
||||
self.setChatListLocation(location)
|
||||
self.peerSelected?(target.1, false, nil)
|
||||
self.peerSelected?(target.1, false, false, nil)
|
||||
}
|
||||
case let .peerId(peerId):
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> Peer? in
|
||||
@ -1784,7 +1795,7 @@ public final class ChatListNode: ListView {
|
||||
guard let strongSelf = self, let peer = peer else {
|
||||
return
|
||||
}
|
||||
strongSelf.peerSelected?(peer, false, nil)
|
||||
strongSelf.peerSelected?(peer, false, false, nil)
|
||||
})
|
||||
case let .index(index):
|
||||
guard index < 10 else {
|
||||
@ -1803,7 +1814,7 @@ public final class ChatListNode: ListView {
|
||||
if entries.count > index, case let .MessageEntry(index, _, _, _, _, renderedPeer, _, _, _, _) = entries[10 - index - 1] {
|
||||
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true, filter: filter)
|
||||
self.setChatListLocation(location)
|
||||
self.peerSelected?(renderedPeer.peer!, false, nil)
|
||||
self.peerSelected?(renderedPeer.peer!, false, false, nil)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -352,16 +352,16 @@ public final class InviteLinkInviteController: ViewController {
|
||||
}
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if let invite = invite {
|
||||
let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
self?.controller?.present(controller, in: .window(.root))
|
||||
}
|
||||
})))
|
||||
// items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
// return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
// }, action: { _, f in
|
||||
// f(.dismissWithoutContent)
|
||||
//
|
||||
// if let invite = invite {
|
||||
// let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
// self?.controller?.present(controller, 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)
|
||||
@ -407,7 +407,8 @@ public final class InviteLinkInviteController: ViewController {
|
||||
let previousEntries = Atomic<[InviteLinkInviteEntry]?>(value: nil)
|
||||
|
||||
let peerView = context.account.postbox.peerView(id: peerId)
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), peerView, self.invitesContext.state)
|
||||
let invites: Signal<PeerExportedInvitationsState, NoError> = .single(PeerExportedInvitationsState())
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), peerView, invites)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, view, invites in
|
||||
if let strongSelf = self {
|
||||
var entries: [InviteLinkInviteEntry] = []
|
||||
@ -426,19 +427,19 @@ public final class InviteLinkInviteController: ViewController {
|
||||
entries.append(.mainLink(presentationData.theme, mainInvite))
|
||||
}
|
||||
|
||||
let additionalInvites = invites.invitations.filter { $0.link != mainInvite?.link }
|
||||
var index: Int32 = 0
|
||||
for i in stride(from: 0, to: additionalInvites.endIndex, by: 2) {
|
||||
var invitesPair: [ExportedInvitation] = []
|
||||
invitesPair.append(additionalInvites[i])
|
||||
if i + 1 < additionalInvites.count {
|
||||
invitesPair.append(additionalInvites[i + 1])
|
||||
}
|
||||
entries.append(.links(index, presentationData.theme, invitesPair))
|
||||
index += 1
|
||||
}
|
||||
// let additionalInvites = invites.invitations.filter { $0.link != mainInvite?.link }
|
||||
// var index: Int32 = 0
|
||||
// for i in stride(from: 0, to: additionalInvites.endIndex, by: 2) {
|
||||
// var invitesPair: [ExportedInvitation] = []
|
||||
// invitesPair.append(additionalInvites[i])
|
||||
// if i + 1 < additionalInvites.count {
|
||||
// invitesPair.append(additionalInvites[i + 1])
|
||||
// }
|
||||
// entries.append(.links(index, presentationData.theme, invitesPair))
|
||||
// index += 1
|
||||
// }
|
||||
|
||||
entries.append(.manage(presentationData.theme, presentationData.strings.InviteLink_Manage, additionalInvites.isEmpty))
|
||||
// entries.append(.manage(presentationData.theme, presentationData.strings.InviteLink_Manage, additionalInvites.isEmpty))
|
||||
|
||||
let previousEntries = previousEntries.swap(entries)
|
||||
|
||||
@ -467,7 +468,6 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self.contentNode.addSubnode(self.headerNode)
|
||||
|
||||
self.headerNode.addSubnode(self.headerBackgroundNode)
|
||||
// self.headerNode.addSubnode(self.titleNode)
|
||||
self.headerNode.addSubnode(self.doneButton)
|
||||
|
||||
self.doneButton.addTarget(self, action: #selector(self.doneButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
@ -53,23 +53,6 @@ private enum InviteLinkViewEntryId: Hashable {
|
||||
case importer(PeerId)
|
||||
}
|
||||
|
||||
private func color(for invite: ExportedInvitation) -> UIColor? {
|
||||
let color: UIColor?
|
||||
let availability = invitationAvailability(invite)
|
||||
if invite.isRevoked {
|
||||
color = nil
|
||||
} else if invite.expireDate == nil && invite.usageLimit == nil {
|
||||
color = nil
|
||||
} else if availability >= 0.5 {
|
||||
color = UIColor(rgb: 0x4aca62)
|
||||
} else if availability > 0.0 {
|
||||
color = UIColor(rgb: 0xf8a953)
|
||||
} else {
|
||||
color = UIColor(rgb: 0xf2656a)
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
||||
private enum InviteLinkViewEntry: Comparable, Identifiable {
|
||||
case link(PresentationTheme, ExportedInvitation)
|
||||
case creatorHeader(PresentationTheme, String)
|
||||
@ -170,9 +153,8 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
||||
func item(account: Account, presentationData: PresentationData, interaction: InviteLinkViewInteraction) -> ListViewItem {
|
||||
switch self {
|
||||
case let .link(_, invite):
|
||||
let buttonColor = color(for: invite)
|
||||
let availability = invitationAvailability(invite)
|
||||
return ItemListPermanentInviteLinkItem(context: interaction.context, presentationData: ItemListPresentationData(presentationData), invite: invite, count: 0, peers: [], displayButton: !invite.isRevoked && !availability.isZero, displayImporters: false, buttonColor: buttonColor, sectionId: 0, style: .plain, copyAction: {
|
||||
return ItemListPermanentInviteLinkItem(context: interaction.context, presentationData: ItemListPresentationData(presentationData), invite: invite, count: 0, peers: [], displayButton: !invite.isRevoked && !availability.isZero, displayImporters: false, buttonColor: nil, sectionId: 0, style: .plain, copyAction: {
|
||||
interaction.copyLink(invite)
|
||||
}, shareAction: {
|
||||
interaction.shareLink(invite)
|
||||
@ -383,13 +365,13 @@ public final class InviteLinkViewController: ViewController {
|
||||
self.subtitleNode.maximumNumberOfLines = 1
|
||||
self.subtitleNode.textAlignment = .center
|
||||
|
||||
let buttonColor = color(for: invite) ?? presentationData.theme.actionSheet.controlAccentColor
|
||||
let accentColor = presentationData.theme.actionSheet.controlAccentColor
|
||||
|
||||
self.editButton = HighlightableButtonNode()
|
||||
self.editButton.setTitle(self.presentationData.strings.Common_Edit, with: Font.regular(17.0), with: buttonColor, for: .normal)
|
||||
self.editButton.setTitle(self.presentationData.strings.Common_Edit, with: Font.regular(17.0), with: accentColor, for: .normal)
|
||||
|
||||
self.doneButton = HighlightableButtonNode()
|
||||
self.doneButton.setTitle(self.presentationData.strings.Common_Done, with: Font.bold(17.0), with: buttonColor, for: .normal)
|
||||
self.doneButton.setTitle(self.presentationData.strings.Common_Done, with: Font.bold(17.0), with: accentColor, for: .normal)
|
||||
|
||||
self.historyBackgroundNode = ASDisplayNode()
|
||||
self.historyBackgroundNode.isLayerBacked = true
|
||||
@ -617,9 +599,9 @@ public final class InviteLinkViewController: ViewController {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.InviteLink_InviteLink, font: titleFont, textColor: self.presentationData.theme.actionSheet.primaryTextColor)
|
||||
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitleNode.attributedText?.string ?? "", font: subtitleFont, textColor: self.presentationData.theme.list.itemSecondaryTextColor)
|
||||
|
||||
let buttonColor = color(for: invite) ?? self.presentationData.theme.actionSheet.controlAccentColor
|
||||
self.editButton.setTitle(self.presentationData.strings.Common_Edit, with: Font.regular(17.0), with: buttonColor, for: .normal)
|
||||
self.doneButton.setTitle(self.presentationData.strings.Common_Done, with: Font.bold(17.0), with: buttonColor, for: .normal)
|
||||
let accentColor = self.presentationData.theme.actionSheet.controlAccentColor
|
||||
self.editButton.setTitle(self.presentationData.strings.Common_Edit, with: Font.regular(17.0), with: accentColor, for: .normal)
|
||||
self.doneButton.setTitle(self.presentationData.strings.Common_Done, with: Font.bold(17.0), with: accentColor, for: .normal)
|
||||
}
|
||||
|
||||
private func enqueueTransition(_ transition: InviteLinkViewTransaction) {
|
||||
|
@ -218,17 +218,29 @@ private class ItemNode: ASDisplayNode {
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
let availability = invitationAvailability(invite)
|
||||
let transitionFraction: CGFloat
|
||||
let color: ItemBackgroundColor
|
||||
let nextColor: ItemBackgroundColor
|
||||
if invite.isRevoked {
|
||||
color = .gray
|
||||
nextColor = .gray
|
||||
transitionFraction = 0.0
|
||||
} else if invite.expireDate == nil && invite.usageLimit == nil {
|
||||
color = .blue
|
||||
nextColor = .blue
|
||||
transitionFraction = 0.0
|
||||
} else if availability >= 0.5 {
|
||||
color = .green
|
||||
nextColor = .yellow
|
||||
transitionFraction = (availability - 0.5) / 0.5
|
||||
} else if availability > 0.0 {
|
||||
color = .yellow
|
||||
nextColor = .red
|
||||
transitionFraction = availability / 0.5
|
||||
} else {
|
||||
color = .red
|
||||
nextColor = .red
|
||||
transitionFraction = 0.0
|
||||
}
|
||||
|
||||
let previousParams = self.params
|
||||
@ -239,8 +251,9 @@ private class ItemNode: ASDisplayNode {
|
||||
self.updateTimer?.invalidate()
|
||||
self.updateTimer = nil
|
||||
|
||||
if let _ = invite.expireDate, availability > 0.0 {
|
||||
let updateTimer = SwiftSignalKit.Timer(timeout: 5.0, repeat: true, completion: { [weak self] in
|
||||
if let expireDate = invite.expireDate, availability > 0.0 {
|
||||
let timeout = min(2.0, max(0.001, Double(expireDate - currentTime)))
|
||||
let updateTimer = SwiftSignalKit.Timer(timeout: timeout, repeat: true, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let (size, wide, invite, _, presentationData) = strongSelf.params {
|
||||
let _ = strongSelf.update(size: size, wide: wide, share: share, invite: invite, presentationData: presentationData, transition: .animated(duration: 0.3, curve: .linear))
|
||||
@ -255,9 +268,14 @@ private class ItemNode: ASDisplayNode {
|
||||
self.updateTimer = nil
|
||||
}
|
||||
|
||||
let colors: NSArray = [color.colors.top.cgColor, color.colors.bottom.cgColor]
|
||||
let topColor = color.colors.top
|
||||
let bottomColor = color.colors.bottom
|
||||
let nextTopColor = nextColor.colors.top
|
||||
let nextBottomColor = nextColor.colors.bottom
|
||||
let colors: NSArray = [nextTopColor.mixedWith(topColor, alpha: transitionFraction).cgColor, nextBottomColor.mixedWith(bottomColor, alpha: transitionFraction).cgColor]
|
||||
|
||||
if let (_, _, previousInvite, previousColor, _) = previousParams, previousInvite == invite {
|
||||
if previousColor != color {
|
||||
if previousColor != color && color == .red {
|
||||
if let snapshotView = self.wrapperNode.view.snapshotContentTree() {
|
||||
snapshotView.frame = self.wrapperNode.bounds
|
||||
self.wrapperNode.view.addSubview(snapshotView)
|
||||
@ -266,12 +284,18 @@ private class ItemNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
self.backgroundGradientLayer.colors = colors as? [Any]
|
||||
} else if (color == .green && nextColor == .yellow) || (color == .yellow && nextColor == .red) {
|
||||
let previousColors = self.backgroundGradientLayer.colors
|
||||
if transition.isAnimated {
|
||||
self.backgroundGradientLayer.animate(from: previousColors as AnyObject, to: self.backgroundGradientLayer.colors as AnyObject, keyPath: "colors", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 2.5)
|
||||
}
|
||||
self.backgroundGradientLayer.colors = colors as? [Any]
|
||||
}
|
||||
} else {
|
||||
self.backgroundGradientLayer.colors = colors as? [Any]
|
||||
}
|
||||
|
||||
let secondaryTextColor = color.colors.text
|
||||
let secondaryTextColor = nextColor.colors.text.mixedWith(color.colors.text, alpha: transitionFraction)
|
||||
|
||||
let itemWidth = wide ? size.width : floor((size.width - itemSpacing) / 2.0)
|
||||
var inviteLink = invite.link.replacingOccurrences(of: "https://", with: "")
|
||||
|
@ -293,7 +293,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
case let .privateLinkHeader(_, title):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||
case let .privateLink(_, invite, displayImporters):
|
||||
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: displayImporters, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
||||
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: false, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
||||
if let invite = invite {
|
||||
arguments.copyLink(invite)
|
||||
}
|
||||
@ -598,13 +598,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
} else {
|
||||
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePublicLinkHelp))
|
||||
}
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
}
|
||||
case .privateChannel:
|
||||
let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation
|
||||
@ -615,13 +615,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
} else {
|
||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePrivateLinkHelp))
|
||||
}
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
}
|
||||
} else if let _ = view.peers[view.peerId] as? TelegramGroup {
|
||||
switch mode {
|
||||
@ -630,13 +630,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
||||
entries.append(.privateLink(presentationData.theme, invite, mode != .initialSetup))
|
||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
case .generic, .initialSetup:
|
||||
let selectedType: CurrentChannelType
|
||||
if let current = state.selectedType {
|
||||
@ -729,13 +729,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
||||
entries.append(.privateLink(presentationData.theme, invite, mode != .initialSetup))
|
||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -943,27 +943,27 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
||||
})
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
let _ = (context.account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
if let cachedData = transaction.getPeerCachedData(peerId: peerId) {
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
return cachedData.exportedInvitation
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
return cachedData.exportedInvitation
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} |> deliverOnMainQueue).start(next: { invite in
|
||||
if let invite = invite {
|
||||
let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
presentControllerImpl?(controller, nil)
|
||||
}
|
||||
})
|
||||
})))
|
||||
// items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
// return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
// }, action: { _, f in
|
||||
// f(.dismissWithoutContent)
|
||||
//
|
||||
// let _ = (context.account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
// if let cachedData = transaction.getPeerCachedData(peerId: peerId) {
|
||||
// if let cachedData = cachedData as? CachedChannelData {
|
||||
// return cachedData.exportedInvitation
|
||||
// } else if let cachedData = cachedData as? CachedGroupData {
|
||||
// return cachedData.exportedInvitation
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
// } |> deliverOnMainQueue).start(next: { invite in
|
||||
// if let invite = invite {
|
||||
// let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
// presentControllerImpl?(controller, nil)
|
||||
// }
|
||||
// })
|
||||
// })))
|
||||
|
||||
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)
|
||||
|
@ -354,7 +354,8 @@ public final class VoiceChatController: ViewController {
|
||||
text = .text(presentationData.strings.VoiceChat_StatusMutedForYou, .destructive)
|
||||
icon = .microphone(true, UIColor(rgb: 0xff3b30))
|
||||
} else {
|
||||
if let volume = peerEntry.volume, volume != 10000 {
|
||||
let volumeValue = peerEntry.volume.flatMap { $0 / 100 }
|
||||
if let volume = volumeValue, volume != 100 {
|
||||
text = .text("\(volume / 100)% \(presentationData.strings.VoiceChat_StatusSpeaking)", .constructive)
|
||||
} else {
|
||||
text = .text(presentationData.strings.VoiceChat_StatusSpeaking, .constructive)
|
||||
@ -1275,6 +1276,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
@objc private func closePressed() {
|
||||
self.controller?.dismiss(closing: false)
|
||||
self.controller?.dismissAllTooltips()
|
||||
}
|
||||
|
||||
@objc private func leavePressed() {
|
||||
@ -1284,11 +1286,13 @@ public final class VoiceChatController: ViewController {
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
self?.controller?.dismiss(closing: true)
|
||||
}))
|
||||
self.controller?.dismissAllTooltips()
|
||||
}
|
||||
|
||||
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.controller?.dismiss(closing: false)
|
||||
self.controller?.dismissAllTooltips()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2144,6 +2148,8 @@ public final class VoiceChatController: ViewController {
|
||||
topInset = self.listNode.frame.height
|
||||
}
|
||||
self.panGestureArguments = (topInset, 0.0)
|
||||
|
||||
self.controller?.dismissAllTooltips()
|
||||
case .changed:
|
||||
var translation = recognizer.translation(in: self.contentContainer.view).y
|
||||
var topInset: CGFloat = 0.0
|
||||
@ -2459,6 +2465,20 @@ public final class VoiceChatController: ViewController {
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
private func dismissAllTooltips() {
|
||||
self.window?.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismissWithCommitAction()
|
||||
}
|
||||
})
|
||||
self.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismissWithCommitAction()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
private func detachActionButton() {
|
||||
guard self.currentOverlayController == nil && !self.isDisconnected else {
|
||||
return
|
||||
|
@ -85,6 +85,9 @@ private final class VoiceChatVolumeContextItemNode: ASDisplayNode, ContextMenuCu
|
||||
|
||||
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
self.view.addGestureRecognizer(panGestureRecognizer)
|
||||
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||
self.view.addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
|
||||
func updateTheme(presentationData: PresentationData) {
|
||||
@ -180,4 +183,10 @@ private final class VoiceChatVolumeContextItemNode: ASDisplayNode, ContextMenuCu
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
let location = gestureRecognizer.location(in: gestureRecognizer.view)
|
||||
self.value = max(0.0, min(2.0, location.x / self.bounds.width * 2.0))
|
||||
self.valueChanged(self.value, true)
|
||||
}
|
||||
}
|
||||
|
@ -1094,12 +1094,6 @@ public final class GroupCallParticipantsContext {
|
||||
|
||||
public func updateMuteState(peerId: PeerId, muteState: Participant.MuteState?, volume: Int32?) {
|
||||
|
||||
var muteState = muteState
|
||||
if muteState == nil, let _ = volume {
|
||||
muteState = self.stateValue.overlayState.pendingMuteStateChanges[peerId]?.state
|
||||
?? self.stateValue.state.participants.first(where: { $0.peer.id == peerId })?.muteState
|
||||
}
|
||||
|
||||
if let current = self.stateValue.overlayState.pendingMuteStateChanges[peerId] {
|
||||
if current.state == muteState {
|
||||
return
|
||||
@ -1135,12 +1129,11 @@ public final class GroupCallParticipantsContext {
|
||||
return .single(nil)
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
if let _ = volume {
|
||||
if let volume = volume, volume > 0 {
|
||||
flags |= 1 << 1
|
||||
} else {
|
||||
if let muteState = muteState, (!muteState.canUnmute || peerId == account.peerId || muteState.mutedByYou) {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
}
|
||||
if let muteState = muteState, (!muteState.canUnmute || peerId == account.peerId || muteState.mutedByYou) {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.phone.editGroupCallMember(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), userId: inputUser, volume: volume))
|
||||
|
@ -258,6 +258,22 @@ public struct PeerExportedInvitationsState: Equatable {
|
||||
public var hasLoadedOnce: Bool
|
||||
public var canLoadMore: Bool
|
||||
public var count: Int32
|
||||
|
||||
public init() {
|
||||
self.invitations = []
|
||||
self.isLoadingMore = false
|
||||
self.hasLoadedOnce = false
|
||||
self.canLoadMore = false
|
||||
self.count = 0
|
||||
}
|
||||
|
||||
public init(invitations: [ExportedInvitation], isLoadingMore: Bool, hasLoadedOnce: Bool, canLoadMore: Bool, count: Int32) {
|
||||
self.invitations = invitations
|
||||
self.isLoadingMore = isLoadingMore
|
||||
self.hasLoadedOnce = hasLoadedOnce
|
||||
self.canLoadMore = canLoadMore
|
||||
self.count = count
|
||||
}
|
||||
}
|
||||
|
||||
final class CachedPeerExportedInvitations: PostboxCoding {
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -66,6 +66,173 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNodeContent, UIGestureRecognizerDelegate {
|
||||
private let account: Account
|
||||
private let interaction: ChatPanelInterfaceInteraction?
|
||||
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
private var stickerItem: ChatMediaInputStickerGridItem?
|
||||
private let stickerNode: ChatMediaInputStickerGridItemNode
|
||||
|
||||
private var currentTheme: PresentationTheme?
|
||||
private var currentStrings: PresentationStrings?
|
||||
|
||||
private var didSetupSticker = false
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
var greetingStickerNode: ASDisplayNode? {
|
||||
if let animationNode = self.stickerNode.animationNode, animationNode.supernode === stickerNode {
|
||||
return animationNode
|
||||
} else if self.stickerNode.imageNode.supernode === stickerNode {
|
||||
return self.stickerNode.imageNode
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
init(account: Account, interaction: ChatPanelInterfaceInteraction?) {
|
||||
self.account = account
|
||||
self.interaction = interaction
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
self.titleNode.lineSpacing = 0.15
|
||||
self.titleNode.textAlignment = .center
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
self.textNode.lineSpacing = 0.15
|
||||
self.textNode.textAlignment = .center
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.displaysAsynchronously = false
|
||||
|
||||
self.stickerNode = ChatMediaInputStickerGridItemNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.stickerNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.stickerTapGesture(_:)))
|
||||
tapRecognizer.delegate = self
|
||||
self.stickerNode.view.addGestureRecognizer(tapRecognizer)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@objc private func stickerTapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
guard let stickerItem = self.stickerItem else {
|
||||
return
|
||||
}
|
||||
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file), self.stickerNode, self.stickerNode.bounds)
|
||||
}
|
||||
|
||||
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||
self.currentTheme = interfaceState.theme
|
||||
self.currentStrings = interfaceState.strings
|
||||
|
||||
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_EmptyPlaceholder, font: titleFont, textColor: serviceColor.primaryText)
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_GreetingText, font: messageFont, textColor: serviceColor.primaryText)
|
||||
}
|
||||
|
||||
let stickerSize = CGSize(width: 160.0, height: 160.0)
|
||||
if let item = self.stickerItem {
|
||||
self.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
|
||||
} else if !self.didSetupSticker {
|
||||
let sticker: Signal<TelegramMediaFile?, NoError>
|
||||
if let preloadedSticker = interfaceState.peerNearbyData?.sticker {
|
||||
sticker = .single(preloadedSticker)
|
||||
} else {
|
||||
sticker = randomGreetingSticker(account: self.account)
|
||||
|> map { item -> TelegramMediaFile? in
|
||||
return item?.file
|
||||
}
|
||||
}
|
||||
|
||||
self.didSetupSticker = true
|
||||
self.disposable.set((sticker
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sticker in
|
||||
if let strongSelf = self, let sticker = sticker {
|
||||
let inputNodeInteraction = ChatMediaInputNodeInteraction(
|
||||
navigateToCollectionId: { _ in
|
||||
},
|
||||
navigateBackToStickers: {
|
||||
},
|
||||
setGifMode: { _ in
|
||||
},
|
||||
openSettings: {
|
||||
},
|
||||
toggleSearch: { _, _, _ in
|
||||
},
|
||||
openPeerSpecificSettings: {
|
||||
},
|
||||
dismissPeerSpecificSettings: {
|
||||
},
|
||||
clearRecentlyUsedStickers: {
|
||||
}
|
||||
)
|
||||
inputNodeInteraction.displayStickerPlaceholder = false
|
||||
|
||||
let index = ItemCollectionItemIndex(index: 0, id: 0)
|
||||
let collectionId = ItemCollectionId(namespace: 0, id: 0)
|
||||
let stickerPackItem = StickerPackItem(index: index, file: sticker, indexKeys: [])
|
||||
let item = ChatMediaInputStickerGridItem(account: strongSelf.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {})
|
||||
strongSelf.stickerItem = item
|
||||
strongSelf.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
|
||||
strongSelf.stickerNode.isVisibleInGrid = true
|
||||
strongSelf.stickerNode.updateIsPanelVisible(true)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
let insets = UIEdgeInsets(top: 15.0, left: 15.0, bottom: 15.0, right: 15.0)
|
||||
let titleSpacing: CGFloat = 5.0
|
||||
let stickerSpacing: CGFloat = 5.0
|
||||
|
||||
var contentWidth: CGFloat = 210.0
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
contentWidth = max(contentWidth, max(titleSize.width, textSize.width))
|
||||
|
||||
contentHeight += titleSize.height + titleSpacing + textSize.height + stickerSpacing + stickerSize.height
|
||||
|
||||
let contentRect = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: contentWidth, height: contentHeight))
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - titleSize.width) / 2.0), y: contentRect.minY), size: titleSize)
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
|
||||
let stickerFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - stickerSize.width) / 2.0), y: textFrame.maxY + stickerSpacing), size: stickerSize)
|
||||
transition.updateFrame(node: self.stickerNode, frame: stickerFrame)
|
||||
|
||||
return contentRect.insetBy(dx: -insets.left, dy: -insets.top).size
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNodeContent, UIGestureRecognizerDelegate {
|
||||
private let account: Account
|
||||
private let interaction: ChatPanelInterfaceInteraction?
|
||||
@ -232,7 +399,8 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - titleSize.width) / 2.0), y: contentRect.minY), size: titleSize)
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
let textFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
|
||||
let stickerFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - stickerSize.width) / 2.0), y: textFrame.maxY + stickerSpacing), size: stickerSize)
|
||||
@ -611,6 +779,7 @@ private enum ChatEmptyNodeContentType {
|
||||
case group
|
||||
case cloud
|
||||
case peerNearby
|
||||
case greeting
|
||||
}
|
||||
|
||||
final class ChatEmptyNode: ASDisplayNode {
|
||||
@ -668,6 +837,8 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
contentType = .group
|
||||
} else if let _ = interfaceState.peerNearbyData {
|
||||
contentType = .peerNearby
|
||||
} else if let _ = peer as? TelegramUser {
|
||||
contentType = .greeting
|
||||
} else {
|
||||
contentType = .regular
|
||||
}
|
||||
@ -692,12 +863,14 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
node = ChatEmptyNodeCloudChatContent()
|
||||
case .peerNearby:
|
||||
node = ChatEmptyNodeNearbyChatContent(account: self.account, interaction: self.interaction)
|
||||
case .greeting:
|
||||
node = ChatEmptyNodeGreetingChatContent(account: self.account, interaction: self.interaction)
|
||||
}
|
||||
self.content = (contentType, node)
|
||||
self.addSubnode(node)
|
||||
contentTransition = .immediate
|
||||
}
|
||||
self.isUserInteractionEnabled = contentType == .peerNearby
|
||||
self.isUserInteractionEnabled = [.regular, .peerNearby].contains(contentType)
|
||||
|
||||
let displayRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
||||
|
||||
@ -716,7 +889,9 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
|
||||
var greetingStickerNode: ASDisplayNode? {
|
||||
if let (_, node) = self.content {
|
||||
if let node = node as? ChatEmptyNodeNearbyChatContent {
|
||||
if let node = node as? ChatEmptyNodeGreetingChatContent {
|
||||
return node.greetingStickerNode
|
||||
} else if let node = node as? ChatEmptyNodeNearbyChatContent {
|
||||
return node.greetingStickerNode
|
||||
}
|
||||
}
|
||||
|
@ -203,11 +203,14 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
if entries.count <= 3 {
|
||||
loop: for entry in view.entries {
|
||||
var isEmptyMedia = false
|
||||
var isPeerJoined = false
|
||||
for media in entry.message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case .groupCreated, .photoUpdated, .channelMigratedFromGroup, .groupMigratedToChannel:
|
||||
isEmptyMedia = true
|
||||
case .peerJoined:
|
||||
isPeerJoined = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -219,7 +222,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
} else if let peer = entry.message.peers[entry.message.id.peerId] as? TelegramChannel, case .group = peer.info, peer.flags.contains(.isCreator) {
|
||||
isCreator = true
|
||||
}
|
||||
if isEmptyMedia && isCreator {
|
||||
if isPeerJoined || (isEmptyMedia && isCreator) {
|
||||
} else {
|
||||
isEmpty = false
|
||||
break loop
|
||||
|
@ -777,7 +777,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
|
||||
func canSendMessagesToChat(_ state: ChatPresentationInterfaceState) -> Bool {
|
||||
if let peer = state.renderedPeer?.peer {
|
||||
if canSendMessagesToPeer(peer) {
|
||||
if true || canSendMessagesToPeer(peer) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
@ -57,6 +57,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
|
||||
private var titleLeftIcon: ChatTitleIcon = .none
|
||||
private var titleRightIcon: ChatTitleIcon = .none
|
||||
private var titleFakeIcon = false
|
||||
private var titleScamIcon = false
|
||||
|
||||
//private var networkStatusNode: ChatTitleNetworkStatusNode?
|
||||
@ -104,6 +105,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
var titleLeftIcon: ChatTitleIcon = .none
|
||||
var titleRightIcon: ChatTitleIcon = .none
|
||||
var titleScamIcon = false
|
||||
var titleFakeIcon = false
|
||||
var isEnabled = true
|
||||
switch titleContent {
|
||||
case let .peer(peerView, _, isScheduledMessages):
|
||||
@ -129,7 +131,11 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
segments = [.text(0, NSAttributedString(string: peer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
}
|
||||
}
|
||||
titleScamIcon = peer.isScam
|
||||
if peer.isFake {
|
||||
titleFakeIcon = true
|
||||
} else if peer.isScam {
|
||||
titleScamIcon = true
|
||||
}
|
||||
}
|
||||
if peerView.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
titleLeftIcon = .lock
|
||||
@ -231,6 +237,12 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleFakeIcon != self.titleFakeIcon {
|
||||
self.titleFakeIcon = titleFakeIcon
|
||||
self.titleCredibilityIconNode.image = titleFakeIcon ? PresentationResourcesChatList.fakeIcon(titleTheme, type: .regular) : nil
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleScamIcon != self.titleScamIcon {
|
||||
self.titleScamIcon = titleScamIcon
|
||||
self.titleCredibilityIconNode.image = titleScamIcon ? PresentationResourcesChatList.scamIcon(titleTheme, type: .regular) : nil
|
||||
|
@ -123,7 +123,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
||||
self?.openPeer?(peer)
|
||||
}
|
||||
case let .chats(chatsNode):
|
||||
chatsNode.peerSelected = { [weak self] peer, _, _ in
|
||||
chatsNode.peerSelected = { [weak self] peer, _, _, _ in
|
||||
self?.openPeer?(.peer(peer: peer, isGlobal: false, participantCount: nil))
|
||||
}
|
||||
chatsNode.additionalCategorySelected = { [weak self] id in
|
||||
|
@ -654,17 +654,17 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
discussionPeer = peer
|
||||
}
|
||||
|
||||
if currentInvitationsContext == nil {
|
||||
var canManageInvitations = false
|
||||
if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
canManageInvitations = true
|
||||
}
|
||||
if canManageInvitations {
|
||||
let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||
invitationsContextPromise.set(.single(invitationsContext))
|
||||
invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
}
|
||||
}
|
||||
// if currentInvitationsContext == nil {
|
||||
// var canManageInvitations = false
|
||||
// if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
// canManageInvitations = true
|
||||
// }
|
||||
// if canManageInvitations {
|
||||
// let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||
// invitationsContextPromise.set(.single(invitationsContext))
|
||||
// invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
// }
|
||||
// }
|
||||
|
||||
return PeerInfoScreenData(
|
||||
peer: peerView.peers[peerId],
|
||||
@ -811,19 +811,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
}
|
||||
}
|
||||
|
||||
if currentInvitationsContext == nil {
|
||||
var canManageInvitations = false
|
||||
if let group = peerViewMainPeer(peerView) as? TelegramGroup, case .creator = group.role {
|
||||
canManageInvitations = true
|
||||
} else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
canManageInvitations = true
|
||||
}
|
||||
if canManageInvitations {
|
||||
let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||
invitationsContextPromise.set(.single(invitationsContext))
|
||||
invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
}
|
||||
}
|
||||
// if currentInvitationsContext == nil {
|
||||
// var canManageInvitations = false
|
||||
// if let group = peerViewMainPeer(peerView) as? TelegramGroup, case .creator = group.role {
|
||||
// canManageInvitations = true
|
||||
// } else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
// canManageInvitations = true
|
||||
// }
|
||||
// if canManageInvitations {
|
||||
// let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||
// invitationsContextPromise.set(.single(invitationsContext))
|
||||
// invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
// }
|
||||
// }
|
||||
|
||||
return PeerInfoScreenData(
|
||||
peer: peerView.peers[groupId],
|
||||
|
@ -2754,6 +2754,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry)
|
||||
}
|
||||
|
||||
var initializedCredibilityIcon = false
|
||||
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
|
||||
self.state = state
|
||||
self.peer = peer
|
||||
@ -2775,19 +2776,35 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let themeUpdated = self.presentationData?.theme !== presentationData.theme
|
||||
self.presentationData = presentationData
|
||||
|
||||
if themeUpdated {
|
||||
if let sourceImage = UIImage(bundleImageName: "Peer Info/VerifiedIcon") {
|
||||
let image = generateImage(sourceImage.size, contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.foregroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: 7.0, dy: 7.0))
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||
context.clip(to: CGRect(origin: CGPoint(), size: size), mask: sourceImage.cgImage!)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
})
|
||||
self.titleCredibilityIconNode.image = image
|
||||
self.titleExpandedCredibilityIconNode.image = image
|
||||
if themeUpdated || !initializedCredibilityIcon {
|
||||
let image: UIImage?
|
||||
if let peer = peer {
|
||||
self.initializedCredibilityIcon = true
|
||||
if peer.isFake {
|
||||
image = PresentationResourcesChatList.fakeIcon(presentationData.theme, type: .regular)
|
||||
} else if peer.isScam {
|
||||
image = PresentationResourcesChatList.scamIcon(presentationData.theme, type: .regular)
|
||||
} else if peer.isVerified {
|
||||
if let sourceImage = UIImage(bundleImageName: "Peer Info/VerifiedIcon") {
|
||||
image = generateImage(sourceImage.size, contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.foregroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: 7.0, dy: 7.0))
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||
context.clip(to: CGRect(origin: CGPoint(), size: size), mask: sourceImage.cgImage!)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
})
|
||||
} else {
|
||||
image = nil
|
||||
}
|
||||
} else {
|
||||
image = nil
|
||||
}
|
||||
} else {
|
||||
image = nil
|
||||
}
|
||||
self.titleCredibilityIconNode.image = image
|
||||
self.titleExpandedCredibilityIconNode.image = image
|
||||
}
|
||||
|
||||
self.regularContentNode.alpha = state.isEditing ? 0.0 : 1.0
|
||||
@ -2925,10 +2942,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
if let image = self.titleCredibilityIconNode.image {
|
||||
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||
self.titleCredibilityIconNode.isHidden = !isVerified
|
||||
|
||||
transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||
self.titleExpandedCredibilityIconNode.isHidden = !isVerified
|
||||
}
|
||||
|
||||
let titleFrame: CGRect
|
||||
|
@ -1219,9 +1219,9 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
invitesText = ""
|
||||
}
|
||||
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
interaction.editingOpenInviteLinksSetup()
|
||||
}))
|
||||
// items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
// interaction.editingOpenInviteLinksSetup()
|
||||
// }))
|
||||
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemDiscussionGroup, label: .text(discussionGroupTitle), text: presentationData.strings.Channel_DiscussionGroup, action: {
|
||||
interaction.editingOpenDiscussionGroupSetup()
|
||||
@ -1303,9 +1303,9 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
invitesText = ""
|
||||
}
|
||||
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
interaction.editingOpenInviteLinksSetup()
|
||||
}))
|
||||
// items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
// interaction.editingOpenInviteLinksSetup()
|
||||
// }))
|
||||
}
|
||||
|
||||
if cachedData.flags.contains(.canChangeUsername) {
|
||||
@ -1387,9 +1387,9 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
invitesText = ""
|
||||
}
|
||||
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
interaction.editingOpenInviteLinksSetup()
|
||||
}))
|
||||
// items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
// interaction.editingOpenInviteLinksSetup()
|
||||
// }))
|
||||
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistory, action: {
|
||||
interaction.editingOpenPreHistorySetup()
|
||||
@ -1412,6 +1412,8 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, text: presentationData.strings.GroupInfo_Administrators, action: {
|
||||
interaction.openParticipantsSection(.admins)
|
||||
}))
|
||||
} else if case .admin = group.role {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3667,9 +3669,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
|
||||
let options: [PeerReportOption]
|
||||
if user {
|
||||
options = [.spam, .violence, .pornography, .childAbuse]
|
||||
options = [.spam, .fake, .violence, .pornography, .childAbuse]
|
||||
} else {
|
||||
options = [.spam, .violence, .pornography, .childAbuse, .copyright, .other]
|
||||
options = [.spam, .fake, .violence, .pornography, .childAbuse, .copyright, .other]
|
||||
}
|
||||
controller.present(peerReportOptionsController(context: self.context, subject: .peer(self.peerId), options: options, present: { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
|
@ -112,7 +112,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
self?.requestActivateSearch?()
|
||||
}
|
||||
|
||||
self.chatListNode.peerSelected = { [weak self] peer, _, _ in
|
||||
self.chatListNode.peerSelected = { [weak self] peer, _, _, _ in
|
||||
self?.chatListNode.clearHighlightAnimated(true)
|
||||
self?.requestOpenPeer?(peer)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user