Merge commit '4930778807c2438620ae2dcb31099102e70ff374'

This commit is contained in:
Ali 2021-11-12 20:40:07 +04:00
commit 4567ce03cf
9 changed files with 210 additions and 65 deletions

View File

@ -7050,9 +7050,19 @@ Sorry for the inconvenience.";
"Chat.JumpToDate" = "Jump to Date"; "Chat.JumpToDate" = "Jump to Date";
"Channel.AdminLog.MessageToggleNoForwardsOn" = "%@ enabled no-forwards";
"Channel.AdminLog.MessageToggleNoForwardsOff" = "%@ disabled no-forwards";
"VoiceChat.DiscussionGroup" = "discussion group"; "VoiceChat.DiscussionGroup" = "discussion group";
"Group.Edit.PrivatePublicLinkAlert" = "Please note that if you choose a public link for your group, anyone will be able to find it in search and join.\n\nDo not create this link if you want your group to stay private."; "Group.Edit.PrivatePublicLinkAlert" = "Please note that if you choose a public link for your group, anyone will be able to find it in search and join.\n\nDo not create this link if you want your group to stay private.";
"Conversation.CopyProtectionInfoGroup" = "Admins restricted members to copy or forward content from this group.";
"Conversation.CopyProtectionInfoChannel" = "Admins restricted members to copy or forward content from this channel.";
"Channel.AdminLog.MessageToggleNoForwardsOn" = "%@ restricted message forwarding";
"Channel.AdminLog.MessageToggleNoForwardsOff" = "%@ allowed message forwarding";
"Group.Setup.ForwardingGroupTitle" = "Forwarding From This Group";
"Group.Setup.ForwardingChannelTitle" = "Forwarding From This Channel";
"Group.Setup.ForwardingEnabled" = "Allow Forwarding";
"Group.Setup.ForwardingDisabled" = "Restrict Forwarding";
"Group.Setup.ForwardingGroupInfo" = "Participants can forward messages from this group and save media files.";
"Group.Setup.ForwardingChannelInfo" = "Participants can forward messages from this channel and save media files.";

View File

@ -351,6 +351,7 @@ private final class InnerTextSelectionTipContainerNode: ASDisplayNode {
self.presentationData = presentationData self.presentationData = presentationData
self.textNode = TextNode() self.textNode = TextNode()
var icon: UIImage?
switch tip { switch tip {
case .textSelection: case .textSelection:
var rawText = self.presentationData.strings.ChatContextMenu_TextSelectionTip var rawText = self.presentationData.strings.ChatContextMenu_TextSelectionTip
@ -362,15 +363,21 @@ private final class InnerTextSelectionTipContainerNode: ASDisplayNode {
self.text = rawText self.text = rawText
self.targetSelectionIndex = 1 self.targetSelectionIndex = 1
} }
icon = UIImage(bundleImageName: "Chat/Context Menu/Tip")
case .messageViewsPrivacy: case .messageViewsPrivacy:
self.text = self.presentationData.strings.ChatContextMenu_MessageViewsPrivacyTip self.text = self.presentationData.strings.ChatContextMenu_MessageViewsPrivacyTip
self.targetSelectionIndex = nil self.targetSelectionIndex = nil
icon = UIImage(bundleImageName: "Chat/Context Menu/Tip")
case let .messageCopyProtection(isChannel):
self.text = isChannel ? self.presentationData.strings.Conversation_CopyProtectionInfoChannel : self.presentationData.strings.Conversation_CopyProtectionInfoGroup
self.targetSelectionIndex = nil
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportCopyright")
} }
self.iconNode = ASImageNode() self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true self.iconNode.displayWithoutProcessing = true
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tip"), color: presentationData.theme.contextMenu.primaryColor) self.iconNode.image = generateTintedImage(image: icon, color: presentationData.theme.contextMenu.primaryColor)
super.init() super.init()

View File

@ -1828,6 +1828,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
public enum Tip { public enum Tip {
case textSelection case textSelection
case messageViewsPrivacy case messageViewsPrivacy
case messageCopyProtection(isChannel: Bool)
} }
public final class ActionsHeight { public final class ActionsHeight {

View File

@ -329,15 +329,18 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
open func animateIn(animateContent: Bool, useSimpleAnimation: Bool) { open func animateIn(animateContent: Bool, useSimpleAnimation: Bool) {
let duration: Double = animateContent ? 0.2 : 0.3 let duration: Double = animateContent ? 0.2 : 0.3
let fadeDuration: Double = 0.2
self.backgroundNode.backgroundColor = self.backgroundNode.backgroundColor?.withAlphaComponent(0.0)
let backgroundColor = self.backgroundNode.backgroundColor ?? .black
self.statusBar?.alpha = 0.0 self.statusBar?.alpha = 0.0
self.navigationBar?.alpha = 0.0 self.navigationBar?.alpha = 0.0
self.footerNode.alpha = 0.0 self.footerNode.alpha = 0.0
self.currentThumbnailContainerNode?.alpha = 0.0 self.currentThumbnailContainerNode?.alpha = 0.0
UIView.animate(withDuration: duration, animations: { self.backgroundNode.layer.animate(from: backgroundColor.withAlphaComponent(0.0).cgColor, to: backgroundColor.cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: fadeDuration)
self.backgroundNode.backgroundColor = self.backgroundNode.backgroundColor?.withAlphaComponent(1.0)
UIView.animate(withDuration: fadeDuration, delay: 0.0, options: [.curveLinear], animations: {
if !self.areControlsHidden { if !self.areControlsHidden {
self.statusBar?.alpha = 1.0 self.statusBar?.alpha = 1.0
self.navigationBar?.alpha = 1.0 self.navigationBar?.alpha = 1.0

View File

@ -279,34 +279,49 @@ final class LocationInfoListItemNode: ListViewItemNode {
let iconNodeFrame = CGRect(origin: CGPoint(x: params.leftInset + inset, y: 10.0), size: CGSize(width: iconSize, height: iconSize)) let iconNodeFrame = CGRect(origin: CGPoint(x: params.leftInset + inset, y: 10.0), size: CGSize(width: iconSize, height: iconSize))
strongSelf.venueIconNode.frame = iconNodeFrame strongSelf.venueIconNode.frame = iconNodeFrame
if case let .ready(drivingTime) = item.drivingTime { var directionsWidth: CGFloat = 93.0
strongSelf.drivingButtonNode?.title = stringForEstimatedDuration(strings: item.presentationData.strings, time: drivingTime, format: { $0 })
if item.drivingTime == .unknown && item.transitTime == .unknown && item.walkingTime == .unknown {
strongSelf.drivingButtonNode?.icon = nil
strongSelf.drivingButtonNode?.title = item.presentationData.strings.Map_GetDirections
if let drivingButtonNode = strongSelf.drivingButtonNode {
let buttonSize = drivingButtonNode.sizeThatFits(contentSize)
directionsWidth = buttonSize.width
}
if let previousDrivingTime = currentItem?.drivingTime, case .calculating = previousDrivingTime { if let previousDrivingTime = currentItem?.drivingTime, case .calculating = previousDrivingTime {
strongSelf.drivingButtonNode?.alpha = 1.0 strongSelf.drivingButtonNode?.alpha = 1.0
strongSelf.drivingButtonNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) strongSelf.drivingButtonNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} }
} } else {
if case let .ready(drivingTime) = item.drivingTime {
if case let .ready(transitTime) = item.transitTime { strongSelf.drivingButtonNode?.title = stringForEstimatedDuration(strings: item.presentationData.strings, time: drivingTime, format: { $0 })
strongSelf.transitButtonNode?.title = stringForEstimatedDuration(strings: item.presentationData.strings, time: transitTime, format: { $0 })
if let previousDrivingTime = currentItem?.drivingTime, case .calculating = previousDrivingTime {
strongSelf.drivingButtonNode?.alpha = 1.0
strongSelf.drivingButtonNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
if let previousTransitTime = currentItem?.transitTime, case .calculating = previousTransitTime { if case let .ready(transitTime) = item.transitTime {
strongSelf.transitButtonNode?.alpha = 1.0 strongSelf.transitButtonNode?.title = stringForEstimatedDuration(strings: item.presentationData.strings, time: transitTime, format: { $0 })
strongSelf.transitButtonNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
if let previousTransitTime = currentItem?.transitTime, case .calculating = previousTransitTime {
strongSelf.transitButtonNode?.alpha = 1.0
strongSelf.transitButtonNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
if case let .ready(walkingTime) = item.walkingTime {
strongSelf.walkingButtonNode?.title = stringForEstimatedDuration(strings: item.presentationData.strings, time: walkingTime, format: { $0 })
if let previousWalkingTime = currentItem?.walkingTime, case .calculating = previousWalkingTime {
strongSelf.walkingButtonNode?.alpha = 1.0
strongSelf.walkingButtonNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
} }
} }
if case let .ready(walkingTime) = item.walkingTime {
strongSelf.walkingButtonNode?.title = stringForEstimatedDuration(strings: item.presentationData.strings, time: walkingTime, format: { $0 })
if let previousWalkingTime = currentItem?.walkingTime, case .calculating = previousWalkingTime {
strongSelf.walkingButtonNode?.alpha = 1.0
strongSelf.walkingButtonNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let directionsWidth: CGFloat = 93.0
let directionsSpacing: CGFloat = 8.0 let directionsSpacing: CGFloat = 8.0
if case .calculating = item.drivingTime, case .calculating = item.transitTime, case .calculating = item.walkingTime { if case .calculating = item.drivingTime, case .calculating = item.transitTime, case .calculating = item.walkingTime {

View File

@ -34,8 +34,9 @@ private final class ChannelVisibilityControllerArguments {
let linkContextAction: (ASDisplayNode, ContextGesture?) -> Void let linkContextAction: (ASDisplayNode, ContextGesture?) -> Void
let manageInviteLinks: () -> Void let manageInviteLinks: () -> Void
let openLink: (ExportedInvitation) -> Void let openLink: (ExportedInvitation) -> Void
let toggleForwarding: (Bool) -> Void
init(context: AccountContext, updateCurrentType: @escaping (CurrentChannelType) -> Void, updatePublicLinkText: @escaping (String?, String) -> Void, scrollToPublicLinkText: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, revokePeerId: @escaping (PeerId) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, shareLink: @escaping (ExportedInvitation) -> Void, linkContextAction: @escaping (ASDisplayNode, ContextGesture?) -> Void, manageInviteLinks: @escaping () -> Void, openLink: @escaping (ExportedInvitation) -> Void) { init(context: AccountContext, updateCurrentType: @escaping (CurrentChannelType) -> Void, updatePublicLinkText: @escaping (String?, String) -> Void, scrollToPublicLinkText: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, revokePeerId: @escaping (PeerId) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, shareLink: @escaping (ExportedInvitation) -> Void, linkContextAction: @escaping (ASDisplayNode, ContextGesture?) -> Void, manageInviteLinks: @escaping () -> Void, openLink: @escaping (ExportedInvitation) -> Void, toggleForwarding: @escaping (Bool) -> Void) {
self.context = context self.context = context
self.updateCurrentType = updateCurrentType self.updateCurrentType = updateCurrentType
self.updatePublicLinkText = updatePublicLinkText self.updatePublicLinkText = updatePublicLinkText
@ -47,6 +48,7 @@ private final class ChannelVisibilityControllerArguments {
self.linkContextAction = linkContextAction self.linkContextAction = linkContextAction
self.manageInviteLinks = manageInviteLinks self.manageInviteLinks = manageInviteLinks
self.openLink = openLink self.openLink = openLink
self.toggleForwarding = toggleForwarding
} }
} }
@ -54,6 +56,7 @@ private enum ChannelVisibilitySection: Int32 {
case type case type
case link case link
case linkActions case linkActions
case forwarding
} }
private enum ChannelVisibilityEntryTag: ItemListItemTag { private enum ChannelVisibilityEntryTag: ItemListItemTag {
@ -90,6 +93,11 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
case existingLinksInfo(PresentationTheme, String) case existingLinksInfo(PresentationTheme, String)
case existingLinkPeerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool) case existingLinkPeerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool)
case forwardingHeader(PresentationTheme, String)
case forwardingEnabled(PresentationTheme, String, Bool)
case forwardingDisabled(PresentationTheme, String, Bool)
case forwardingInfo(PresentationTheme, String)
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
case .typeHeader, .typePublic, .typePrivate, .typeInfo: case .typeHeader, .typePublic, .typePrivate, .typeInfo:
@ -100,6 +108,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
return ChannelVisibilitySection.linkActions.rawValue return ChannelVisibilitySection.linkActions.rawValue
case .existingLinksInfo, .existingLinkPeerItem: case .existingLinksInfo, .existingLinkPeerItem:
return ChannelVisibilitySection.link.rawValue return ChannelVisibilitySection.link.rawValue
case .forwardingHeader, .forwardingEnabled, .forwardingDisabled, .forwardingInfo:
return ChannelVisibilitySection.forwarding.rawValue
} }
} }
@ -137,6 +147,14 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
return 1000 return 1000
case .privateLinkManageInfo: case .privateLinkManageInfo:
return 1001 return 1001
case .forwardingHeader:
return 1002
case .forwardingEnabled:
return 1003
case .forwardingDisabled:
return 1004
case .forwardingInfo:
return 1005
} }
} }
@ -262,6 +280,30 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .forwardingHeader(lhsTheme, lhsText):
if case let .forwardingHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .forwardingEnabled(lhsTheme, lhsText, lhsValue):
if case let .forwardingEnabled(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .forwardingDisabled(lhsTheme, lhsText, lhsValue):
if case let .forwardingDisabled(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .forwardingInfo(lhsTheme, lhsText):
if case let .forwardingInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
} }
} }
@ -359,6 +401,18 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
}, removePeer: { peerId in }, removePeer: { peerId in
arguments.revokePeerId(peerId) arguments.revokePeerId(peerId)
}) })
case let .forwardingHeader(_, title):
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
case let .forwardingEnabled(_, text, selected):
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.toggleForwarding(true)
})
case let .forwardingDisabled(_, text, selected):
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.toggleForwarding(false)
})
case let .forwardingInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
} }
} }
} }
@ -381,6 +435,7 @@ private struct ChannelVisibilityControllerState: Equatable {
let revealedRevokePeerId: PeerId? let revealedRevokePeerId: PeerId?
let revokingPeerId: PeerId? let revokingPeerId: PeerId?
let revokingPrivateLink: Bool let revokingPrivateLink: Bool
let forwardingEnabled: Bool?
init() { init() {
self.selectedType = nil self.selectedType = nil
@ -390,9 +445,10 @@ private struct ChannelVisibilityControllerState: Equatable {
self.revealedRevokePeerId = nil self.revealedRevokePeerId = nil
self.revokingPeerId = nil self.revokingPeerId = nil
self.revokingPrivateLink = false self.revokingPrivateLink = false
self.forwardingEnabled = nil
} }
init(selectedType: CurrentChannelType?, editingPublicLinkText: String?, addressNameValidationStatus: AddressNameValidationStatus?, updatingAddressName: Bool, revealedRevokePeerId: PeerId?, revokingPeerId: PeerId?, revokingPrivateLink: Bool) { init(selectedType: CurrentChannelType?, editingPublicLinkText: String?, addressNameValidationStatus: AddressNameValidationStatus?, updatingAddressName: Bool, revealedRevokePeerId: PeerId?, revokingPeerId: PeerId?, revokingPrivateLink: Bool, forwardingEnabled: Bool?) {
self.selectedType = selectedType self.selectedType = selectedType
self.editingPublicLinkText = editingPublicLinkText self.editingPublicLinkText = editingPublicLinkText
self.addressNameValidationStatus = addressNameValidationStatus self.addressNameValidationStatus = addressNameValidationStatus
@ -400,6 +456,7 @@ private struct ChannelVisibilityControllerState: Equatable {
self.revealedRevokePeerId = revealedRevokePeerId self.revealedRevokePeerId = revealedRevokePeerId
self.revokingPeerId = revokingPeerId self.revokingPeerId = revokingPeerId
self.revokingPrivateLink = revokingPrivateLink self.revokingPrivateLink = revokingPrivateLink
self.forwardingEnabled = forwardingEnabled
} }
static func ==(lhs: ChannelVisibilityControllerState, rhs: ChannelVisibilityControllerState) -> Bool { static func ==(lhs: ChannelVisibilityControllerState, rhs: ChannelVisibilityControllerState) -> Bool {
@ -424,35 +481,42 @@ private struct ChannelVisibilityControllerState: Equatable {
if lhs.revokingPrivateLink != rhs.revokingPrivateLink { if lhs.revokingPrivateLink != rhs.revokingPrivateLink {
return false return false
} }
if lhs.forwardingEnabled != rhs.forwardingEnabled {
return false
}
return true return true
} }
func withUpdatedSelectedType(_ selectedType: CurrentChannelType?) -> ChannelVisibilityControllerState { func withUpdatedSelectedType(_ selectedType: CurrentChannelType?) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: self.updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink) return ChannelVisibilityControllerState(selectedType: selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: self.updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink, forwardingEnabled: self.forwardingEnabled)
} }
func withUpdatedEditingPublicLinkText(_ editingPublicLinkText: String?) -> ChannelVisibilityControllerState { func withUpdatedEditingPublicLinkText(_ editingPublicLinkText: String?) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: self.updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink) return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: self.updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink, forwardingEnabled: self.forwardingEnabled)
} }
func withUpdatedAddressNameValidationStatus(_ addressNameValidationStatus: AddressNameValidationStatus?) -> ChannelVisibilityControllerState { func withUpdatedAddressNameValidationStatus(_ addressNameValidationStatus: AddressNameValidationStatus?) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: addressNameValidationStatus, updatingAddressName: self.updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink) return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: addressNameValidationStatus, updatingAddressName: self.updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink, forwardingEnabled: self.forwardingEnabled)
} }
func withUpdatedUpdatingAddressName(_ updatingAddressName: Bool) -> ChannelVisibilityControllerState { func withUpdatedUpdatingAddressName(_ updatingAddressName: Bool) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink) return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink, forwardingEnabled: self.forwardingEnabled)
} }
func withUpdatedRevealedRevokePeerId(_ revealedRevokePeerId: PeerId?) -> ChannelVisibilityControllerState { func withUpdatedRevealedRevokePeerId(_ revealedRevokePeerId: PeerId?) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink) return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink, forwardingEnabled: self.forwardingEnabled)
} }
func withUpdatedRevokingPeerId(_ revokingPeerId: PeerId?) -> ChannelVisibilityControllerState { func withUpdatedRevokingPeerId(_ revokingPeerId: PeerId?) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: revokingPeerId, revokingPrivateLink: self.revokingPrivateLink) return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: revokingPeerId, revokingPrivateLink: self.revokingPrivateLink, forwardingEnabled: self.forwardingEnabled)
} }
func withUpdatedRevokingPrivateLink(_ revokingPrivateLink: Bool) -> ChannelVisibilityControllerState { func withUpdatedRevokingPrivateLink(_ revokingPrivateLink: Bool) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: revokingPrivateLink) return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: revokingPrivateLink, forwardingEnabled: self.forwardingEnabled)
}
func withUpdatedForwardingEnabled(_ forwardingEnabled: Bool) -> ChannelVisibilityControllerState {
return ChannelVisibilityControllerState(selectedType: self.selectedType, editingPublicLinkText: self.editingPublicLinkText, addressNameValidationStatus: self.addressNameValidationStatus, updatingAddressName: updatingAddressName, revealedRevokePeerId: self.revealedRevokePeerId, revokingPeerId: self.revokingPeerId, revokingPrivateLink: self.revokingPrivateLink, forwardingEnabled: forwardingEnabled)
} }
} }
@ -484,6 +548,17 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
} }
} }
let forwardingEnabled: Bool
if let enabled = state.forwardingEnabled {
forwardingEnabled = enabled
} else {
if peer.flags.contains(.copyProtectionEnabled) {
forwardingEnabled = false
} else {
forwardingEnabled = true
}
}
let currentAddressName: String let currentAddressName: String
if let current = state.editingPublicLinkText { if let current = state.editingPublicLinkText {
currentAddressName = current currentAddressName = current
@ -633,6 +708,12 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo)) entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
} }
} }
entries.append(.forwardingHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased() : presentationData.strings.Group_Setup_ForwardingChannelTitle.uppercased()))
entries.append(.forwardingEnabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingEnabled, forwardingEnabled))
entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingDisabled, !forwardingEnabled))
entries.append(.forwardingInfo(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_ForwardingGroupInfo : presentationData.strings.Group_Setup_ForwardingChannelInfo))
} else if let _ = view.peers[view.peerId] as? TelegramGroup { } else if let _ = view.peers[view.peerId] as? TelegramGroup {
switch mode { switch mode {
case .privateLink: case .privateLink:
@ -748,6 +829,18 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
} }
} }
} }
let forwardingEnabled: Bool
if let enabled = state.forwardingEnabled {
forwardingEnabled = enabled
} else {
forwardingEnabled = true
}
entries.append(.forwardingHeader(presentationData.theme, presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased()))
entries.append(.forwardingEnabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingEnabled, forwardingEnabled))
entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingEnabled, !forwardingEnabled))
entries.append(.forwardingInfo(presentationData.theme, presentationData.strings.Group_Setup_ForwardingGroupInfo))
} }
return entries return entries
@ -862,6 +955,9 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
let revokeLinkDisposable = MetaDisposable() let revokeLinkDisposable = MetaDisposable()
actionsDisposable.add(revokeLinkDisposable) actionsDisposable.add(revokeLinkDisposable)
let toggleCopyProtectionDisposable = MetaDisposable()
actionsDisposable.add(toggleCopyProtectionDisposable)
let arguments = ChannelVisibilityControllerArguments(context: context, updateCurrentType: { type in let arguments = ChannelVisibilityControllerArguments(context: context, updateCurrentType: { type in
updateState { state in updateState { state in
return state.withUpdatedSelectedType(type) return state.withUpdatedSelectedType(type)
@ -1062,6 +1158,10 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
}, openLink: { invite in }, openLink: { invite in
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: nil, revokedInvitationsContext: nil, importersContext: nil) let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: nil, revokedInvitationsContext: nil, importersContext: nil)
pushControllerImpl?(controller) pushControllerImpl?(controller)
}, toggleForwarding: { value in
updateState { state in
return state.withUpdatedForwardingEnabled(value)
}
}) })
let peerView = context.account.viewTracker.peerView(peerId) let peerView = context.account.viewTracker.peerView(peerId)
@ -1127,6 +1227,10 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
return state return state
} }
if let updatedCopyProtection = state.forwardingEnabled {
toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start())
}
if let updatedAddressNameValue = updatedAddressNameValue { if let updatedAddressNameValue = updatedAddressNameValue {
let invokeAction: () -> Void = { let invokeAction: () -> Void = {
updateState { state in updateState { state in
@ -1203,6 +1307,10 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
return state return state
} }
if let updatedCopyProtection = state.forwardingEnabled {
toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start())
}
if let updatedAddressNameValue = updatedAddressNameValue { if let updatedAddressNameValue = updatedAddressNameValue {
let invokeAction: () -> Void = { let invokeAction: () -> Void = {
updateState { state in updateState { state in

View File

@ -216,6 +216,11 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
} }
} }
public func sizeThatFits(_ constrainedSize: CGSize) -> CGSize {
let titleSize = self.titleNode.updateLayout(constrainedSize)
return CGSize(width: titleSize.width + 20.0, height: self.buttonHeight)
}
public func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { public func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
return self.updateLayout(width: width, previousSubtitle: self.subtitle, transition: transition) return self.updateLayout(width: width, previousSubtitle: self.subtitle, transition: transition)
} }

View File

@ -280,7 +280,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var clearCacheDisposable: MetaDisposable? private var clearCacheDisposable: MetaDisposable?
private var bankCardDisposable: MetaDisposable? private var bankCardDisposable: MetaDisposable?
private var hasActiveGroupCallDisposable: Disposable? private var hasActiveGroupCallDisposable: Disposable?
private var sendAsPeersDisposable: Disposable?
private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true) private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true)
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true) private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
private let unblockingPeer = ValuePromise<Bool>(false, ignoreRepeated: true) private let unblockingPeer = ValuePromise<Bool>(false, ignoreRepeated: true)
@ -943,11 +944,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var tip: ContextController.Tip? var tip: ContextController.Tip?
if tip == nil { if tip == nil {
let numberOfComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines).count if message.isCopyProtected() {
let displayTextSelectionTip = numberOfComponents >= 3 && !message.text.isEmpty && chatTextSelectionTips < 3 && !message.isCopyProtected() var isChannel = false
if displayTextSelectionTip { if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = channel.info {
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start() isChannel = true
tip = .textSelection }
tip = .messageCopyProtection(isChannel: isChannel)
} else {
let numberOfComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines).count
let displayTextSelectionTip = numberOfComponents >= 3 && !message.text.isEmpty && chatTextSelectionTips < 3
if displayTextSelectionTip {
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
tip = .textSelection
}
} }
} }
@ -1145,7 +1154,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}) })
} else { } else {
strongSelf.sendMessages(messages) let transformedMessages = strongSelf.transformEnqueueMessages(messages)
strongSelf.sendMessages(transformedMessages)
} }
return true return true
}, sendGif: { [weak self] fileReference, sourceNode, sourceRect, silentPosting, schedule in }, sendGif: { [weak self] fileReference, sourceNode, sourceRect, silentPosting, schedule in
@ -1180,6 +1190,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}) })
} else { } else {
messages = strongSelf.transformEnqueueMessages(messages)
strongSelf.sendMessages(messages) strongSelf.sendMessages(messages)
} }
} }
@ -3838,7 +3849,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.inputActivityDisposable = (self.typingActivityPromise.get() self.inputActivityDisposable = (self.typingActivityPromise.get()
|> deliverOnMainQueue).start(next: { [weak self] value in |> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self { if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil {
strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: value) strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: value)
} }
}) })
@ -3852,7 +3863,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
|> deliverOnMainQueue).start(next: { [weak self] value in |> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self { if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil {
if value { if value {
strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: false) strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: false)
} }
@ -3862,7 +3873,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.recordingActivityDisposable = (self.recordingActivityPromise.get() self.recordingActivityDisposable = (self.recordingActivityPromise.get()
|> deliverOnMainQueue).start(next: { [weak self] value in |> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self { if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil {
strongSelf.acquiredRecordingActivityDisposable?.dispose() strongSelf.acquiredRecordingActivityDisposable?.dispose()
switch value { switch value {
case .voice: case .voice:
@ -4176,6 +4187,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.importStateDisposable?.dispose() self.importStateDisposable?.dispose()
self.nextChannelToReadDisposable?.dispose() self.nextChannelToReadDisposable?.dispose()
self.inviteRequestsDisposable.dispose() self.inviteRequestsDisposable.dispose()
self.sendAsPeersDisposable?.dispose()
} }
public func updatePresentationMode(_ mode: ChatControllerPresentationMode) { public func updatePresentationMode(_ mode: ChatControllerPresentationMode) {
@ -4565,7 +4577,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return [FoundPeer(peer: peer, subscribers: nil)] return [FoundPeer(peer: peer, subscribers: nil)]
} }
let _ = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.context.account.postbox.peerView(id: self.chatLocation.peerId), self.context.engine.peers.sendAsAvailablePeers(peerId: self.chatLocation.peerId))) self.sendAsPeersDisposable = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.context.account.postbox.peerView(id: self.chatLocation.peerId), self.context.engine.peers.sendAsAvailablePeers(peerId: self.chatLocation.peerId)))
.start(next: { [weak self] currentAccountPeer, peerView, peers in .start(next: { [weak self] currentAccountPeer, peerView, peers in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -5335,7 +5347,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
self.chatDisplayNode.updateTypingActivity = { [weak self] value in self.chatDisplayNode.updateTypingActivity = { [weak self] value in
if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages { if let strongSelf = self {
if value { if value {
strongSelf.typingActivityPromise.set(Signal<Bool, NoError>.single(true) strongSelf.typingActivityPromise.set(Signal<Bool, NoError>.single(true)
|> then( |> then(

View File

@ -436,7 +436,6 @@ private final class PeerInfoInteraction {
let editingOpenInviteLinksSetup: () -> Void let editingOpenInviteLinksSetup: () -> Void
let editingOpenDiscussionGroupSetup: () -> Void let editingOpenDiscussionGroupSetup: () -> Void
let editingToggleMessageSignatures: (Bool) -> Void let editingToggleMessageSignatures: (Bool) -> Void
let editingToggleChannelMessageCopyProtection: (Bool) -> Void
let openParticipantsSection: (PeerInfoParticipantsSection) -> Void let openParticipantsSection: (PeerInfoParticipantsSection) -> Void
let editingOpenPreHistorySetup: () -> Void let editingOpenPreHistorySetup: () -> Void
let editingOpenAutoremoveMesages: () -> Void let editingOpenAutoremoveMesages: () -> Void
@ -477,7 +476,6 @@ private final class PeerInfoInteraction {
editingOpenInviteLinksSetup: @escaping () -> Void, editingOpenInviteLinksSetup: @escaping () -> Void,
editingOpenDiscussionGroupSetup: @escaping () -> Void, editingOpenDiscussionGroupSetup: @escaping () -> Void,
editingToggleMessageSignatures: @escaping (Bool) -> Void, editingToggleMessageSignatures: @escaping (Bool) -> Void,
editingToggleChannelMessageCopyProtection: @escaping (Bool) -> Void,
openParticipantsSection: @escaping (PeerInfoParticipantsSection) -> Void, openParticipantsSection: @escaping (PeerInfoParticipantsSection) -> Void,
editingOpenPreHistorySetup: @escaping () -> Void, editingOpenPreHistorySetup: @escaping () -> Void,
editingOpenAutoremoveMesages: @escaping () -> Void, editingOpenAutoremoveMesages: @escaping () -> Void,
@ -517,7 +515,6 @@ private final class PeerInfoInteraction {
self.editingOpenInviteLinksSetup = editingOpenInviteLinksSetup self.editingOpenInviteLinksSetup = editingOpenInviteLinksSetup
self.editingOpenDiscussionGroupSetup = editingOpenDiscussionGroupSetup self.editingOpenDiscussionGroupSetup = editingOpenDiscussionGroupSetup
self.editingToggleMessageSignatures = editingToggleMessageSignatures self.editingToggleMessageSignatures = editingToggleMessageSignatures
self.editingToggleChannelMessageCopyProtection = editingToggleChannelMessageCopyProtection
self.openParticipantsSection = openParticipantsSection self.openParticipantsSection = openParticipantsSection
self.editingOpenPreHistorySetup = editingOpenPreHistorySetup self.editingOpenPreHistorySetup = editingOpenPreHistorySetup
self.editingOpenAutoremoveMesages = editingOpenAutoremoveMesages self.editingOpenAutoremoveMesages = editingOpenAutoremoveMesages
@ -1117,7 +1114,6 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
let ItemDiscussionGroup = 3 let ItemDiscussionGroup = 3
let ItemSignMessages = 4 let ItemSignMessages = 4
let ItemSignMessagesHelp = 5 let ItemSignMessagesHelp = 5
let ItemCopyProtection = 6
if channel.flags.contains(.isCreator) { if channel.flags.contains(.isCreator) {
let linkText: String let linkText: String
@ -1166,7 +1162,6 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
if channel.flags.contains(.isCreator) || (channel.adminRights != nil && channel.hasPermission(.sendMessages)) { if channel.flags.contains(.isCreator) || (channel.adminRights != nil && channel.hasPermission(.sendMessages)) {
let messagesShouldHaveSignatures: Bool let messagesShouldHaveSignatures: Bool
let messagesCopyProtection = channel.flags.contains(.copyProtectionEnabled)
switch channel.info { switch channel.info {
case let .broadcast(info): case let .broadcast(info):
messagesShouldHaveSignatures = info.flags.contains(.messagesShouldHaveSignatures) messagesShouldHaveSignatures = info.flags.contains(.messagesShouldHaveSignatures)
@ -1177,10 +1172,6 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
interaction.editingToggleMessageSignatures(value) interaction.editingToggleMessageSignatures(value)
})) }))
items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemSignMessagesHelp, text: presentationData.strings.Channel_SignMessages_Help)) items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemSignMessagesHelp, text: presentationData.strings.Channel_SignMessages_Help))
items[.peerAdditionalSettings]!.append(PeerInfoScreenSwitchItem(id: ItemCopyProtection, text: "Restrict Saving Content", value: messagesCopyProtection, icon: UIImage(bundleImageName: "Chat/Info/GroupSignIcon"), toggled: { value in
interaction.editingToggleChannelMessageCopyProtection(value)
}))
} }
case .group: case .group:
let ItemUsername = 101 let ItemUsername = 101
@ -1597,9 +1588,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
editingToggleMessageSignatures: { [weak self] value in editingToggleMessageSignatures: { [weak self] value in
self?.editingToggleMessageSignatures(value: value) self?.editingToggleMessageSignatures(value: value)
}, },
editingToggleChannelMessageCopyProtection: { [weak self] value in
self?.editingToggleMessageCopyProtection(value: value)
},
openParticipantsSection: { [weak self] section in openParticipantsSection: { [weak self] section in
self?.openParticipantsSection(section: section) self?.openParticipantsSection(section: section)
}, },
@ -4708,11 +4696,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
private func editingToggleMessageSignatures(value: Bool) { private func editingToggleMessageSignatures(value: Bool) {
self.toggleShouldChannelMessagesSignaturesDisposable.set(self.context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: self.peerId, enabled: value).start()) self.toggleShouldChannelMessagesSignaturesDisposable.set(self.context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: self.peerId, enabled: value).start())
} }
private func editingToggleMessageCopyProtection(value: Bool) {
self.toggleMessageCopyProtectionDisposable.set(self.context.engine.peers.toggleMessageCopyProtection(peerId: self.peerId, enabled: value).start())
}
private func openParticipantsSection(section: PeerInfoParticipantsSection) { private func openParticipantsSection(section: PeerInfoParticipantsSection) {
guard let data = self.data, let peer = data.peer else { guard let data = self.data, let peer = data.peer else {
return return