mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Various improvements
This commit is contained in:
parent
1d80861e09
commit
4203d70414
20 changed files with 408 additions and 350 deletions
|
|
@ -15825,3 +15825,6 @@ Error: %8$@";
|
|||
"Group.EditAdmin.MemberTagInfo" = "Add short tag next to %@'s name.";
|
||||
|
||||
"Channel.Owner.Title" = "Owner";
|
||||
|
||||
"Channel.AdminLog.EditRankOwn" = "Edit Member Tag";
|
||||
"Channel.AdminLog.EditRank" = "Edit Member Tags";
|
||||
|
|
|
|||
|
|
@ -1528,7 +1528,6 @@ public protocol SharedAccountContext: AnyObject {
|
|||
func makeLoginEmailSetupController(context: AccountContext, blocking: Bool, emailPattern: String?, canAutoDismissIfNeeded: Bool, navigationController: NavigationController?, completion: @escaping () -> Void, dismiss: @escaping () -> Void) -> ViewController
|
||||
func makePasskeySetupController(context: AccountContext, displaySkip: Bool, navigationController: NavigationController?, completion: @escaping () -> Void, dismiss: @escaping () -> Void) -> ViewController
|
||||
|
||||
func makeChatParticipantRightsScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) -> ViewController
|
||||
func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) -> ViewController
|
||||
|
||||
func makePeerCopyProtectionInfoScreen(context: AccountContext, completion: @escaping () -> Void) -> ViewController
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||
}
|
||||
|
||||
joinChannelDisposable.set((createSignal
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
}, error: { _ in
|
||||
if let chatListController = chatListController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
|
|
|||
|
|
@ -5242,6 +5242,36 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
}
|
||||
}
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, completed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
Queue.mainQueue().after(0.5) {
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
||||
guard let self, let peer = peer?._asPeer() else {
|
||||
return
|
||||
}
|
||||
var canEditRank = false
|
||||
if let channel = peer as? TelegramChannel, channel.hasPermission(.editRank) {
|
||||
canEditRank = true
|
||||
} else if let group = peer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) {
|
||||
canEditRank = true
|
||||
}
|
||||
//TODO:localize
|
||||
if canEditRank {
|
||||
let context = self.context
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: nil, text: "You joined the group", cancel: "Add Tag", destructive: false), elevatedLayout: true, action: { [weak self] action in
|
||||
if let self, case .undo = action {
|
||||
let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peerId, participantId: context.account.peerId, rank: nil)
|
||||
self.push(tagController)
|
||||
}
|
||||
return true
|
||||
})
|
||||
self.present(controller, in: .current)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}))
|
||||
case .savedMessagesChats:
|
||||
|
|
|
|||
|
|
@ -173,6 +173,18 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
|||
case firstNameFirst
|
||||
case lastNameFirst
|
||||
}
|
||||
|
||||
public struct LabelText {
|
||||
let text: String
|
||||
let color: UIColor
|
||||
let hasBackground: Bool
|
||||
|
||||
public init(text: String, color: UIColor, hasBackground: Bool) {
|
||||
self.text = text
|
||||
self.color = color
|
||||
self.hasBackground = hasBackground
|
||||
}
|
||||
}
|
||||
|
||||
let presentationData: ItemListPresentationData
|
||||
let style: ItemListStyle
|
||||
|
|
@ -186,7 +198,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
|||
public let peer: ContactsPeerItemPeer
|
||||
let status: ContactsPeerItemStatus
|
||||
let badge: ContactsPeerItemBadge?
|
||||
let rightLabelText: String?
|
||||
let rightLabelText: LabelText?
|
||||
let requiresPremiumForMessaging: Bool
|
||||
let enabled: Bool
|
||||
let selection: ContactsPeerItemSelection
|
||||
|
|
@ -233,7 +245,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
|||
peer: ContactsPeerItemPeer,
|
||||
status: ContactsPeerItemStatus,
|
||||
badge: ContactsPeerItemBadge? = nil,
|
||||
rightLabelText: String? = nil,
|
||||
rightLabelText: LabelText? = nil,
|
||||
requiresPremiumForMessaging: Bool = false,
|
||||
enabled: Bool,
|
||||
selection: ContactsPeerItemSelection,
|
||||
|
|
@ -471,6 +483,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
private var actionButtonNodes: [HighlightableButtonNode]?
|
||||
private var moreButtonNode: MoreButtonNode?
|
||||
private var arrowButtonNode: HighlightableButtonNode?
|
||||
private let labelBadgeNode: ASImageNode
|
||||
private var rightLabelTextNode: TextNode?
|
||||
|
||||
private var adButton: HighlightableButtonNode?
|
||||
|
|
@ -587,6 +600,11 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
self.titleNode = TextNode()
|
||||
self.statusNode = TextNodeWithEntities()
|
||||
|
||||
self.labelBadgeNode = ASImageNode()
|
||||
self.labelBadgeNode.displayWithoutProcessing = true
|
||||
self.labelBadgeNode.displaysAsynchronously = false
|
||||
self.labelBadgeNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
|
|
@ -753,6 +771,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
|
||||
let currentItem = self.layoutParams?.0
|
||||
|
||||
let currentHasBadge = self.labelBadgeNode.image != nil
|
||||
|
||||
return { [weak self] item, params, first, last, firstWithHeader, neighbors in
|
||||
var updatedTheme: PresentationTheme?
|
||||
|
||||
|
|
@ -807,7 +827,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
|
||||
var rightLabelTextLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||
if let rightLabelText = item.rightLabelText {
|
||||
let rightLabelTextLayoutAndApplyValue = makeRightLabelTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: rightLabelText, font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 20.0, height: 100.0)))
|
||||
let rightLabelTextLayoutAndApplyValue = makeRightLabelTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: rightLabelText.text, font: statusFont, textColor: rightLabelText.color), maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 20.0, height: 100.0)))
|
||||
rightLabelTextLayoutAndApply = rightLabelTextLayoutAndApplyValue
|
||||
rightInset -= 6.0 + rightLabelTextLayoutAndApplyValue.0.size.width
|
||||
}
|
||||
|
|
@ -1208,6 +1228,21 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
peerRevealOptions = []
|
||||
}
|
||||
|
||||
var badgeColor: UIColor?
|
||||
if let rightLabelText = item.rightLabelText, rightLabelText.hasBackground {
|
||||
badgeColor = rightLabelText.color.withMultipliedAlpha(0.1)
|
||||
}
|
||||
|
||||
var updatedLabelBadgeImage: UIImage?
|
||||
let badgeDiameter: CGFloat = 20.0
|
||||
if currentItem?.presentationData.theme !== item.presentationData.theme {
|
||||
if let badgeColor = badgeColor {
|
||||
updatedLabelBadgeImage = generateStretchableFilledCircleImage(diameter: badgeDiameter, color: badgeColor)
|
||||
}
|
||||
} else if let badgeColor = badgeColor, !currentHasBadge {
|
||||
updatedLabelBadgeImage = generateStretchableFilledCircleImage(diameter: badgeDiameter, color: badgeColor)
|
||||
}
|
||||
|
||||
return (nodeLayout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (.complete(), { [weak strongSelf] animated, synchronousLoads in
|
||||
|
|
@ -1808,6 +1843,21 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
}
|
||||
}
|
||||
|
||||
if let updateBadgeImage = updatedLabelBadgeImage {
|
||||
if strongSelf.labelBadgeNode.supernode == nil {
|
||||
if let rightLabelTextNode = strongSelf.rightLabelTextNode {
|
||||
strongSelf.offsetContainerNode.insertSubnode(strongSelf.labelBadgeNode, belowSubnode: rightLabelTextNode)
|
||||
} else {
|
||||
strongSelf.offsetContainerNode.addSubnode(strongSelf.labelBadgeNode)
|
||||
}
|
||||
}
|
||||
strongSelf.labelBadgeNode.image = updateBadgeImage
|
||||
}
|
||||
if badgeColor == nil && strongSelf.labelBadgeNode.supernode != nil {
|
||||
strongSelf.labelBadgeNode.image = nil
|
||||
strongSelf.labelBadgeNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let (rightLabelTextLayout, rightLabelTextApply) = rightLabelTextLayoutAndApply {
|
||||
let rightLabelTextNode = rightLabelTextApply()
|
||||
var rightLabelTextTransition = transition
|
||||
|
|
@ -1818,11 +1868,25 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
rightLabelTextTransition = .immediate
|
||||
}
|
||||
|
||||
var rightLabelTextFrame = CGRect(x: revealOffset + params.width - params.rightInset - 8.0 - rightLabelTextLayout.size.width, y: floor((nodeLayout.contentSize.height - rightLabelTextLayout.size.height) / 2.0), width: rightLabelTextLayout.size.width, height: rightLabelTextLayout.size.height)
|
||||
let rightInset: CGFloat
|
||||
switch item.systemStyle {
|
||||
case .glass:
|
||||
rightInset = 16.0
|
||||
case .legacy:
|
||||
rightInset = 8.0
|
||||
}
|
||||
|
||||
var rightLabelTextFrame = CGRect(x: revealOffset + params.width - params.rightInset - rightInset - rightLabelTextLayout.size.width, y: floor((nodeLayout.contentSize.height - rightLabelTextLayout.size.height) / 2.0), width: rightLabelTextLayout.size.width, height: rightLabelTextLayout.size.height)
|
||||
if strongSelf.labelBadgeNode.image != nil {
|
||||
rightLabelTextFrame.origin.x -= 6.0
|
||||
}
|
||||
if let arrowButtonImage = arrowButtonImage {
|
||||
rightLabelTextFrame.origin.x -= arrowButtonImage.size.width + 6.0
|
||||
}
|
||||
|
||||
let badgeWidth = max(badgeDiameter, rightLabelTextLayout.size.width + 12.0)
|
||||
strongSelf.labelBadgeNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(rightLabelTextFrame.midX - badgeWidth * 0.5), y: floorToScreenPixels(rightLabelTextFrame.midY - badgeDiameter * 0.5)), size: CGSize(width: badgeWidth, height: badgeDiameter))
|
||||
|
||||
rightLabelTextNode.bounds = CGRect(origin: CGPoint(), size: rightLabelTextFrame.size)
|
||||
rightLabelTextTransition.updatePosition(node: rightLabelTextNode, position: rightLabelTextFrame.center)
|
||||
} else if let rightLabelTextNode = strongSelf.rightLabelTextNode {
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
|||
return entries
|
||||
}
|
||||
|
||||
public func channelBannedMemberController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, memberId: PeerId, initialParticipant: ChannelParticipant?, updated: @escaping (TelegramChatBannedRights?) -> Void, upgradedToSupergroup: @escaping (PeerId, @escaping () -> Void) -> Void) -> ViewController {
|
||||
public func channelBannedMemberController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, memberId: PeerId, editMember: Bool = false, initialParticipant: ChannelParticipant?, updated: @escaping (TelegramChatBannedRights?) -> Void, upgradedToSupergroup: @escaping (PeerId, @escaping () -> Void) -> Void) -> ViewController {
|
||||
let initialState = ChannelBannedMemberControllerState(referenceTimestamp: Int32(Date().timeIntervalSince1970), updatedFlags: nil, updatedTimeout: nil, updating: false)
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ private final class ChannelMembersControllerArguments {
|
|||
let addMember: () -> Void
|
||||
let setPeerIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void
|
||||
let removePeer: (EnginePeer.Id) -> Void
|
||||
let openPeer: (EnginePeer, String?) -> Void
|
||||
let openParticipant: (RenderedChannelParticipant) -> Void
|
||||
let inviteViaLink: () -> Void
|
||||
let updateHideMembers: (Bool) -> Void
|
||||
let displayHideMembersTip: (HideMembersDisabledReason) -> Void
|
||||
|
|
@ -33,7 +33,7 @@ private final class ChannelMembersControllerArguments {
|
|||
addMember: @escaping () -> Void,
|
||||
setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void,
|
||||
removePeer: @escaping (EnginePeer.Id) -> Void,
|
||||
openPeer: @escaping (EnginePeer, String?) -> Void,
|
||||
openParticipant: @escaping (RenderedChannelParticipant) -> Void,
|
||||
inviteViaLink: @escaping () -> Void,
|
||||
updateHideMembers: @escaping (Bool) -> Void,
|
||||
displayHideMembersTip: @escaping (HideMembersDisabledReason) -> Void
|
||||
|
|
@ -42,7 +42,7 @@ private final class ChannelMembersControllerArguments {
|
|||
self.addMember = addMember
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
self.removePeer = removePeer
|
||||
self.openPeer = openPeer
|
||||
self.openParticipant = openParticipant
|
||||
self.inviteViaLink = inviteViaLink
|
||||
self.updateHideMembers = updateHideMembers
|
||||
self.displayHideMembersTip = displayHideMembersTip
|
||||
|
|
@ -319,7 +319,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||
}
|
||||
|
||||
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(participant.peer), presence: participant.presences[participant.peer.id].flatMap(EnginePeer.Presence.init), text: text, label: label, editing: editing, switchValue: nil, enabled: enabled, selectable: participant.peer.id != arguments.context.account.peerId, sectionId: self.section, action: {
|
||||
arguments.openPeer(EnginePeer(participant.peer), participant.participant.rank)
|
||||
arguments.openParticipant(participant)
|
||||
}, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||
}, removePeer: { peerId in
|
||||
|
|
@ -680,8 +680,9 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||
return $0.withUpdatedRemovingPeerId(nil)
|
||||
}
|
||||
}))
|
||||
}, openPeer: { peer, rank in
|
||||
let controller = context.sharedContext.makeChatParticipantRightsScreen(context: context, peerId: peerId, participantId: peer.id, rank: rank)
|
||||
}, openParticipant: { participant in
|
||||
let controller = channelBannedMemberController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, memberId: participant.peer.id, editMember: true, initialParticipant: participant.participant, updated: { rights in
|
||||
}, upgradedToSupergroup: { _, _ in })
|
||||
pushControllerImpl?(controller)
|
||||
}, inviteViaLink: {
|
||||
if let controller = getControllerImpl?() {
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ private struct ChannelPermissionsControllerState: Equatable {
|
|||
var expandedPermissions = Set<TelegramChatBannedRightsFlags>()
|
||||
}
|
||||
|
||||
func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatBannedRightsFlags, isForum: Bool) -> String {
|
||||
func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatBannedRightsFlags, isForum: Bool, defaultPermissions: Bool = false) -> String {
|
||||
if right.contains(.banSendText) {
|
||||
return strings.Channel_BanUser_PermissionSendMessages
|
||||
} else if right.contains(.banSendMedia) {
|
||||
|
|
@ -536,7 +536,11 @@ func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatB
|
|||
return strings.Channel_BanUser_PermissionSendVideoMessage
|
||||
} else if right.contains(.banEditRank) {
|
||||
//TODO:localize
|
||||
return "Edit Own Tags"
|
||||
if defaultPermissions {
|
||||
return "Edit Own Tags"
|
||||
} else {
|
||||
return "Edit Member Tag"
|
||||
}
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
|
@ -715,11 +719,11 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
|
|||
for (subRight, _) in banSendMediaSubList() {
|
||||
let subRightEnabled = true
|
||||
|
||||
subItems.append(SubPermission(title: stringForGroupPermission(strings: presentationData.strings, right: subRight, isForum: channel.isForum), flags: subRight, isSelected: !effectiveRightsFlags.contains(subRight), isEnabled: enabled && subRightEnabled))
|
||||
subItems.append(SubPermission(title: stringForGroupPermission(strings: presentationData.strings, right: subRight, isForum: channel.isForum, defaultPermissions: true), flags: subRight, isSelected: !effectiveRightsFlags.contains(subRight), isEnabled: enabled && subRightEnabled))
|
||||
}
|
||||
}
|
||||
|
||||
entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: channel.isForum), isSelected, rights, enabled, subItems, state.expandedPermissions.contains(rights)))
|
||||
entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: channel.isForum, defaultPermissions: true), isSelected, rights, enabled, subItems, state.expandedPermissions.contains(rights)))
|
||||
rightIndex += 1
|
||||
}
|
||||
|
||||
|
|
@ -799,11 +803,11 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
|
|||
for (subRight, _) in banSendMediaSubList() {
|
||||
let subRightEnabled = true
|
||||
|
||||
subItems.append(SubPermission(title: stringForGroupPermission(strings: presentationData.strings, right: subRight, isForum: false), flags: subRight, isSelected: !effectiveRightsFlags.contains(subRight), isEnabled: subRightEnabled))
|
||||
subItems.append(SubPermission(title: stringForGroupPermission(strings: presentationData.strings, right: subRight, isForum: false, defaultPermissions: true), flags: subRight, isSelected: !effectiveRightsFlags.contains(subRight), isEnabled: subRightEnabled))
|
||||
}
|
||||
}
|
||||
|
||||
entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: false), isSelected, rights, true, subItems, state.expandedPermissions.contains(rights)))
|
||||
entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: false, defaultPermissions: true), isSelected, rights, true, subItems, state.expandedPermissions.contains(rights)))
|
||||
rightIndex += 1
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let mode: CameraScreenImpl.Mode
|
||||
let cameraState: CameraState
|
||||
let cameraAuthorizationStatus: AccessType
|
||||
let microphoneAuthorizationStatus: AccessType
|
||||
|
|
@ -190,6 +191,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
|
||||
init(
|
||||
context: AccountContext,
|
||||
mode: CameraScreenImpl.Mode,
|
||||
cameraState: CameraState,
|
||||
cameraAuthorizationStatus: AccessType,
|
||||
microphoneAuthorizationStatus: AccessType,
|
||||
|
|
@ -208,6 +210,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
openResolvedPeer: @escaping (EnginePeer) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.cameraState = cameraState
|
||||
self.cameraAuthorizationStatus = cameraAuthorizationStatus
|
||||
self.microphoneAuthorizationStatus = microphoneAuthorizationStatus
|
||||
|
|
@ -230,6 +233,9 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.mode != rhs.mode {
|
||||
return false
|
||||
}
|
||||
if lhs.cameraState != rhs.cameraState {
|
||||
return false
|
||||
}
|
||||
|
|
@ -318,7 +324,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
|
||||
fileprivate var sendAsPeerId: EnginePeer.Id?
|
||||
fileprivate var isCustomTarget = false
|
||||
fileprivate var canLivestream = true
|
||||
fileprivate var canLivestream = false
|
||||
|
||||
private var privacy: EngineStoryPrivacy = EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: [])
|
||||
private var allowComments = true
|
||||
|
|
@ -345,6 +351,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
|
||||
init(
|
||||
context: AccountContext,
|
||||
mode: CameraScreenImpl.Mode,
|
||||
present: @escaping (ViewController) -> Void,
|
||||
completion: ActionSlot<Signal<CameraScreenImpl.Result, NoError>>,
|
||||
animateShutter: @escaping () -> Void = {},
|
||||
|
|
@ -380,6 +387,10 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
self.setupRecentAssetSubscription()
|
||||
}
|
||||
|
||||
if case .story = mode {
|
||||
self.canLivestream = true
|
||||
}
|
||||
|
||||
if let controller = getController() {
|
||||
if controller.resumeLiveStream {
|
||||
controller.updateCameraState({ $0.updatedMode(.live).updatedIsWaitingForStream(true) }, update: false, transition: .immediate)
|
||||
|
|
@ -1299,6 +1310,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
func makeState() -> State {
|
||||
return State(
|
||||
context: self.context,
|
||||
mode: self.mode,
|
||||
present: self.present,
|
||||
completion: self.completion,
|
||||
animateShutter: self.animateShutter,
|
||||
|
|
@ -3707,6 +3719,7 @@ public class CameraScreenImpl: ViewController, CameraScreen {
|
|||
component: AnyComponent(
|
||||
CameraScreenComponent(
|
||||
context: self.context,
|
||||
mode: controller.mode,
|
||||
cameraState: self.cameraState,
|
||||
cameraAuthorizationStatus: self.cameraAuthorizationStatus,
|
||||
microphoneAuthorizationStatus: self.microphoneAuthorizationStatus,
|
||||
|
|
|
|||
|
|
@ -213,18 +213,6 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||
|
||||
switch action {
|
||||
case .join, .joinGroup, .applyToJoin:
|
||||
Queue.mainQueue().after(1.0) {
|
||||
//TODO:localize
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: "You joined the group", cancel: "Add Tag", destructive: false), elevatedLayout: true, action: { action in
|
||||
if case .undo = action {
|
||||
let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peer.id, participantId: context.account.peerId, rank: nil)
|
||||
self.interfaceInteraction?.getNavigationController()?.pushViewController(tagController)
|
||||
}
|
||||
return true
|
||||
})
|
||||
self.interfaceInteraction?.presentController(controller, nil)
|
||||
}
|
||||
self.isJoining = true
|
||||
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, isSecondary, metrics) = self.layoutData, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, maxOverlayHeight: maxOverlayHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics, force: true)
|
||||
|
|
@ -263,6 +251,32 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||
}
|
||||
}
|
||||
strongSelf.interfaceInteraction?.presentController(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationInterfaceState.strings.Common_OK, action: {})]), nil)
|
||||
}, completed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
Queue.mainQueue().after(0.5) {
|
||||
if let presentationInterfaceState = self.presentationInterfaceState, let peer = presentationInterfaceState.renderedPeer?.peer {
|
||||
var canEditRank = false
|
||||
if let channel = peer as? TelegramChannel, channel.hasPermission(.editRank) {
|
||||
canEditRank = true
|
||||
} else if let group = peer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) {
|
||||
canEditRank = true
|
||||
}
|
||||
//TODO:localize
|
||||
if canEditRank {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: "You joined the group", cancel: "Add Tag", destructive: false), elevatedLayout: true, action: { action in
|
||||
if case .undo = action {
|
||||
let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peer.id, participantId: context.account.peerId, rank: nil)
|
||||
self.interfaceInteraction?.getNavigationController()?.pushViewController(tagController)
|
||||
}
|
||||
return true
|
||||
})
|
||||
self.interfaceInteraction?.presentController(controller, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
case .kicked:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -777,6 +777,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||
(.banEmbedLinks, self.presentationData.strings.Channel_AdminLog_BanEmbedLinks),
|
||||
(.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls),
|
||||
(.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers),
|
||||
(.banEditRank, self.presentationData.strings.Channel_AdminLog_EditRankOwn),
|
||||
(.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages),
|
||||
(.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics),
|
||||
(.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo)
|
||||
|
|
@ -1139,6 +1140,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||
(.banEmbedLinks, self.presentationData.strings.Channel_AdminLog_BanEmbedLinks),
|
||||
(.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls),
|
||||
(.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers),
|
||||
(.banEditRank, self.presentationData.strings.Channel_AdminLog_EditRank),
|
||||
(.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages),
|
||||
(.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics),
|
||||
(.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import UndoUI
|
|||
import RankChatPreviewItem
|
||||
|
||||
private let rankFieldTag = GenericComponentViewTag()
|
||||
private let rankMaxLength: Int32 = 16
|
||||
|
||||
private final class ChatParticipantRightsContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -115,16 +116,6 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
self.disposable?.dispose()
|
||||
}
|
||||
|
||||
func promoteMember() {
|
||||
let controller = channelAdminController(context: self.context, updatedPresentationData: nil, peerId: self.subject.peerId, adminId: self.memberId, initialParticipant: nil, updated: { _ in
|
||||
}, upgradedToSupergroup: { _, _ in }, transferedOwnership: { _ in })
|
||||
self.controller?.push(controller)
|
||||
}
|
||||
|
||||
func removeMember() {
|
||||
|
||||
}
|
||||
|
||||
func complete() {
|
||||
var rank = self.rank
|
||||
if rank?.isEmpty == true {
|
||||
|
|
@ -160,8 +151,6 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
let title = Child(MultilineTextComponent.self)
|
||||
let peerSection = Child(ListSectionComponent.self)
|
||||
let rankSection = Child(ListSectionComponent.self)
|
||||
//let permissionsSection = Child(ListSectionComponent.self)
|
||||
let actionsSection = Child(ListSectionComponent.self)
|
||||
let doneButton = Child(ButtonComponent.self)
|
||||
|
||||
return { context in
|
||||
|
|
@ -262,207 +251,113 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
break
|
||||
}
|
||||
|
||||
if let peer = state.peer {
|
||||
var rankPreviewPlaceholder = ""
|
||||
var rankFooterString = ""
|
||||
var rankRole: ChatRankInfoScreenRole = .member
|
||||
switch component.subject {
|
||||
case .admin:
|
||||
rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name."
|
||||
rankPreviewPlaceholder = "admin"
|
||||
rankRole = .admin
|
||||
case .member:
|
||||
rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name."
|
||||
rankPreviewPlaceholder = "0️⃣"
|
||||
case .rank:
|
||||
if peer.id == component.context.account.peerId {
|
||||
rankFooterString = "Share your role, title, or how you're known in this group. Your tag is visible to all members."
|
||||
} else {
|
||||
rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name."
|
||||
}
|
||||
rankPreviewPlaceholder = "0️⃣"
|
||||
}
|
||||
|
||||
let rankValue = state.rank ?? ""
|
||||
let messageItem = RankChatPreviewItem.MessageItem(
|
||||
peer: peer,
|
||||
text: "Reinhardt, we need to find you some new tunes.",
|
||||
entities: nil,
|
||||
media: [],
|
||||
rank: rankValue.isEmpty ? rankPreviewPlaceholder : rankValue,
|
||||
rankRole: rankRole
|
||||
)
|
||||
|
||||
var rankSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
rankSectionItems.append(
|
||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
|
||||
itemGenerator: RankChatPreviewItem(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
componentTheme: theme,
|
||||
strings: strings,
|
||||
sectionId: 0,
|
||||
fontSize: presentationData.chatFontSize,
|
||||
chatBubbleCorners: presentationData.chatBubbleCorners,
|
||||
wallpaper: presentationData.chatWallpaper,
|
||||
dateTimeFormat: environment.dateTimeFormat,
|
||||
nameDisplayOrder: presentationData.nameDisplayOrder,
|
||||
messageItems: [messageItem]
|
||||
),
|
||||
params: listItemParams
|
||||
)))
|
||||
)
|
||||
rankSectionItems.append(
|
||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(ListTextFieldItemComponent(
|
||||
style: .glass,
|
||||
theme: theme,
|
||||
initialText: state.initialRank ?? "",
|
||||
resetText: nil,
|
||||
placeholder: "Add tag",
|
||||
characterLimit: 16,
|
||||
autocapitalizationType: .sentences,
|
||||
autocorrectionType: .default,
|
||||
returnKeyType: .done,
|
||||
contentInsets: .zero,
|
||||
updated: { [weak state] value in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.rank = value
|
||||
state.updated(transition: .easeInOut(duration: 0.2))
|
||||
},
|
||||
onReturn: {},
|
||||
tag: rankFieldTag
|
||||
)))
|
||||
)
|
||||
|
||||
let rankSection = rankSection.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
header: nil,
|
||||
footer: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: rankFooterString,
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||
textColor: theme.list.freeTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
items: rankSectionItems
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(rankSection
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + rankSection.size.height / 2.0))
|
||||
)
|
||||
contentHeight += rankSection.size.height
|
||||
let peer: EnginePeer
|
||||
if let current = state.peer {
|
||||
peer = current
|
||||
} else {
|
||||
peer = EnginePeer.user(TelegramUser(id: EnginePeer.Id(0), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil, verificationIconFileId: nil))
|
||||
}
|
||||
|
||||
var rankPreviewPlaceholder = ""
|
||||
var rankFooterString = ""
|
||||
var rankRole: ChatRankInfoScreenRole = .member
|
||||
switch component.subject {
|
||||
case .member:
|
||||
contentHeight += 24.0
|
||||
|
||||
// var permissionsSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
// permissionsSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
|
||||
// theme: theme,
|
||||
// style: .glass,
|
||||
// title: AnyComponent(VStack([
|
||||
// AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
// text: .plain(NSAttributedString(
|
||||
// string: "Permissions",
|
||||
// font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize),
|
||||
// textColor: theme.list.itemPrimaryTextColor
|
||||
// )),
|
||||
// maximumNumberOfLines: 1
|
||||
// ))),
|
||||
// ], alignment: .left, spacing: 2.0)),
|
||||
// accessory: .arrow,
|
||||
// action: { _ in
|
||||
//
|
||||
// }
|
||||
// ))))
|
||||
// let permissionsSection = permissionsSection.update(
|
||||
// component: ListSectionComponent(
|
||||
// theme: theme,
|
||||
// style: .glass,
|
||||
// header: AnyComponent(MultilineTextComponent(
|
||||
// text: .plain(NSAttributedString(
|
||||
// string: "What can this member do".uppercased(),
|
||||
// font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||
// textColor: environment.theme.list.freeTextColor
|
||||
// )),
|
||||
// maximumNumberOfLines: 0
|
||||
// )),
|
||||
// footer: nil,
|
||||
// items: permissionsSectionItems
|
||||
// ),
|
||||
// availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||
// transition: context.transition
|
||||
// )
|
||||
// context.add(permissionsSection
|
||||
// .position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + permissionsSection.size.height / 2.0))
|
||||
// )
|
||||
// contentHeight += permissionsSection.size.height
|
||||
// contentHeight += 24.0
|
||||
|
||||
var actionsSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
actionsSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
title: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Promote Member",
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize),
|
||||
textColor: theme.list.itemAccentColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
)),
|
||||
titleAlignment: .center,
|
||||
accessory: .none,
|
||||
action: { [weak state] _ in
|
||||
state?.promoteMember()
|
||||
}
|
||||
))))
|
||||
actionsSectionItems.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ListActionItemComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
title: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Remove Member",
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize),
|
||||
textColor: theme.list.itemDestructiveColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
)),
|
||||
titleAlignment: .center,
|
||||
accessory: .none,
|
||||
action: { [weak state] _ in
|
||||
state?.removeMember()
|
||||
}
|
||||
))))
|
||||
let actionsSection = actionsSection.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
header: nil,
|
||||
footer: nil,
|
||||
items: actionsSectionItems
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(actionsSection
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + actionsSection.size.height / 2.0))
|
||||
)
|
||||
contentHeight += actionsSection.size.height
|
||||
case .admin:
|
||||
break
|
||||
default:
|
||||
break
|
||||
rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name."
|
||||
rankPreviewPlaceholder = "admin"
|
||||
rankRole = .admin
|
||||
case .member:
|
||||
rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name."
|
||||
rankPreviewPlaceholder = "0️⃣"
|
||||
case .rank:
|
||||
if peer.id == component.context.account.peerId {
|
||||
rankFooterString = "Share your role, title, or how you're known in this group. Your tag is visible to all members."
|
||||
} else {
|
||||
rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name."
|
||||
}
|
||||
rankPreviewPlaceholder = "0️⃣"
|
||||
}
|
||||
|
||||
let rankValue = state.rank ?? ""
|
||||
let messageItem = RankChatPreviewItem.MessageItem(
|
||||
peer: peer,
|
||||
text: "Reinhardt, we need to find you some new tunes.",
|
||||
entities: nil,
|
||||
media: [],
|
||||
rank: rankValue.isEmpty ? rankPreviewPlaceholder : rankValue,
|
||||
rankRole: rankRole
|
||||
)
|
||||
|
||||
var rankSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
rankSectionItems.append(
|
||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
|
||||
itemGenerator: RankChatPreviewItem(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
componentTheme: theme,
|
||||
strings: strings,
|
||||
sectionId: 0,
|
||||
fontSize: presentationData.chatFontSize,
|
||||
chatBubbleCorners: presentationData.chatBubbleCorners,
|
||||
wallpaper: presentationData.chatWallpaper,
|
||||
dateTimeFormat: environment.dateTimeFormat,
|
||||
nameDisplayOrder: presentationData.nameDisplayOrder,
|
||||
messageItems: [messageItem]
|
||||
),
|
||||
params: listItemParams
|
||||
)))
|
||||
)
|
||||
rankSectionItems.append(
|
||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(ListTextFieldItemComponent(
|
||||
style: .glass,
|
||||
theme: theme,
|
||||
initialText: state.initialRank ?? "",
|
||||
resetText: nil,
|
||||
placeholder: "Add tag",
|
||||
characterLimit: Int(rankMaxLength),
|
||||
autocapitalizationType: .sentences,
|
||||
autocorrectionType: .default,
|
||||
returnKeyType: .done,
|
||||
contentInsets: .zero,
|
||||
updated: { [weak state] value in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.rank = value
|
||||
state.updated(transition: .easeInOut(duration: 0.2))
|
||||
},
|
||||
onReturn: { [weak state] in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.complete()
|
||||
},
|
||||
tag: rankFieldTag
|
||||
)))
|
||||
)
|
||||
|
||||
let rankSection = rankSection.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
header: nil,
|
||||
footer: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: rankFooterString,
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||
textColor: theme.list.freeTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
items: rankSectionItems
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(rankSection
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + rankSection.size.height / 2.0))
|
||||
)
|
||||
contentHeight += rankSection.size.height
|
||||
|
||||
contentHeight += 24.0
|
||||
|
||||
|
|
@ -500,7 +395,6 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
return
|
||||
}
|
||||
state.complete()
|
||||
component.cancel(true)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0),
|
||||
|
|
@ -510,7 +404,13 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + doneButton.size.height / 2.0))
|
||||
)
|
||||
contentHeight += doneButton.size.height
|
||||
contentHeight += buttonInsets.bottom
|
||||
|
||||
if environment.inputHeight > 0.0 {
|
||||
contentHeight += 15.0
|
||||
contentHeight += max(environment.inputHeight, environment.safeInsets.bottom)
|
||||
} else {
|
||||
contentHeight += buttonInsets.bottom
|
||||
}
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: contentHeight)
|
||||
}
|
||||
|
|
@ -565,7 +465,7 @@ private final class ChatParticipantRightsComponent: CombinedComponent {
|
|||
)),
|
||||
style: .glass,
|
||||
backgroundColor: .color(environment.theme.list.modalBlocksBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
followContentSizeChanges: false,
|
||||
clipsContent: true,
|
||||
isScrollEnabled: false,
|
||||
animateOut: animateOut
|
||||
|
|
@ -663,7 +563,7 @@ public class ChatParticipantRightsScreen: ViewControllerComponentContainer {
|
|||
super.viewDidAppear(animated)
|
||||
|
||||
if let view = self.node.hostView.findTaggedView(tag: rankFieldTag) as? ListTextFieldItemComponent.View {
|
||||
Queue.mainQueue().after(0.15) {
|
||||
Queue.mainQueue().after(0.01) {
|
||||
view.activateInput()
|
||||
view.selectAll()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ final class MinimizedHeaderNode: ASDisplayNode {
|
|||
self.iconView.contentMode = .scaleAspectFit
|
||||
self.iconView.clipsToBounds = true
|
||||
self.iconView.layer.cornerRadius = 2.5
|
||||
self.iconView.tintColor = self.theme.navigationBar.primaryTextColor
|
||||
self.iconView.tintColor = theme.navigationBar.primaryTextColor
|
||||
|
||||
super.init()
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ final class MinimizedHeaderNode: ASDisplayNode {
|
|||
self.validLayout = (size, insets, isExpanded)
|
||||
|
||||
if self.closeIcon == nil {
|
||||
self.closeIcon = generateCloseButtonImage()
|
||||
self.closeIcon = generateCloseButtonImage()?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
|
||||
let headerHeight: CGFloat = 56.0
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ enum PeerMembersListAction {
|
|||
case restrict
|
||||
case remove
|
||||
case openStories(sourceView: UIView)
|
||||
case openContextMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?)
|
||||
}
|
||||
|
||||
private enum PeerMembersListEntryStableId: Hashable {
|
||||
|
|
@ -94,6 +95,8 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||
addMemberAction()
|
||||
})
|
||||
case let .member(_, _, member):
|
||||
var labelColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
var labelBackground = false
|
||||
let label: String?
|
||||
if let rank = member.rank {
|
||||
label = rank
|
||||
|
|
@ -107,6 +110,17 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||
label = nil
|
||||
}
|
||||
}
|
||||
|
||||
switch member.role {
|
||||
case .creator:
|
||||
labelBackground = true
|
||||
labelColor = UIColor(rgb: 0x956ac8)
|
||||
case .admin:
|
||||
labelBackground = true
|
||||
labelColor = UIColor(rgb: 0x49a355)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: enclosingPeer, member: member)
|
||||
|
||||
|
|
@ -150,6 +164,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||
return ContactsPeerItem(
|
||||
presentationData: ItemListPresentationData(presentationData),
|
||||
style: .plain,
|
||||
systemStyle: .glass,
|
||||
sectionId: 0,
|
||||
sortOrder: presentationData.nameSortOrder,
|
||||
displayOrder: presentationData.nameDisplayOrder,
|
||||
|
|
@ -157,7 +172,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||
peerMode: .memberList,
|
||||
peer: .peer(peer: EnginePeer(member.peer), chatPeer: EnginePeer(member.peer)),
|
||||
status: status,
|
||||
rightLabelText: label,
|
||||
rightLabelText: label.flatMap { .init(text: $0, color: labelColor, hasBackground: labelBackground) },
|
||||
enabled: true,
|
||||
selection: .none,
|
||||
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
|
||||
|
|
@ -400,51 +415,8 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||
gesture?.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
let actions = availableActionsForMemberOfPeer(accountPeerId: self.context.account.peerId, peer: enclosingPeer, member: member)
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var items: [ContextMenuItem] = []
|
||||
let action = self.action
|
||||
|
||||
if actions.contains(.promote) && enclosingPeer is TelegramChannel {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.GroupInfo_ActionPromote, icon: { _ in
|
||||
return nil
|
||||
}, action: { c, _ in
|
||||
c?.dismiss(completion: {
|
||||
action(member, .promote)
|
||||
})
|
||||
})))
|
||||
}
|
||||
if actions.contains(.restrict) {
|
||||
if enclosingPeer is TelegramChannel {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.GroupInfo_ActionRestrict, icon: { _ in
|
||||
return nil
|
||||
}, action: { c, _ in
|
||||
c?.dismiss(completion: {
|
||||
action(member, .restrict)
|
||||
})
|
||||
})))
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Delete, textColor: .destructive, icon: { _ in
|
||||
return nil
|
||||
}, action: { c, _ in
|
||||
c?.dismiss(completion: {
|
||||
action(member, .remove)
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if items.isEmpty {
|
||||
gesture?.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
let dismissPromise = ValuePromise<Bool>(false)
|
||||
let source = PeerInfoMemberExtractedContentSource(sourceNode: node, keepInPlace: false, blurBackground: true, centerVertically: false, shouldBeDismissed: dismissPromise.get())
|
||||
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self.parentController?.presentInGlobalOverlay(contextController)
|
||||
|
||||
self.action(member, .openContextMenu(sourceNode: node, gesture: gesture))
|
||||
})
|
||||
self.enclosingPeer = enclosingPeer
|
||||
self.currentEntries = entries
|
||||
|
|
|
|||
|
|
@ -2237,6 +2237,7 @@ struct PeerInfoMemberActions: OptionSet {
|
|||
static let restrict = PeerInfoMemberActions(rawValue: 1 << 0)
|
||||
static let promote = PeerInfoMemberActions(rawValue: 1 << 1)
|
||||
static let logout = PeerInfoMemberActions(rawValue: 1 << 2)
|
||||
static let editRank = PeerInfoMemberActions(rawValue: 1 << 3)
|
||||
}
|
||||
|
||||
func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member: PeerInfoMember) -> PeerInfoMemberActions {
|
||||
|
|
@ -2244,13 +2245,24 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member:
|
|||
|
||||
if peer == nil {
|
||||
result.insert(.logout)
|
||||
} else if member.id != accountPeerId {
|
||||
} else if member.id == accountPeerId {
|
||||
if let channel = peer as? TelegramChannel {
|
||||
if channel.hasPermission(.editRank) {
|
||||
result.insert(.editRank)
|
||||
}
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
if !group.hasBannedPermission(.banEditRank) {
|
||||
result.insert(.editRank)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let channel = peer as? TelegramChannel {
|
||||
if channel.flags.contains(.isCreator) {
|
||||
if !channel.flags.contains(.isGigagroup) {
|
||||
result.insert(.restrict)
|
||||
}
|
||||
result.insert(.promote)
|
||||
result.insert(.editRank)
|
||||
} else {
|
||||
switch member {
|
||||
case let .channelMember(channelMember, _):
|
||||
|
|
@ -2266,6 +2278,9 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member:
|
|||
if channel.hasPermission(.addAdmins) {
|
||||
result.insert(.promote)
|
||||
}
|
||||
if channel.hasPermission(.editRank) {
|
||||
result.insert(.editRank)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if channel.hasPermission(.banMembers) && !channel.flags.contains(.isGigagroup) {
|
||||
|
|
@ -2274,6 +2289,9 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member:
|
|||
if channel.hasPermission(.addAdmins) {
|
||||
result.insert(.promote)
|
||||
}
|
||||
if channel.hasPermission(.editRank) {
|
||||
result.insert(.editRank)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .legacyGroupMember:
|
||||
|
|
@ -2287,12 +2305,14 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member:
|
|||
case .creator:
|
||||
result.insert(.restrict)
|
||||
result.insert(.promote)
|
||||
result.insert(.editRank)
|
||||
case .admin:
|
||||
switch member {
|
||||
case let .legacyGroupMember(_, _, invitedBy, _, _, _):
|
||||
result.insert(.restrict)
|
||||
if invitedBy == accountPeerId {
|
||||
result.insert(.promote)
|
||||
result.insert(.editRank)
|
||||
}
|
||||
case .channelMember:
|
||||
break
|
||||
|
|
@ -2304,6 +2324,7 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member:
|
|||
case let .legacyGroupMember(_, _, invitedBy, _, _, _):
|
||||
if invitedBy == accountPeerId {
|
||||
result.insert(.restrict)
|
||||
result.insert(.editRank)
|
||||
}
|
||||
case .channelMember:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -836,7 +836,10 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
|
|||
for member in memberList {
|
||||
let isAccountPeer = member.id == context.account.peerId
|
||||
items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: .account(context), enclosingPeer: peer, member: member, isAccount: false, action: isAccountPeer ? { _ in
|
||||
interaction.performMemberAction(member, .editRank)
|
||||
let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: peer, member: member)
|
||||
if actions.contains(.editRank) {
|
||||
interaction.performMemberAction(member, .editRank)
|
||||
}
|
||||
} : { action in
|
||||
switch action {
|
||||
case .open:
|
||||
|
|
|
|||
|
|
@ -1448,6 +1448,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
strongSelf.performMemberAction(member: member, action: .remove)
|
||||
case let .openStories(sourceView):
|
||||
strongSelf.performMemberAction(member: member, action: .openStories(sourceView: sourceView))
|
||||
case let .openContextMenu(sourceNode, gesture):
|
||||
strongSelf.openMemberContextMenu(member: member, node: sourceNode, gesture: gesture)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4250,17 +4252,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
}
|
||||
|
||||
private func openPeerInfo(peer: Peer, isMember: Bool) {
|
||||
if peer.id == self.context.account.peerId {
|
||||
let controller = self.context.sharedContext.makeChatParticipantRightsScreen(context: self.context, peerId: self.peerId, participantId: peer.id, rank: "")
|
||||
(self.controller?.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
} else {
|
||||
var mode: PeerInfoControllerMode = .generic
|
||||
if isMember {
|
||||
mode = .group(self.peerId)
|
||||
}
|
||||
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer, mode: mode, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
(self.controller?.navigationController as? NavigationController)?.pushViewController(infoController)
|
||||
}
|
||||
var mode: PeerInfoControllerMode = .generic
|
||||
if isMember {
|
||||
mode = .group(self.peerId)
|
||||
}
|
||||
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer, mode: mode, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
(self.controller?.navigationController as? NavigationController)?.pushViewController(infoController)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,59 +12,81 @@ import ContextUI
|
|||
|
||||
extension PeerInfoScreenNode {
|
||||
func openMemberContextMenu(member: PeerInfoMember, node: ASDisplayNode, gesture: ContextGesture?) {
|
||||
guard let controller = self.controller else {
|
||||
guard let controller = self.controller, let enclosingPeer = self.data?.peer else {
|
||||
return
|
||||
}
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Send Message", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self, let navigationController = self.controller?.navigationController as? NavigationController else {
|
||||
return
|
||||
let actions = availableActionsForMemberOfPeer(accountPeerId: self.context.account.peerId, peer: enclosingPeer, member: member)
|
||||
|
||||
//TODO:localize
|
||||
if member.id != self.context.account.peerId {
|
||||
items.append(.action(ContextMenuActionItem(text: "Send Message", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self, let navigationController = self.controller?.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(member.peer))))
|
||||
}
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(member.peer))))
|
||||
}
|
||||
})))
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Member Tag", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tag"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
if actions.contains(.editRank) {
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Member Tag", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tag"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.performMemberAction(member: member, action: .editRank)
|
||||
}
|
||||
self.performMemberAction(member: member, action: .editRank)
|
||||
}
|
||||
})))
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Promote", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Promote"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
if actions.contains(.promote) && enclosingPeer is TelegramChannel {
|
||||
items.append(.action(ContextMenuActionItem(text: "Promote", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Promote"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.performMemberAction(member: member, action: .promote)
|
||||
}
|
||||
self.performMemberAction(member: member, action: .promote)
|
||||
}
|
||||
})))
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Restrict", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
if actions.contains(.restrict) {
|
||||
if enclosingPeer is TelegramChannel {
|
||||
items.append(.action(ContextMenuActionItem(text: "Restrict", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.performMemberAction(member: member, action: .restrict)
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.performMemberAction(member: member, action: .remove)
|
||||
}
|
||||
self.performMemberAction(member: member, action: .restrict)
|
||||
}
|
||||
})))
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.performMemberAction(member: member, action: .remove)
|
||||
}
|
||||
})))
|
||||
if items.isEmpty {
|
||||
gesture?.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
let actions = ContextController.Items(content: .list(items))
|
||||
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceView: node.view)), items: .single(actions), gesture: gesture)
|
||||
let source: ContextContentSource
|
||||
if let node = node as? ContextExtractedContentContainingNode {
|
||||
source = .extracted(PeerInfoMemberExtractedContentSource(sourceNode: node, keepInPlace: false, blurBackground: true, centerVertically: false, shouldBeDismissed: .single(false)))
|
||||
} else {
|
||||
source = .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceView: node.view))
|
||||
}
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2551,11 +2551,28 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
return buffer.baseAddress!.assumingMemoryBound(to: UInt8.self).pointee
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
switch buttonType {
|
||||
case 0:
|
||||
let _ = strongSelf.context.engine.peers.toggleMessageCopyProtection(peerId: message.id.peerId, enabled: true, requestMessageId: message.id).startStandalone()
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: "Disable Sharing", text: "Are you sure you want to keep sharing disabled?", actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Yes", action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.peers.toggleMessageCopyProtection(peerId: message.id.peerId, enabled: true, requestMessageId: message.id).startStandalone()
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
case 1:
|
||||
let _ = strongSelf.context.engine.peers.toggleMessageCopyProtection(peerId: message.id.peerId, enabled: false, requestMessageId: message.id).startStandalone()
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: "Enable Sharing", text: "Are you sure you want to enable sharing?", actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Yes", action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.peers.toggleMessageCopyProtection(peerId: message.id.peerId, enabled: false, requestMessageId: message.id).startStandalone()
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4317,10 +4317,6 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return PasskeysScreen(context: context, displaySkip: displaySkip, initialPasskeysData: nil, passkeysDataUpdated: { _ in }, completion: completion, cancel: dismiss)
|
||||
}
|
||||
|
||||
public func makeChatParticipantRightsScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) -> ViewController {
|
||||
return ChatParticipantRightsScreen(context: context, subject: .member(peerId: peerId, participantId: participantId, rank: rank))
|
||||
}
|
||||
|
||||
public func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) -> ViewController {
|
||||
return ChatParticipantRightsScreen(context: context, subject: .rank(peerId: peerId, participantId: participantId, rank: rank))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue