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
4203d70414
commit
5d50aa34af
19 changed files with 2010 additions and 1905 deletions
|
|
@ -15828,3 +15828,7 @@ Error: %8$@";
|
|||
|
||||
"Channel.AdminLog.EditRankOwn" = "Edit Member Tag";
|
||||
"Channel.AdminLog.EditRank" = "Edit Member Tags";
|
||||
|
||||
"Channel.AdminLog.MessageRankNameNew" = "changed member tag for %1$@:\n%2$@";
|
||||
"Channel.AdminLog.MessageRankUsernameNew" = "changed member tag for %1$@ (%2$@):\n%3$@";
|
||||
"Channel.AdminLog.MessageRankNew" = "changed member tag:\n%1$@";
|
||||
|
|
|
|||
|
|
@ -1528,7 +1528,7 @@ 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 makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) -> ViewController
|
||||
func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?, role: ChatRankInfoScreenRole) -> ViewController
|
||||
|
||||
func makePeerCopyProtectionInfoScreen(context: AccountContext, completion: @escaping () -> Void) -> ViewController
|
||||
|
||||
|
|
|
|||
|
|
@ -5263,7 +5263,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
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)
|
||||
let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peerId, participantId: context.account.peerId, rank: nil, role: .member)
|
||||
self.push(tagController)
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1515256996] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteDelete($0) }
|
||||
dict[-384910503] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteEdit($0) }
|
||||
dict[1091179342] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteRevoke($0) }
|
||||
dict[1476834540] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantEditRank($0) }
|
||||
dict[-484690728] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantInvite($0) }
|
||||
dict[405815507] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantJoin($0) }
|
||||
dict[-23084712] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantJoinByInvite($0) }
|
||||
|
|
|
|||
|
|
@ -787,6 +787,19 @@ public extension Api {
|
|||
return ("channelAdminLogEventActionExportedInviteRevoke", [("invite", self.invite as Any)])
|
||||
}
|
||||
}
|
||||
public class Cons_channelAdminLogEventActionParticipantEditRank: TypeConstructorDescription {
|
||||
public var userId: Int64
|
||||
public var prevRank: String
|
||||
public var newRank: String
|
||||
public init(userId: Int64, prevRank: String, newRank: String) {
|
||||
self.userId = userId
|
||||
self.prevRank = prevRank
|
||||
self.newRank = newRank
|
||||
}
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
return ("channelAdminLogEventActionParticipantEditRank", [("userId", self.userId as Any), ("prevRank", self.prevRank as Any), ("newRank", self.newRank as Any)])
|
||||
}
|
||||
}
|
||||
public class Cons_channelAdminLogEventActionParticipantInvite: TypeConstructorDescription {
|
||||
public var participant: Api.ChannelParticipant
|
||||
public init(participant: Api.ChannelParticipant) {
|
||||
|
|
@ -1044,6 +1057,7 @@ public extension Api {
|
|||
case channelAdminLogEventActionExportedInviteDelete(Cons_channelAdminLogEventActionExportedInviteDelete)
|
||||
case channelAdminLogEventActionExportedInviteEdit(Cons_channelAdminLogEventActionExportedInviteEdit)
|
||||
case channelAdminLogEventActionExportedInviteRevoke(Cons_channelAdminLogEventActionExportedInviteRevoke)
|
||||
case channelAdminLogEventActionParticipantEditRank(Cons_channelAdminLogEventActionParticipantEditRank)
|
||||
case channelAdminLogEventActionParticipantInvite(Cons_channelAdminLogEventActionParticipantInvite)
|
||||
case channelAdminLogEventActionParticipantJoin
|
||||
case channelAdminLogEventActionParticipantJoinByInvite(Cons_channelAdminLogEventActionParticipantJoinByInvite)
|
||||
|
|
@ -1250,6 +1264,14 @@ public extension Api {
|
|||
}
|
||||
_data.invite.serialize(buffer, true)
|
||||
break
|
||||
case .channelAdminLogEventActionParticipantEditRank(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1476834540)
|
||||
}
|
||||
serializeInt64(_data.userId, buffer: buffer, boxed: false)
|
||||
serializeString(_data.prevRank, buffer: buffer, boxed: false)
|
||||
serializeString(_data.newRank, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .channelAdminLogEventActionParticipantInvite(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-484690728)
|
||||
|
|
@ -1471,6 +1493,8 @@ public extension Api {
|
|||
return ("channelAdminLogEventActionExportedInviteEdit", [("prevInvite", _data.prevInvite as Any), ("newInvite", _data.newInvite as Any)])
|
||||
case .channelAdminLogEventActionExportedInviteRevoke(let _data):
|
||||
return ("channelAdminLogEventActionExportedInviteRevoke", [("invite", _data.invite as Any)])
|
||||
case .channelAdminLogEventActionParticipantEditRank(let _data):
|
||||
return ("channelAdminLogEventActionParticipantEditRank", [("userId", _data.userId as Any), ("prevRank", _data.prevRank as Any), ("newRank", _data.newRank as Any)])
|
||||
case .channelAdminLogEventActionParticipantInvite(let _data):
|
||||
return ("channelAdminLogEventActionParticipantInvite", [("participant", _data.participant as Any)])
|
||||
case .channelAdminLogEventActionParticipantJoin:
|
||||
|
|
@ -1926,6 +1950,23 @@ public extension Api {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelAdminLogEventActionParticipantEditRank(_ reader: BufferReader) -> ChannelAdminLogEventAction? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantEditRank(Cons_channelAdminLogEventActionParticipantEditRank(userId: _1!, prevRank: _2!, newRank: _3!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelAdminLogEventActionParticipantInvite(_ reader: BufferReader) -> ChannelAdminLogEventAction? {
|
||||
var _1: Api.ChannelParticipant?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
|
|||
|
|
@ -3157,14 +3157,17 @@ public extension Api.functions.channels {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.channels {
|
||||
static func editAdmin(channel: Api.InputChannel, userId: Api.InputUser, adminRights: Api.ChatAdminRights, rank: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func editAdmin(flags: Int32, channel: Api.InputChannel, userId: Api.InputUser, adminRights: Api.ChatAdminRights, rank: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-751007486)
|
||||
buffer.appendInt32(-1701270168)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
channel.serialize(buffer, true)
|
||||
userId.serialize(buffer, true)
|
||||
adminRights.serialize(buffer, true)
|
||||
serializeString(rank, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "channels.editAdmin", parameters: [("channel", String(describing: channel)), ("userId", String(describing: userId)), ("adminRights", String(describing: adminRights)), ("rank", String(describing: rank))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
if Int(flags) & Int(1 << 0) != 0 {
|
||||
serializeString(rank!, buffer: buffer, boxed: false)
|
||||
}
|
||||
return (FunctionDescription(name: "channels.editAdmin", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("userId", String(describing: userId)), ("adminRights", String(describing: adminRights)), ("rank", String(describing: rank))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ public final class ChannelAdminEventLogContext {
|
|||
clipIndex = i - 1
|
||||
}
|
||||
}
|
||||
clipIndex = max(0, clipIndex)
|
||||
if clipIndex < events.count {
|
||||
events.removeSubrange(clipIndex ..< events.count)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ public enum AdminLogEventAction {
|
|||
case changeEmojiPack(prev: StickerPackReference?, new: StickerPackReference?)
|
||||
case participantSubscriptionExtended(prev: RenderedChannelParticipant, new: RenderedChannelParticipant)
|
||||
case toggleAutoTranslation(Bool)
|
||||
case editRank(peerId: EnginePeer.Id, prev: String, new: String)
|
||||
}
|
||||
|
||||
public enum ChannelAdminLogEventError {
|
||||
|
|
@ -129,12 +130,13 @@ public struct AdminLogEventsFlags: OptionSet {
|
|||
public static let invites = AdminLogEventsFlags(rawValue: 1 << 15)
|
||||
public static let sendMessages = AdminLogEventsFlags(rawValue: 1 << 16)
|
||||
public static let forums = AdminLogEventsFlags(rawValue: 1 << 17)
|
||||
public static let ranks = AdminLogEventsFlags(rawValue: 1 << 19)
|
||||
|
||||
public static var all: AdminLogEventsFlags {
|
||||
return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums]
|
||||
return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums, .ranks]
|
||||
}
|
||||
public static var flags: AdminLogEventsFlags {
|
||||
return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums]
|
||||
return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums, .ranks]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -483,6 +485,8 @@ func channelAdminLogEvents(accountPeerId: PeerId, postbox: Postbox, network: Net
|
|||
case let .channelAdminLogEventActionToggleAutotranslation(channelAdminLogEventActionToggleAutotranslationData):
|
||||
let newValue = channelAdminLogEventActionToggleAutotranslationData.newValue
|
||||
action = .toggleAutoTranslation(boolFromApiValue(newValue))
|
||||
case let .channelAdminLogEventActionParticipantEditRank(channelAdminLogEventActionParticipantEditRankData):
|
||||
action = .editRank(peerId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(channelAdminLogEventActionParticipantEditRankData.userId)), prev: channelAdminLogEventActionParticipantEditRankData.prevRank, new: channelAdminLogEventActionParticipantEditRankData.newRank)
|
||||
}
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
if let action = action {
|
||||
|
|
|
|||
|
|
@ -184,7 +184,12 @@ func _internal_updateChannelAdminRights(account: Account, peerId: PeerId, adminI
|
|||
}
|
||||
updatedParticipant = .member(id: adminId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: adminInfo, banInfo: nil, rank: rank, subscriptionUntilDate: nil)
|
||||
}
|
||||
return account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? ""))
|
||||
|
||||
var flags: Int32 = 0
|
||||
if let _ = rank {
|
||||
flags = (1 << 0)
|
||||
}
|
||||
return account.network.request(Api.functions.channels.editAdmin(flags: flags, channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? ""))
|
||||
|> map { [$0] }
|
||||
|> `catch` { error -> Signal<[Api.Updates], UpdateChannelAdminRightsError> in
|
||||
if error.errorDescription == "USER_NOT_PARTICIPANT" {
|
||||
|
|
@ -196,7 +201,7 @@ func _internal_updateChannelAdminRights(account: Account, peerId: PeerId, adminI
|
|||
return .addMemberError(error)
|
||||
}
|
||||
|> then(
|
||||
account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? ""))
|
||||
account.network.request(Api.functions.channels.editAdmin(flags: flags, channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? ""))
|
||||
|> mapError { error -> UpdateChannelAdminRightsError in
|
||||
return .generic
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||
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)
|
||||
let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peer.id, participantId: context.account.peerId, rank: nil, role: .member)
|
||||
self.interfaceInteraction?.getNavigationController()?.pushViewController(tagController)
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -1054,7 +1054,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
|||
}
|
||||
|
||||
public func updatePeer(peer: Peer) {
|
||||
if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor {
|
||||
if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor || previousPeer.smallProfileImage != peer.smallProfileImage {
|
||||
self.peer = peer
|
||||
if peer.smallProfileImage != nil {
|
||||
self.avatarNode.setPeerV2(context: self.context, theme: self.presentationData.theme.theme, peer: EnginePeer(peer), authorOfMessage: self.messageReference, overrideImage: nil, emptyColor: .black, synchronousLoad: false, displayDimensions: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ private final class ChatRankInfoSheetContent: CombinedComponent {
|
|||
let textString: String
|
||||
let linkColor: UIColor
|
||||
var linkHasBackground = true
|
||||
let additionalTextString: String? = "Only admins can assign member tags in this group."
|
||||
let additionalTextString: String? = component.canChange ? nil : "Only admins can assign member tags in this group."
|
||||
var adminPreviewTag = "Admin Tag"
|
||||
var adminPreviewRole: ChatRankInfoScreenRole = .admin
|
||||
|
||||
|
|
@ -379,7 +379,7 @@ private final class ChatRankInfoSheetContent: CombinedComponent {
|
|||
var buttonTitle: [AnyComponentWithIdentity<Empty>] = []
|
||||
if canEdit {
|
||||
buttonTitle.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ButtonTextContentComponent(
|
||||
text: "Add My Tag",
|
||||
text: "Set My Tag",
|
||||
badge: 0,
|
||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -65,42 +65,23 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
|
||||
weak var controller: ViewController?
|
||||
|
||||
var memberId: EnginePeer.Id {
|
||||
let memberId: EnginePeer.Id
|
||||
switch subject {
|
||||
case let .admin(_, peerIdValue):
|
||||
memberId = peerIdValue
|
||||
case let .member(_, peerIdValue, _):
|
||||
memberId = peerIdValue
|
||||
case .rank:
|
||||
memberId = self.context.account.peerId
|
||||
}
|
||||
return memberId
|
||||
}
|
||||
|
||||
init(context: AccountContext, subject: ChatParticipantRightsScreen.Subject) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
|
||||
super.init()
|
||||
|
||||
let peerId: EnginePeer.Id
|
||||
let participantId: EnginePeer.Id
|
||||
switch subject {
|
||||
case let .admin(_, peerIdValue):
|
||||
peerId = peerIdValue
|
||||
case let .member(_, peerIdValue, rankValue):
|
||||
peerId = peerIdValue
|
||||
self.rank = rankValue
|
||||
self.initialRank = rankValue
|
||||
case let .rank(_, peerIdValue, rankValue):
|
||||
peerId = peerIdValue
|
||||
case let .rank(_, participantIdValue, rankValue, _):
|
||||
participantId = participantIdValue
|
||||
self.rank = rankValue
|
||||
self.initialRank = rankValue
|
||||
}
|
||||
|
||||
self.disposable = (context.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.Presence(id: peerId)
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: participantId),
|
||||
TelegramEngine.EngineData.Item.Peer.Presence(id: participantId)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer, presence in
|
||||
guard let self else {
|
||||
|
|
@ -117,28 +98,10 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
}
|
||||
|
||||
func complete() {
|
||||
var rank = self.rank
|
||||
if rank?.isEmpty == true {
|
||||
rank = nil
|
||||
}
|
||||
let _ = self.context.peerChannelMemberCategoriesContextsManager.updateMemberRank(engine: self.context.engine, peerId: self.subject.peerId, memberId: self.memberId, rank: rank).start()
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController {
|
||||
Queue.mainQueue().after(0.5) {
|
||||
//TODO:localize
|
||||
var title: String?
|
||||
var text: String
|
||||
if let rank {
|
||||
title = "Tag added"
|
||||
text = rank
|
||||
} else {
|
||||
text = "Tag removed"
|
||||
}
|
||||
let toastController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: title, text: text, cancel: nil, destructive: false), appearance: .init(isNarrow: true), action: { _ in return true})
|
||||
(navigationController.topViewController as? ViewController)?.present(toastController, in: .current)
|
||||
}
|
||||
guard let controller = self.controller as? ChatParticipantRightsScreen else {
|
||||
return
|
||||
}
|
||||
controller.complete(rank: self.rank)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +112,6 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
static var body: Body {
|
||||
let closeButton = Child(GlassBarButtonComponent.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let peerSection = Child(ListSectionComponent.self)
|
||||
let rankSection = Child(ListSectionComponent.self)
|
||||
let doneButton = Child(ButtonComponent.self)
|
||||
|
||||
|
|
@ -192,10 +154,6 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
|
||||
let titleString: String
|
||||
switch component.subject {
|
||||
case .member:
|
||||
titleString = "Member"
|
||||
case .admin:
|
||||
titleString = "Admin"
|
||||
case .rank:
|
||||
titleString = "Member Tag"
|
||||
}
|
||||
|
|
@ -215,67 +173,35 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
contentHeight += 44.0
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let itemPresentationData = ItemListPresentationData(theme: theme, fontSize: presentationData.listsFontSize, strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: environment.dateTimeFormat)
|
||||
|
||||
let listItemParams = ListViewItemLayoutParams(width: context.availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true)
|
||||
|
||||
switch component.subject {
|
||||
case .admin, .member:
|
||||
var peerSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
peerSectionItems.append(
|
||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
|
||||
itemGenerator: ItemListAvatarAndNameInfoItem(itemContext: .accountContext(component.context), presentationData: itemPresentationData, systemStyle: .glass, dateTimeFormat: environment.dateTimeFormat, mode: .generic, peer: state.peer, presence: state.presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: 0, style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { _ in
|
||||
}, avatarTapped: {
|
||||
}),
|
||||
params: listItemParams
|
||||
)))
|
||||
)
|
||||
|
||||
let peerSection = peerSection.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
style: .glass,
|
||||
header: nil,
|
||||
footer: nil,
|
||||
items: peerSectionItems
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(peerSection
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + peerSection.size.height / 2.0))
|
||||
)
|
||||
contentHeight += peerSection.size.height
|
||||
contentHeight += 38.0
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
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))
|
||||
peer = EnginePeer.user(TelegramUser(id: component.subject.participantId, accessHash: nil, firstName: " ", 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 .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:
|
||||
case let .rank(_, _, _, role):
|
||||
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️⃣"
|
||||
switch role {
|
||||
case .creator:
|
||||
rankPreviewPlaceholder = strings.Conversation_Owner
|
||||
case .admin:
|
||||
rankPreviewPlaceholder = strings.Conversation_Admin
|
||||
case .member:
|
||||
rankPreviewPlaceholder = "0️⃣"
|
||||
}
|
||||
rankRole = role
|
||||
}
|
||||
|
||||
let rankValue = state.rank ?? ""
|
||||
|
|
@ -363,7 +289,7 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
|
||||
let buttonTitle: String
|
||||
switch component.subject {
|
||||
case let .rank(_, _, initialRank):
|
||||
case let .rank(_, _, initialRank, _):
|
||||
if (initialRank ?? "").isEmpty && (state.rank ?? "").isEmpty {
|
||||
buttonTitle = "Add Later"
|
||||
} else if (initialRank ?? "").isEmpty && !(state.rank ?? "").isEmpty {
|
||||
|
|
@ -373,8 +299,6 @@ private final class ChatParticipantRightsContent: CombinedComponent {
|
|||
} else {
|
||||
buttonTitle = "Edit Tag"
|
||||
}
|
||||
default:
|
||||
buttonTitle = "Save Changes"
|
||||
}
|
||||
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
||||
|
|
@ -509,20 +433,21 @@ private final class ChatParticipantRightsComponent: CombinedComponent {
|
|||
|
||||
public class ChatParticipantRightsScreen: ViewControllerComponentContainer {
|
||||
public enum Subject {
|
||||
case member(peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?)
|
||||
case admin(peerId: EnginePeer.Id, participantId: EnginePeer.Id)
|
||||
case rank(peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?)
|
||||
case rank(peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?, role: ChatRankInfoScreenRole)
|
||||
|
||||
var peerId: EnginePeer.Id {
|
||||
switch self {
|
||||
case let .member(peerId, _, _):
|
||||
return peerId
|
||||
case let .admin(peerId, _):
|
||||
return peerId
|
||||
case let .rank(peerId, _, _):
|
||||
case let .rank(peerId, _, _, _):
|
||||
return peerId
|
||||
}
|
||||
}
|
||||
|
||||
var participantId: EnginePeer.Id {
|
||||
switch self {
|
||||
case let .rank(_, participantId, _, _):
|
||||
return participantId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
|
|
@ -559,6 +484,42 @@ public class ChatParticipantRightsScreen: ViewControllerComponentContainer {
|
|||
self.view.disablesInteractiveModalDismiss = true
|
||||
}
|
||||
|
||||
fileprivate func complete(rank: String?) {
|
||||
var rank = rank
|
||||
if rank?.isEmpty == true {
|
||||
rank = nil
|
||||
}
|
||||
|
||||
if let rank, rank.count > rankMaxLength {
|
||||
if let view = self.node.hostView.findTaggedView(tag: rankFieldTag) as? ListTextFieldItemComponent.View {
|
||||
view.animateError()
|
||||
HapticFeedback().error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let _ = self.context.peerChannelMemberCategoriesContextsManager.updateMemberRank(engine: self.context.engine, peerId: self.subject.peerId, memberId: self.subject.participantId, rank: rank).start()
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
Queue.mainQueue().after(0.5) {
|
||||
//TODO:localize
|
||||
var title: String?
|
||||
var text: String
|
||||
if let rank {
|
||||
title = "Tag added"
|
||||
text = rank
|
||||
} else {
|
||||
text = "Tag removed"
|
||||
}
|
||||
let toastController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: title, text: text, cancel: nil, destructive: false), appearance: .init(isNarrow: true), action: { _ in return true})
|
||||
(navigationController.topViewController as? ViewController)?.present(toastController, in: .current)
|
||||
}
|
||||
}
|
||||
|
||||
self.dismissAnimated()
|
||||
}
|
||||
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ public final class ListTextFieldItemComponent: Component {
|
|||
private let textField: TextField
|
||||
private let placeholder = ComponentView<Empty>()
|
||||
private let clearButton = ComponentView<Empty>()
|
||||
private let counter = ComponentView<Empty>()
|
||||
|
||||
private var component: ListTextFieldItemComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
|
@ -178,6 +179,10 @@ public final class ListTextFieldItemComponent: Component {
|
|||
return false
|
||||
}
|
||||
|
||||
public func animateError() {
|
||||
self.textField.layer.addShakeAnimation()
|
||||
}
|
||||
|
||||
public func activateInput() {
|
||||
self.textField.becomeFirstResponder()
|
||||
}
|
||||
|
|
@ -294,6 +299,31 @@ public final class ListTextFieldItemComponent: Component {
|
|||
clearButtonView.isHidden = self.currentText.isEmpty || !self.textField.isFirstResponder
|
||||
}
|
||||
|
||||
let text = self.textField.text ?? ""
|
||||
if let characterLimit = component.characterLimit, text.count > 0 {
|
||||
let counterSize = self.counter.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "\(characterLimit - text.count)", font: Font.with(size: 15.0, traits: .monospacedNumbers), textColor: text.count > characterLimit ? component.theme.list.itemDestructiveColor : component.theme.list.itemSecondaryTextColor)),
|
||||
horizontalAlignment: .right
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 30.0 - component.contentInsets.left - component.contentInsets.right, height: 100.0)
|
||||
)
|
||||
let counterFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - component.contentInsets.right - clearButtonSize.width - counterSize.width + 17.0, y: floor((contentHeight - counterSize.height) * 0.5)), size: counterSize)
|
||||
if let counterView = self.counter.view {
|
||||
if counterView.superview == nil {
|
||||
counterView.layer.anchorPoint = CGPoint()
|
||||
counterView.isUserInteractionEnabled = false
|
||||
self.addSubview(counterView)
|
||||
}
|
||||
transition.setPosition(view: counterView, position: counterFrame.origin)
|
||||
counterView.bounds = CGRect(origin: CGPoint(), size: counterFrame.size)
|
||||
}
|
||||
} else {
|
||||
self.counter.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
self.separatorInset = 16.0 + component.contentInsets.left
|
||||
|
||||
return CGSize(width: availableSize.width, height: contentHeight)
|
||||
|
|
|
|||
|
|
@ -4268,15 +4268,31 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
switch action {
|
||||
case .editRank:
|
||||
let rank: String?
|
||||
let role: ChatRankInfoScreenRole
|
||||
switch member {
|
||||
case let .channelMember(participant, _):
|
||||
rank = participant.participant.rank
|
||||
case let .legacyGroupMember(_, _, _, _, _, rankValue):
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
role = .creator
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
role = adminInfo != nil ? .admin : .member
|
||||
}
|
||||
case let .legacyGroupMember(_, roleValue, _, _, _, rankValue):
|
||||
rank = rankValue
|
||||
switch roleValue {
|
||||
case .creator:
|
||||
role = .creator
|
||||
case .admin:
|
||||
role = .admin
|
||||
case .member:
|
||||
role = .member
|
||||
}
|
||||
default:
|
||||
rank = nil
|
||||
role = .member
|
||||
}
|
||||
let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: peer.id, participantId: member.id, rank: rank)
|
||||
let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: peer.id, participantId: member.id, rank: rank, role: role)
|
||||
self.controller?.push(controller)
|
||||
case .promote:
|
||||
if case let .channelMember(channelMember, _) = member {
|
||||
|
|
|
|||
|
|
@ -174,8 +174,6 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||
private let topOverscrollLayer = SimpleLayer()
|
||||
private let scrollView: ScrollView
|
||||
private let actionButton = ComponentView<Empty>()
|
||||
private let bottomPanelBackgroundView: BlurredBackgroundView
|
||||
private let bottomPanelSeparator: SimpleLayer
|
||||
|
||||
private let navigationTitle = ComponentView<Empty>()
|
||||
|
||||
|
|
@ -242,9 +240,6 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||
}
|
||||
self.scrollView.alwaysBounceVertical = true
|
||||
|
||||
self.bottomPanelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
|
||||
self.bottomPanelSeparator = SimpleLayer()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.scrollView.delegate = self
|
||||
|
|
@ -253,9 +248,6 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||
self.scrollView.layer.addSublayer(self.topOverscrollLayer)
|
||||
|
||||
self.addSubview(self.edgeEffectView)
|
||||
|
||||
self.addSubview(self.bottomPanelBackgroundView)
|
||||
self.layer.addSublayer(self.bottomPanelSeparator)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -348,13 +340,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||
transition.setAlpha(view: navigationTitleView, alpha: navigationAlpha)
|
||||
}
|
||||
|
||||
transition.setAlpha(view: self.edgeEffectView, alpha: navigationAlpha)
|
||||
|
||||
let bottomNavigationAlphaDistance: CGFloat = 16.0
|
||||
let bottomNavigationAlpha: CGFloat = max(0.0, min(1.0, (self.scrollView.contentSize.height - self.scrollView.bounds.maxY) / bottomNavigationAlphaDistance))
|
||||
|
||||
transition.setAlpha(view: self.bottomPanelBackgroundView, alpha: bottomNavigationAlpha)
|
||||
transition.setAlpha(layer: self.bottomPanelSeparator, alpha: bottomNavigationAlpha)
|
||||
//transition.setAlpha(view: self.edgeEffectView, alpha: navigationAlpha)
|
||||
}
|
||||
|
||||
private func resolveState() -> ResolvedState? {
|
||||
|
|
@ -1007,11 +993,6 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||
}
|
||||
self.requiredBoostSubject = requiredBoostSubject
|
||||
|
||||
let edgeEffectHeight: CGFloat = environment.navigationHeight + 8.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
|
||||
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: min(30, edgeEffectFrame.height), transition: transition)
|
||||
|
||||
let headerColor: UIColor
|
||||
if let profileColor {
|
||||
let headerBackgroundColors = component.context.peerNameColors.getProfile(profileColor, dark: environment.theme.overallDarkAppearance, subject: .background)
|
||||
|
|
@ -1843,13 +1824,10 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||
transition.setAlpha(view: buttonView, alpha: 1.0)
|
||||
}
|
||||
|
||||
let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: buttonY - 8.0), size: CGSize(width: availableSize.width, height: availableSize.height - buttonY + 8.0))
|
||||
transition.setFrame(view: self.bottomPanelBackgroundView, frame: bottomPanelFrame)
|
||||
self.bottomPanelBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||
self.bottomPanelBackgroundView.update(size: bottomPanelFrame.size, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
self.bottomPanelSeparator.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||
transition.setFrame(layer: self.bottomPanelSeparator, frame: CGRect(origin: CGPoint(x: bottomPanelFrame.minX, y: bottomPanelFrame.minY), size: CGSize(width: bottomPanelFrame.width, height: UIScreenPixel)))
|
||||
let edgeEffectHeight: CGFloat = availableSize.height - buttonY + 36.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - edgeEffectHeight), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
|
||||
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, alpha: 1.0, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: transition)
|
||||
|
||||
let previousBounds = self.scrollView.bounds
|
||||
|
||||
|
|
@ -1860,10 +1838,6 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||
if self.scrollView.contentSize != contentSize {
|
||||
self.scrollView.contentSize = contentSize
|
||||
}
|
||||
let scrollInsets = UIEdgeInsets(top: environment.navigationHeight, left: 0.0, bottom: availableSize.height - bottomPanelFrame.minY, right: 0.0)
|
||||
if self.scrollView.verticalScrollIndicatorInsets != scrollInsets {
|
||||
self.scrollView.verticalScrollIndicatorInsets = scrollInsets
|
||||
}
|
||||
|
||||
if !previousBounds.isEmpty, !transition.animation.isImmediate {
|
||||
let bounds = self.scrollView.bounds
|
||||
|
|
|
|||
|
|
@ -5275,19 +5275,46 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
guard let self, let chatPeer = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel else {
|
||||
return
|
||||
}
|
||||
var canChange = false
|
||||
let chatParticipant = Promise<ChannelParticipant?>()
|
||||
if let defaultBannedRights = chatPeer.defaultBannedRights {
|
||||
canChange = !defaultBannedRights.flags.contains(.banEditRank)
|
||||
|
||||
if canChange {
|
||||
chatParticipant.set(self.context.engine.peers.fetchChannelParticipant(peerId: chatPeer.id, participantId: self.context.account.peerId))
|
||||
}
|
||||
}
|
||||
let controller = self.context.sharedContext.makeChatRankInfoScreen(
|
||||
context: self.context,
|
||||
chatPeer: EnginePeer(chatPeer),
|
||||
userPeer: peer,
|
||||
role: role,
|
||||
rank: rank,
|
||||
canChange: false, //!(chatPeer.defaultBannedRights?.flags.contains(.editRank) ?? true),
|
||||
canChange: canChange,
|
||||
completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: chatPeer.id, participantId: self.context.account.peerId, rank: nil)
|
||||
self.push(controller)
|
||||
let _ = (chatParticipant.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] participant in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var rank: String? = nil
|
||||
var role: ChatRankInfoScreenRole = .member
|
||||
switch participant {
|
||||
case let .creator(_, _, rankValue):
|
||||
rank = rankValue
|
||||
role = .creator
|
||||
case let .member(_, _, adminInfo, _, rankValue, _):
|
||||
rank = rankValue
|
||||
role = adminInfo != nil ? .admin : .member
|
||||
default:
|
||||
break
|
||||
}
|
||||
let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: chatPeer.id, participantId: self.context.account.peerId, rank: rank, role: role)
|
||||
self.push(controller)
|
||||
})
|
||||
}
|
||||
)
|
||||
self.push(controller)
|
||||
|
|
|
|||
|
|
@ -4317,8 +4317,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return PasskeysScreen(context: context, displaySkip: displaySkip, initialPasskeysData: nil, passkeysDataUpdated: { _ in }, completion: completion, cancel: dismiss)
|
||||
}
|
||||
|
||||
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))
|
||||
public func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?, role: ChatRankInfoScreenRole) -> ViewController {
|
||||
return ChatParticipantRightsScreen(context: context, subject: .rank(peerId: peerId, participantId: participantId, rank: rank, role: role))
|
||||
}
|
||||
|
||||
public func makePeerCopyProtectionInfoScreen(context: AccountContext, completion: @escaping () -> Void) -> ViewController {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue