From 6240a14697b3388fd29ef1deffb1c6e62ba38d97 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 12 Feb 2026 14:04:46 +0400 Subject: [PATCH 1/2] Crafting improvements --- .../TelegramEngine/Payments/StarGifts.swift | 38 ++++++++++++++++--- .../Sources/CraftTableComponent.swift | 5 +-- .../Sources/GiftCraftScreen.swift | 22 ++++++++--- .../Sources/GiftUpgradeVariantsScreen.swift | 5 ++- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index b26492a0b1..508d038a42 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -2372,8 +2372,22 @@ private final class ProfileGiftsContextImpl { self.pushState() } - func insertStarGifts(gifts: [ProfileGiftsContext.State.StarGift]) { - self.gifts.insert(contentsOf: gifts, at: 0) + func insertStarGifts(gifts: [ProfileGiftsContext.State.StarGift], afterPinned: Bool = false) { + if afterPinned { + var added = false + for index in 0 ..< self.gifts.count { + if !self.gifts[index].pinnedToTop { + added = true + self.gifts.insert(contentsOf: gifts, at: index) + break + } + } + if !added { + self.gifts.append(contentsOf: gifts) + } + } else { + self.gifts.insert(contentsOf: gifts, at: 0) + } self.pushState() let peerId = self.peerId @@ -2387,7 +2401,21 @@ private final class ProfileGiftsContextImpl { } else { updatedGifts = [] } - updatedGifts.insert(contentsOf: gifts, at: 0) + if afterPinned { + var added = false + for index in 0 ..< updatedGifts.count { + if !updatedGifts[index].pinnedToTop { + added = true + updatedGifts.insert(contentsOf: gifts, at: index) + break + } + } + if !added { + updatedGifts.append(contentsOf: gifts) + } + } else { + updatedGifts.insert(contentsOf: gifts, at: 0) + } updatedCount += Int32(gifts.count) if let entry = CodableEntry(CachedProfileGifts(gifts: updatedGifts, count: updatedCount, notificationsEnabled: nil)) { transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry) @@ -3087,9 +3115,9 @@ public final class ProfileGiftsContext { } } - public func insertStarGifts(gifts: [ProfileGiftsContext.State.StarGift]) { + public func insertStarGifts(gifts: [ProfileGiftsContext.State.StarGift], afterPinned: Bool = false) { self.impl.with { impl in - impl.insertStarGifts(gifts: gifts) + impl.insertStarGifts(gifts: gifts, afterPinned: afterPinned) } } diff --git a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/CraftTableComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/CraftTableComponent.swift index 7ad3195f89..6dc23e8792 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/CraftTableComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/CraftTableComponent.swift @@ -526,9 +526,6 @@ final class GiftSlotComponent: Component { } @objc private func buttonPressed() { - guard let _ = self.component?.removeAction else { - return - } self.component?.action() } @@ -630,7 +627,7 @@ final class GiftSlotComponent: Component { ) ), environment: {}, - containerSize: CGSize(width: availableSize.width, height: availableSize.height) + containerSize: CGSize(width: availableSize.width - 2.0 + UIScreenPixel, height: availableSize.height - 2.0 + UIScreenPixel) ) let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: iconSize) if let iconView = icon.view { diff --git a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift index f7072a03c3..7e08547313 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift @@ -430,7 +430,12 @@ private final class CraftGiftPageContent: Component { transition.setAlpha(view: titleView, alpha: 1.0) } - let giftTitle = "\(component.gift.title) #\(formatCollectibleNumber(component.gift.number, dateTimeFormat: environment.dateTimeFormat))" + var selectedMainGift = component.gift + if component.selectedGiftIds[0] != selectedMainGift.id, let id = component.selectedGiftIds[0], let gift = self.giftMap[id]?.gift { + selectedMainGift = gift + } + + let giftTitle = "\(selectedMainGift.title) #\(formatCollectibleNumber(selectedMainGift.number, dateTimeFormat: environment.dateTimeFormat))" let descriptionFont = Font.regular(13.0) let descriptionBoldFont = Font.semibold(13.0) @@ -1160,7 +1165,7 @@ private final class CraftGiftPageContent: Component { context: component.context, craftContext: component.craftContext, resaleContext: resaleContext, - gift: component.gift, + gift: selectedMainGift, genericGift: genericGift, selectedGiftIds: Set(component.selectedGiftIds.values), starsTopUpOptions: component.starsTopUpOptionsPromise.get(), @@ -1219,7 +1224,7 @@ private final class CraftGiftPageContent: Component { navigationController.view.addSubview(ConfettiView(frame: navigationController.view.bounds)) } Queue.mainQueue().after(0.5) { - controller.profileGiftsContext?.insertStarGifts(gifts: [gift]) + controller.profileGiftsContext?.insertStarGifts(gifts: [gift], afterPinned: true) } } controller.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false, completion: { _ in @@ -1257,7 +1262,7 @@ private final class CraftGiftPageContent: Component { GiftCompositionComponent( context: component.context, theme: environment.theme, - subject: .unique(nil, component.gift), + subject: .unique(nil, selectedMainGift), animationOffset: nil, animationScale: nil, displayAnimationStars: false, @@ -1621,8 +1626,13 @@ private final class SheetContainerComponent: CombinedComponent { } } + var selectedMainGift = component.gift + if state.selectedGiftIds[0] != selectedMainGift.id, let id = state.selectedGiftIds[0], let gift = externalState.giftsMap[id]?.gift { + selectedMainGift = gift + } + var buttonColor = colors.3 - if state.displayInfo, let backdropAttribute = component.gift.attributes.first(where: { attribute in + if state.displayInfo, let backdropAttribute = selectedMainGift.attributes.first(where: { attribute in if case .backdrop = attribute { return true } else { @@ -1637,7 +1647,7 @@ private final class SheetContainerComponent: CombinedComponent { backgroundColor = environment.theme.list.plainBackgroundColor } - let giftTitle = "\(component.gift.title) #\(formatCollectibleNumber(component.gift.number, dateTimeFormat: environment.dateTimeFormat))" + let giftTitle = "\(selectedMainGift.title) #\(formatCollectibleNumber(selectedMainGift.number, dateTimeFormat: environment.dateTimeFormat))" let buttonContent: AnyComponentWithIdentity if state.displayInfo { diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift index f16c63f1fa..8634d11664 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift @@ -692,7 +692,10 @@ private final class GiftUpgradeVariantsScreenComponent: Component { self.isPlaying = false for attribute in selectedAttributes { switch attribute { - case .model: + case let .model(_, _, _, crafted): + if crafted { + self.displayCraftableModels = true + } self.selectedModel = attribute case .pattern: self.selectedSymbol = attribute From c0f7cda4eb496a6b7a379da78de62bcb66edcddf Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 12 Feb 2026 15:37:57 +0400 Subject: [PATCH 2/2] Update API --- submodules/TelegramApi/Sources/Api0.swift | 2 +- submodules/TelegramApi/Sources/Api29.swift | 26 ++++++-- submodules/TelegramApi/Sources/Api40.swift | 25 ++------ .../Sources/ApiUtils/ApiGroupOrChannel.swift | 6 -- .../ApiUtils/StoreMessage_Telegram.swift | 2 +- .../Sources/ApiUtils/TelegramChannel.swift | 19 ++++++ .../TextEntitiesMessageAttribute.swift | 5 +- .../SyncCore_CachedGroupParticipants.swift | 11 ++++ .../SyncCore/SyncCore_TelegramChannel.swift | 1 - .../SyncCore_TelegramChatAdminRights.swift | 6 +- .../SyncCore_TelegramChatBannedRights.swift | 1 + ...yncCore_TextEntitiesMessageAttribute.swift | 9 ++- .../RequestMessageActionCallback.swift | 4 +- .../Sources/TelegramEngine/Peers/Ranks.swift | 63 ++++++++++++------- .../Peers/TelegramEnginePeers.swift | 8 +-- .../TextFormat/Sources/DateFormat.swift | 3 +- .../Sources/GenerateTextEntities.swift | 6 +- 17 files changed, 126 insertions(+), 71 deletions(-) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 79d69f5fa5..80fc5c1f00 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -1244,7 +1244,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-484987010] = { return Api.Updates.parse_updatesTooLong($0) } dict[1648005024] = { return Api.UrlAuthResult.parse_urlAuthResultAccepted($0) } dict[-1445536993] = { return Api.UrlAuthResult.parse_urlAuthResultDefault($0) } - dict[855293722] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } + dict[-948574935] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } dict[829899656] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } dict[-1607745218] = { return Api.UserFull.parse_userFull($0) } diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 9afe0f8fff..c961de4f5d 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -556,7 +556,8 @@ public extension Api { public var platform: String? public var ip: String? public var region: String? - public init(flags: Int32, bot: Api.User, domain: String, browser: String?, platform: String?, ip: String?, region: String?) { + public var matchCodes: [String]? + public init(flags: Int32, bot: Api.User, domain: String, browser: String?, platform: String?, ip: String?, region: String?, matchCodes: [String]?) { self.flags = flags self.bot = bot self.domain = domain @@ -564,6 +565,7 @@ public extension Api { self.platform = platform self.ip = ip self.region = region + self.matchCodes = matchCodes } } case urlAuthResultAccepted(Cons_urlAuthResultAccepted) @@ -588,7 +590,7 @@ public extension Api { break case .urlAuthResultRequest(let _data): if boxed { - buffer.appendInt32(855293722) + buffer.appendInt32(-948574935) } serializeInt32(_data.flags, buffer: buffer, boxed: false) _data.bot.serialize(buffer, true) @@ -605,6 +607,13 @@ public extension Api { if Int(_data.flags) & Int(1 << 2) != 0 { serializeString(_data.region!, buffer: buffer, boxed: false) } + if Int(_data.flags) & Int(1 << 3) != 0 { + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(_data.matchCodes!.count)) + for item in _data.matchCodes! { + serializeString(item, buffer: buffer, boxed: false) + } + } break } } @@ -616,7 +625,7 @@ public extension Api { case .urlAuthResultDefault: return ("urlAuthResultDefault", []) case .urlAuthResultRequest(let _data): - return ("urlAuthResultRequest", [("flags", _data.flags as Any), ("bot", _data.bot as Any), ("domain", _data.domain as Any), ("browser", _data.browser as Any), ("platform", _data.platform as Any), ("ip", _data.ip as Any), ("region", _data.region as Any)]) + return ("urlAuthResultRequest", [("flags", _data.flags as Any), ("bot", _data.bot as Any), ("domain", _data.domain as Any), ("browser", _data.browser as Any), ("platform", _data.platform as Any), ("ip", _data.ip as Any), ("region", _data.region as Any), ("matchCodes", _data.matchCodes as Any)]) } } @@ -664,6 +673,12 @@ public extension Api { if Int(_1!) & Int(1 << 2) != 0 { _7 = parseString(reader) } + var _8: [String]? + if Int(_1!) & Int(1 << 3) != 0 { + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) + } + } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -671,8 +686,9 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.UrlAuthResult.urlAuthResultRequest(Cons_urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!, browser: _4, platform: _5, ip: _6, region: _7)) + let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.UrlAuthResult.urlAuthResultRequest(Cons_urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!, browser: _4, platform: _5, ip: _6, region: _7, matchCodes: _8)) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api40.swift b/submodules/TelegramApi/Sources/Api40.swift index cf20de843f..3afdb241d3 100644 --- a/submodules/TelegramApi/Sources/Api40.swift +++ b/submodules/TelegramApi/Sources/Api40.swift @@ -5192,9 +5192,9 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func acceptUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func acceptUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?, matchCode: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-1322487515) + buffer.appendInt32(1738797278) serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 { peer!.serialize(buffer, true) @@ -5208,7 +5208,10 @@ public extension Api.functions.messages { if Int(flags) & Int(1 << 2) != 0 { serializeString(url!, buffer: buffer, boxed: false) } - return (FunctionDescription(name: "messages.acceptUrlAuth", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("url", String(describing: url))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in + if Int(flags) & Int(1 << 4) != 0 { + serializeString(matchCode!, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.acceptUrlAuth", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("buttonId", String(describing: buttonId)), ("url", String(describing: url)), ("matchCode", String(describing: matchCode))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in let reader = BufferReader(buffer) var result: Api.UrlAuthResult? if let signature = reader.readInt32() { @@ -9460,22 +9463,6 @@ public extension Api.functions.messages { }) } } -public extension Api.functions.messages { - static func toggleChatCustomRanks(peer: Api.InputPeer, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(874013158) - peer.serialize(buffer, true) - enabled.serialize(buffer, true) - return (FunctionDescription(name: "messages.toggleChatCustomRanks", parameters: [("peer", String(describing: peer)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} public extension Api.functions.messages { static func toggleDialogFilterTags(enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index af0b1edc79..00f9636cc2 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -59,9 +59,6 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { if (flags & Int32(1 << 25)) != 0 { groupFlags.insert(.copyProtectionEnabled) } - if (flags & Int32(1 << 19)) != 0 { - groupFlags.insert(.customRanksEnabled) - } return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: title, photo: imageRepresentationsForApiChatPhoto(photo), participantCount: Int(participantsCount), role: role, membership: left ? .Left : .Member, flags: groupFlags, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init(apiBannedRights:)), migrationReference: migrationReference, creationDate: date, version: Int(version)) case let .chatEmpty(chatEmptyData): let id = chatEmptyData.id @@ -152,9 +149,6 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { if (flags2 & Int32(1 << 19)) != 0 { channelFlags.insert(.displayForumAsTabs) } - if (flags2 & Int32(1 << 20)) != 0 { - channelFlags.insert(.customRanksEnabled) - } var storiesHidden: Bool? if flags2 & (1 << 2) == 0 { // stories_hidden_min if flags2 & (1 << 1) != 0 { diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 3f2a1f75e0..d407448150 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -809,7 +809,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes } else { dateFormat = nil } - format = .full(timeFormat: timeFormat, dateFormat: dateFormat) + format = .full(timeFormat: timeFormat, dateFormat: dateFormat, dayOfWeek: (flags & (1 << 5)) != 0) } result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .FormattedDate(format: format, date: date))) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift index eb67eab945..98dfdd7f56 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift @@ -21,6 +21,8 @@ public enum TelegramChannelPermission { case editStories case deleteStories case manageDirect + case editRank + case manageRanks } public extension TelegramChannel { @@ -261,6 +263,23 @@ public extension TelegramChannel { } else { return false } + case .editRank: + if let adminRights = self.adminRights, adminRights.rights.contains(.canManageRanks) { + return true + } + if let bannedRights = self.bannedRights, bannedRights.flags.contains(.banEditRank) { + return false + } + if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banEditRank) { + return false + } + return true + case .manageRanks: + if let adminRights = self.adminRights { + return adminRights.rights.contains(.canManageRanks) + } else { + return false + } } } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift index 6cfab74975..87b626659a 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift @@ -59,7 +59,7 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa switch format { case .relative: flags |= 1 << 0 - case let .full(timeFormat, dateFormat): + case let .full(timeFormat, dateFormat, dayOfWeek): switch timeFormat { case .short: flags |= 1 << 1 @@ -76,6 +76,9 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa default: break } + if dayOfWeek { + flags |= 1 << 5 + } default: break } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupParticipants.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupParticipants.swift index 88da92502f..a9aa446168 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupParticipants.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupParticipants.swift @@ -73,6 +73,17 @@ public enum GroupParticipant: PostboxCoding, Equatable { } } + public var rank: String? { + switch self { + case let .admin(_, _, _, rank): + return rank + case let .member(_, _, _, rank): + return rank + case let .creator(_, rank): + return rank + } + } + func withUpdated(rank: String?) -> GroupParticipant { switch self { case let .member(id, invitedBy, invitedAt, _): diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift index 283f6a4c83..0cc9e8b16f 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift @@ -185,7 +185,6 @@ public struct TelegramChannelFlags: OptionSet { public static let autoTranslateEnabled = TelegramChannelFlags(rawValue: 1 << 12) public static let isMonoforum = TelegramChannelFlags(rawValue: 1 << 13) public static let displayForumAsTabs = TelegramChannelFlags(rawValue: 1 << 14) - public static let customRanksEnabled = TelegramChannelFlags(rawValue: 1 << 15) } public final class TelegramChannel: Peer, Equatable { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift index 5d270bf553..26c741a196 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift @@ -28,9 +28,10 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable { public static let canEditStories = TelegramChatAdminRightsFlags(rawValue: 1 << 15) public static let canDeleteStories = TelegramChatAdminRightsFlags(rawValue: 1 << 16) public static let canManageDirect = TelegramChatAdminRightsFlags(rawValue: 1 << 17) + public static let canManageRanks = TelegramChatAdminRightsFlags(rawValue: 1 << 18) public static var all: TelegramChatAdminRightsFlags { - return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canBeAnonymous, .canManageCalls, .canManageTopics, .canPostStories, .canEditStories, .canDeleteStories] + return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canBeAnonymous, .canManageCalls, .canManageTopics, .canPostStories, .canEditStories, .canDeleteStories, .canManageRanks] } public static var allChannel: TelegramChatAdminRightsFlags { @@ -48,7 +49,8 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable { .canAddAdmins, .canPostStories, .canEditStories, - .canDeleteStories + .canDeleteStories, + .canManageRanks ] public static let internal_broadcastSpecific: TelegramChatAdminRightsFlags = [ diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift index 3048c0832a..f0ca1387f8 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift @@ -32,6 +32,7 @@ public struct TelegramChatBannedRightsFlags: OptionSet, Hashable { public static let banSendVoice = TelegramChatBannedRightsFlags(rawValue: 1 << 23) public static let banSendFiles = TelegramChatBannedRightsFlags(rawValue: 1 << 24) public static let banSendText = TelegramChatBannedRightsFlags(rawValue: 1 << 25) + public static let banEditRank = TelegramChatBannedRightsFlags(rawValue: 1 << 26) } public struct TelegramChatBannedRights: PostboxCoding, Equatable { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index a688ed00d3..89feec5ed7 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -16,7 +16,7 @@ public enum MessageTextEntityType: Equatable { } case relative - case full(timeFormat: TimeFormat?, dateFormat: DateFormat?) + case full(timeFormat: TimeFormat?, dateFormat: DateFormat?, dayOfWeek: Bool) public init(rawValue: Int32) { if (rawValue & (1 << 0)) != 0 { @@ -38,7 +38,7 @@ public enum MessageTextEntityType: Equatable { } else { dateFormat = nil } - self = .full(timeFormat: timeFormat, dateFormat: dateFormat) + self = .full(timeFormat: timeFormat, dateFormat: dateFormat, dayOfWeek: (rawValue & (1 << 5)) != 0) } } @@ -47,7 +47,7 @@ public enum MessageTextEntityType: Equatable { switch self { case .relative: rawValue |= 1 << 0 - case let .full(timeFormat, dateFormat): + case let .full(timeFormat, dateFormat, dayOfWeek): switch timeFormat { case .short: rawValue |= 1 << 1 @@ -64,6 +64,9 @@ public enum MessageTextEntityType: Equatable { default: break } + if dayOfWeek { + rawValue |= 1 << 5 + } } return rawValue } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift index 2d35a58765..82d08c4738 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift @@ -283,7 +283,7 @@ func _internal_acceptMessageActionUrlAuth(account: Account, subject: MessageActi |> mapToSignal { peer -> Signal in if let inputPeer = apiInputPeer(peer) { let flags: Int32 = 1 << 1 - return account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId, url: nil)) + return account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId, url: nil, matchCode: nil)) |> map(Optional.init) } else { return .single(nil) @@ -291,7 +291,7 @@ func _internal_acceptMessageActionUrlAuth(account: Account, subject: MessageActi } case let .url(url): flags |= (1 << 2) - request = account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: nil, msgId: nil, buttonId: nil, url: url)) + request = account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: nil, msgId: nil, buttonId: nil, url: url, matchCode: nil)) |> map(Optional.init) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift index b1f012e0c5..a7cd926924 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Ranks.swift @@ -4,35 +4,55 @@ import SwiftSignalKit import TelegramApi import MtProtoKit -func _internal_toggleChatCustomRanks(account: Account, peerId: PeerId, enabled: Bool) -> Signal { - return account.postbox.transaction { transaction -> Signal in - if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { - return account.network.request(Api.functions.messages.toggleChatCustomRanks(peer: inputPeer, enabled: enabled ? .boolTrue : .boolFalse)) - |> `catch` { _ in - return .complete() - } - |> map { updates -> Void in - account.stateManager.addUpdates(updates) - } - } else { - return .complete() - } - } |> switchToLatest -} - public enum UpdateChatRankError { case generic + case changeForbidden + case chatAdminRequired + case chatCreatorRequired + case notParticipant } -func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, rank: String?) -> Signal { - return account.postbox.transaction { transaction -> Signal in +func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> { + return account.postbox.transaction { transaction -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> in if let user = transaction.getPeer(userId), let inputUser = apiInputPeer(user), let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { return account.network.request(Api.functions.messages.editChatParticipantRank(peer: inputPeer, participant: inputUser, rank: rank ?? "")) - |> mapError { _ -> UpdateChatRankError in - return .generic + |> mapError { error -> UpdateChatRankError in + if error.errorDescription == "CHAT_ADMIN_REQUIRED" { + return .chatAdminRequired + } else if error.errorDescription == "CHAT_CREATOR_REQUIRED" { + return .chatCreatorRequired + } else if error.errorDescription == "USER_NOT_PARTICIPANT" { + return .notParticipant + } else if error.errorDescription == "RANK_CHANGE_FORBIDDEN" { + return .changeForbidden + } else { + return .generic + } } - |> map { updates -> Void in + |> mapToSignal { updates -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> in account.stateManager.addUpdates(updates) + + if peerId.namespace == Namespaces.Peer.CloudGroup { + return account.postbox.transaction { transaction -> (ChannelParticipant?, RenderedChannelParticipant)? in + transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, current in + if let current = current as? CachedGroupData, let participants = current.participants { + var updatedParticipants = participants.participants + if let index = updatedParticipants.firstIndex(where: { $0.peerId == userId }) { + updatedParticipants[index] = updatedParticipants[index].withUpdated(rank: rank) + } + return current.withUpdatedParticipants(CachedGroupParticipants(participants: updatedParticipants, version: participants.version + 2)) + } else { + return current + } + }) + return nil + } + |> castError(UpdateChatRankError.self) + } else { + let participant: ChannelParticipant = .member(id: userId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: nil, banInfo: nil, rank: rank, subscriptionUntilDate: nil) + let timestamp = Int32(Date().timeIntervalSince1970) + return .single((participant, RenderedChannelParticipant(participant: participant, peer: user, presences: [userId: TelegramUserPresence(status: .present(until: timestamp + 60), lastActivity: timestamp)]))) + } } } else { return .fail(.generic) @@ -40,7 +60,6 @@ func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, } |> mapError { _ -> UpdateChatRankError in } |> switchToLatest - |> ignoreValues } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 2c7240dfca..c58e116352 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -616,12 +616,8 @@ public extension TelegramEngine { public func getFutureCreatorAfterLeave(peerId: EnginePeer.Id) -> Signal { return _internal_getFutureCreatorAfterLeave(account: self.account, peerId: peerId) } - - public func toggleChatCustomRanks(peerId: PeerId, enabled: Bool) -> Signal { - return _internal_toggleChatCustomRanks(account: self.account, peerId: peerId, enabled: enabled) - } - - public func updateChatRank(account: Account, peerId: PeerId, userId: PeerId, rank: String?) -> Signal { + + public func updateChatRank(peerId: PeerId, userId: PeerId, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> { return _internal_updateChatRank(account: account, peerId: peerId, userId: userId, rank: rank) } diff --git a/submodules/TextFormat/Sources/DateFormat.swift b/submodules/TextFormat/Sources/DateFormat.swift index c92ffb7d56..c9a2d5ee1a 100644 --- a/submodules/TextFormat/Sources/DateFormat.swift +++ b/submodules/TextFormat/Sources/DateFormat.swift @@ -30,7 +30,8 @@ public func stringForEntityFormattedDate(timestamp: Int32, format: MessageTextEn return strings.FormattedDate_InDays(Int32(round(Float(value) / (24 * 60 * 60)))) } } - case let .full(timeFormat, dateFormat): + case let .full(timeFormat, dateFormat, dayOfWeek): + let _ = dayOfWeek var string = "" if let dateFormat { switch dateFormat { diff --git a/submodules/TextFormat/Sources/GenerateTextEntities.swift b/submodules/TextFormat/Sources/GenerateTextEntities.swift index 7a3aaaa559..39c3c6ed9b 100644 --- a/submodules/TextFormat/Sources/GenerateTextEntities.swift +++ b/submodules/TextFormat/Sources/GenerateTextEntities.swift @@ -300,6 +300,7 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType type = .Url } else if result.resultType == NSTextCheckingResult.CheckingType.date, let date = result.date?.timeIntervalSince1970 { #if DEBUG + var dayOfWeek = false var format: MessageTextEntityType.DateTimeFormat? if text.contains("[rel]") { format = .relative @@ -316,8 +317,11 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType } else if text.contains("[ld]") { dateFormat = .long } + if text.contains("[d]") { + dayOfWeek = true + } if timeFormat != nil || dateFormat != nil { - format = .full(timeFormat: timeFormat, dateFormat: dateFormat) + format = .full(timeFormat: timeFormat, dateFormat: dateFormat, dayOfWeek: dayOfWeek) } type = .FormattedDate(format: format, date: Int32(date)) #else