mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Various improvements
This commit is contained in:
parent
4ffb0e697e
commit
a663ea50cd
@ -13743,3 +13743,12 @@ Sorry for the inconvenience.";
|
|||||||
"Media.ChooseFromGallery" = "Choose From Gallery";
|
"Media.ChooseFromGallery" = "Choose From Gallery";
|
||||||
"Media.SelectFrame" = "Select Frame";
|
"Media.SelectFrame" = "Select Frame";
|
||||||
"Media.SaveCover" = "Save Cover";
|
"Media.SaveCover" = "Save Cover";
|
||||||
|
|
||||||
|
"Gift.Withdraw.SecurityCheck" = "Security Check";
|
||||||
|
"Gift.Withdraw.SecurityRequirements" = "Gift withdrawals are available if:\n\n• 2-Step verification was enabled for your account more than **7 days** ago.\n\n• You have logged in on this device more than **24 hours** ago.";
|
||||||
|
"Gift.Withdraw.ComeBackLater" = "\n\nPlease come back later.";
|
||||||
|
"Gift.Withdraw.SetupTwoStepAuth" = "Enable 2-Step Verification";
|
||||||
|
|
||||||
|
"Gift.Withdraw.EnterPassword.Title" = "Enter Password";
|
||||||
|
"Gift.Withdraw.EnterPassword.Text" = "Please enter your Two-Step Verification password to complete this action.";
|
||||||
|
"Gift.Withdraw.EnterPassword.Done" = "Proceed";
|
||||||
|
@ -20,9 +20,15 @@ public class ContactListActionItem: ListViewItem, ListViewItemWithHeader {
|
|||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Height {
|
||||||
|
case generic
|
||||||
|
case tall
|
||||||
|
}
|
||||||
|
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
let title: String
|
let title: String
|
||||||
let subtitle: String?
|
let subtitle: String?
|
||||||
|
let height: Height
|
||||||
let icon: ContactListActionItemIcon
|
let icon: ContactListActionItemIcon
|
||||||
let style: Style
|
let style: Style
|
||||||
let highlight: ContactListActionItemHighlight
|
let highlight: ContactListActionItemHighlight
|
||||||
@ -31,12 +37,13 @@ public class ContactListActionItem: ListViewItem, ListViewItemWithHeader {
|
|||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
public let header: ListViewItemHeader?
|
public let header: ListViewItemHeader?
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, title: String, subtitle: String? = nil, icon: ContactListActionItemIcon, style: Style = .accent, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, accessible: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) {
|
public init(presentationData: ItemListPresentationData, title: String, subtitle: String? = nil, icon: ContactListActionItemIcon, style: Style = .accent, height: Height = .generic, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, accessible: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.title = title
|
self.title = title
|
||||||
self.subtitle = subtitle
|
self.subtitle = subtitle
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.style = style
|
self.style = style
|
||||||
|
self.height = height
|
||||||
self.highlight = highlight
|
self.highlight = highlight
|
||||||
self.header = header
|
self.header = header
|
||||||
self.clearHighlightAutomatically = clearHighlightAutomatically
|
self.clearHighlightAutomatically = clearHighlightAutomatically
|
||||||
@ -223,7 +230,9 @@ class ContactListActionItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
let contentHeight: CGFloat
|
let contentHeight: CGFloat
|
||||||
let verticalInset: CGFloat = subtitleAttributedString != nil ? 6.0 : 12.0
|
let verticalInset: CGFloat = subtitleAttributedString != nil ? 6.0 : 12.0
|
||||||
if case .alpha = item.highlight {
|
if case .tall = item.height {
|
||||||
|
contentHeight = 50.0
|
||||||
|
} else if case .alpha = item.highlight {
|
||||||
contentHeight = 50.0
|
contentHeight = 50.0
|
||||||
} else {
|
} else {
|
||||||
contentHeight = verticalInset * 2.0 + titleLayout.size.height + subtitleHeightComponent
|
contentHeight = verticalInset * 2.0 + titleLayout.size.height + subtitleHeightComponent
|
||||||
|
@ -145,13 +145,16 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
})
|
})
|
||||||
case let .option(_, option, header, _, _):
|
case let .option(_, option, header, _, _):
|
||||||
let style: ContactListActionItem.Style
|
let style: ContactListActionItem.Style
|
||||||
|
let height: ContactListActionItem.Height
|
||||||
switch option.style {
|
switch option.style {
|
||||||
case .accent:
|
case .accent:
|
||||||
style = .accent
|
style = .accent
|
||||||
|
height = .generic
|
||||||
case .generic:
|
case .generic:
|
||||||
style = .generic
|
style = .generic
|
||||||
|
height = .tall
|
||||||
}
|
}
|
||||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, subtitle: option.subtitle, icon: option.icon, style: style, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action)
|
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, subtitle: option.subtitle, icon: option.icon, style: style, height: height, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action)
|
||||||
case let .peer(_, peer, presence, header, selection, _, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, hasMoreButton, enabled, storyData, requiresPremiumForMessaging, customSubtitle):
|
case let .peer(_, peer, presence, header, selection, _, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, hasMoreButton, enabled, storyData, requiresPremiumForMessaging, customSubtitle):
|
||||||
var status: ContactsPeerItemStatus
|
var status: ContactsPeerItemStatus
|
||||||
let itemPeer: ContactsPeerItemPeer
|
let itemPeer: ContactsPeerItemPeer
|
||||||
|
@ -1381,7 +1381,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
||||||
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
|
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
|
||||||
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
|
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
|
||||||
dict[1154859627] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) }
|
dict[-1779201615] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) }
|
||||||
dict[377215243] = { return Api.payments.StarGiftUpgradePreview.parse_starGiftUpgradePreview($0) }
|
dict[377215243] = { return Api.payments.StarGiftUpgradePreview.parse_starGiftUpgradePreview($0) }
|
||||||
dict[-2069218660] = { return Api.payments.StarGiftWithdrawalUrl.parse_starGiftWithdrawalUrl($0) }
|
dict[-2069218660] = { return Api.payments.StarGiftWithdrawalUrl.parse_starGiftWithdrawalUrl($0) }
|
||||||
dict[-1877571094] = { return Api.payments.StarGifts.parse_starGifts($0) }
|
dict[-1877571094] = { return Api.payments.StarGifts.parse_starGifts($0) }
|
||||||
|
@ -1404,16 +1404,17 @@ public extension Api.payments {
|
|||||||
}
|
}
|
||||||
public extension Api.payments {
|
public extension Api.payments {
|
||||||
enum SavedStarGifts: TypeConstructorDescription {
|
enum SavedStarGifts: TypeConstructorDescription {
|
||||||
case savedStarGifts(flags: Int32, count: Int32, gifts: [Api.SavedStarGift], nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
case savedStarGifts(flags: Int32, count: Int32, chatNotificationsEnabled: Api.Bool?, gifts: [Api.SavedStarGift], nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .savedStarGifts(let flags, let count, let gifts, let nextOffset, let chats, let users):
|
case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let gifts, let nextOffset, let chats, let users):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1154859627)
|
buffer.appendInt32(-1779201615)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(count, buffer: buffer, boxed: false)
|
serializeInt32(count, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 1) != 0 {chatNotificationsEnabled!.serialize(buffer, true)}
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
buffer.appendInt32(Int32(gifts.count))
|
buffer.appendInt32(Int32(gifts.count))
|
||||||
for item in gifts {
|
for item in gifts {
|
||||||
@ -1436,8 +1437,8 @@ public extension Api.payments {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .savedStarGifts(let flags, let count, let gifts, let nextOffset, let chats, let users):
|
case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let gifts, let nextOffset, let chats, let users):
|
||||||
return ("savedStarGifts", [("flags", flags as Any), ("count", count as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
return ("savedStarGifts", [("flags", flags as Any), ("count", count as Any), ("chatNotificationsEnabled", chatNotificationsEnabled as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1446,28 +1447,33 @@ public extension Api.payments {
|
|||||||
_1 = reader.readInt32()
|
_1 = reader.readInt32()
|
||||||
var _2: Int32?
|
var _2: Int32?
|
||||||
_2 = reader.readInt32()
|
_2 = reader.readInt32()
|
||||||
var _3: [Api.SavedStarGift]?
|
var _3: Api.Bool?
|
||||||
|
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||||
|
_3 = Api.parse(reader, signature: signature) as? Api.Bool
|
||||||
|
} }
|
||||||
|
var _4: [Api.SavedStarGift]?
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedStarGift.self)
|
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedStarGift.self)
|
||||||
}
|
}
|
||||||
var _4: String?
|
var _5: String?
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
|
if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) }
|
||||||
var _5: [Api.Chat]?
|
var _6: [Api.Chat]?
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||||
}
|
}
|
||||||
var _6: [Api.User]?
|
var _7: [Api.User]?
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||||
}
|
}
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
let _c4 = _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
let _c7 = _7 != nil
|
||||||
return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, gifts: _3!, nextOffset: _4, chats: _5!, users: _6!)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||||
|
return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, chatNotificationsEnabled: _3, gifts: _4!, nextOffset: _5, chats: _6!, users: _7!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -9647,6 +9647,22 @@ public extension Api.functions.payments {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.payments {
|
||||||
|
static func toggleChatStarGiftNotifications(flags: Int32, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(1626009505)
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
peer.serialize(buffer, true)
|
||||||
|
return (FunctionDescription(name: "payments.toggleChatStarGiftNotifications", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Bool?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.payments {
|
public extension Api.functions.payments {
|
||||||
static func transferStarGift(stargift: Api.InputSavedStarGift, toId: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
static func transferStarGift(stargift: Api.InputSavedStarGift, toId: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
|
@ -866,14 +866,17 @@ private final class CachedProfileGifts: Codable {
|
|||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case gifts
|
case gifts
|
||||||
case count
|
case count
|
||||||
|
case notificationsEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
var gifts: [ProfileGiftsContext.State.StarGift]
|
var gifts: [ProfileGiftsContext.State.StarGift]
|
||||||
let count: Int32
|
let count: Int32
|
||||||
|
let notificationsEnabled: Bool?
|
||||||
|
|
||||||
init(gifts: [ProfileGiftsContext.State.StarGift], count: Int32) {
|
init(gifts: [ProfileGiftsContext.State.StarGift], count: Int32, notificationsEnabled: Bool?) {
|
||||||
self.gifts = gifts
|
self.gifts = gifts
|
||||||
self.count = count
|
self.count = count
|
||||||
|
self.notificationsEnabled = notificationsEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
@ -881,6 +884,7 @@ private final class CachedProfileGifts: Codable {
|
|||||||
|
|
||||||
self.gifts = try container.decode([ProfileGiftsContext.State.StarGift].self, forKey: .gifts)
|
self.gifts = try container.decode([ProfileGiftsContext.State.StarGift].self, forKey: .gifts)
|
||||||
self.count = try container.decode(Int32.self, forKey: .count)
|
self.count = try container.decode(Int32.self, forKey: .count)
|
||||||
|
self.notificationsEnabled = try container.decodeIfPresent(Bool.self, forKey: .notificationsEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws {
|
func encode(to encoder: Encoder) throws {
|
||||||
@ -888,6 +892,7 @@ private final class CachedProfileGifts: Codable {
|
|||||||
|
|
||||||
try container.encode(self.gifts, forKey: .gifts)
|
try container.encode(self.gifts, forKey: .gifts)
|
||||||
try container.encode(self.count, forKey: .count)
|
try container.encode(self.count, forKey: .count)
|
||||||
|
try container.encodeIfPresent(self.notificationsEnabled, forKey: .notificationsEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
func render(transaction: Transaction) {
|
func render(transaction: Transaction) {
|
||||||
@ -918,6 +923,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
private var gifts: [ProfileGiftsContext.State.StarGift] = []
|
private var gifts: [ProfileGiftsContext.State.StarGift] = []
|
||||||
private var count: Int32?
|
private var count: Int32?
|
||||||
private var dataState: ProfileGiftsContext.State.DataState = .ready(canLoadMore: true, nextOffset: nil)
|
private var dataState: ProfileGiftsContext.State.DataState = .ready(canLoadMore: true, nextOffset: nil)
|
||||||
|
private var notificationsEnabled: Bool?
|
||||||
|
|
||||||
var _state: ProfileGiftsContext.State?
|
var _state: ProfileGiftsContext.State?
|
||||||
private let stateValue = Promise<ProfileGiftsContext.State>()
|
private let stateValue = Promise<ProfileGiftsContext.State>()
|
||||||
@ -958,6 +964,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
if case .loading = self.dataState {
|
if case .loading = self.dataState {
|
||||||
self.gifts = cachedGifts.gifts
|
self.gifts = cachedGifts.gifts
|
||||||
self.count = cachedGifts.count
|
self.count = cachedGifts.count
|
||||||
|
self.notificationsEnabled = cachedGifts.notificationsEnabled
|
||||||
self.pushState()
|
self.pushState()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -966,12 +973,12 @@ private final class ProfileGiftsContextImpl {
|
|||||||
self.dataState = .loading
|
self.dataState = .loading
|
||||||
self.pushState()
|
self.pushState()
|
||||||
|
|
||||||
let signal: Signal<([ProfileGiftsContext.State.StarGift], Int32, String?), NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
let signal: Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
}
|
}
|
||||||
|> mapToSignal { inputPeer -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?), NoError> in
|
|> mapToSignal { inputPeer -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in
|
||||||
guard let inputPeer else {
|
guard let inputPeer else {
|
||||||
return .single(([], 0, nil))
|
return .single(([], 0, nil, nil))
|
||||||
}
|
}
|
||||||
let flags: Int32 = 0
|
let flags: Int32 = 0
|
||||||
return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, offset: initialNextOffset ?? "", limit: 32))
|
return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, offset: initialNextOffset ?? "", limit: 32))
|
||||||
@ -979,25 +986,34 @@ private final class ProfileGiftsContextImpl {
|
|||||||
|> `catch` { _ -> Signal<Api.payments.SavedStarGifts?, NoError> in
|
|> `catch` { _ -> Signal<Api.payments.SavedStarGifts?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?), NoError> in
|
|> mapToSignal { result -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in
|
||||||
guard let result else {
|
guard let result else {
|
||||||
return .single(([], 0, nil))
|
return .single(([], 0, nil, nil))
|
||||||
}
|
}
|
||||||
return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?) in
|
return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?) in
|
||||||
switch result {
|
switch result {
|
||||||
case let .savedStarGifts(_, count, apiGifts, nextOffset, chats, users):
|
case let .savedStarGifts(_, count, apiNotificationsEnabled, apiGifts, nextOffset, chats, users):
|
||||||
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
||||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
||||||
|
|
||||||
|
var notificationsEnabled: Bool?
|
||||||
|
if let apiNotificationsEnabled {
|
||||||
|
if case .boolTrue = apiNotificationsEnabled {
|
||||||
|
notificationsEnabled = true
|
||||||
|
} else {
|
||||||
|
notificationsEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let gifts = apiGifts.compactMap { ProfileGiftsContext.State.StarGift(apiSavedStarGift: $0, peerId: peerId, transaction: transaction) }
|
let gifts = apiGifts.compactMap { ProfileGiftsContext.State.StarGift(apiSavedStarGift: $0, peerId: peerId, transaction: transaction) }
|
||||||
return (gifts, count, nextOffset)
|
return (gifts, count, nextOffset, notificationsEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.disposable.set((signal
|
self.disposable.set((signal
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] (gifts, count, nextOffset) in
|
|> deliverOn(self.queue)).start(next: { [weak self] (gifts, count, nextOffset, notificationsEnabled) in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1005,7 +1021,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
strongSelf.gifts = gifts
|
strongSelf.gifts = gifts
|
||||||
|
|
||||||
strongSelf.cacheDisposable.set(strongSelf.account.postbox.transaction { transaction in
|
strongSelf.cacheDisposable.set(strongSelf.account.postbox.transaction { transaction in
|
||||||
if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count)) {
|
if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count, notificationsEnabled: notificationsEnabled)) {
|
||||||
transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry)
|
transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry)
|
||||||
}
|
}
|
||||||
}.start())
|
}.start())
|
||||||
@ -1018,6 +1034,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
let updatedCount = max(Int32(strongSelf.gifts.count), count)
|
let updatedCount = max(Int32(strongSelf.gifts.count), count)
|
||||||
strongSelf.count = updatedCount
|
strongSelf.count = updatedCount
|
||||||
strongSelf.dataState = .ready(canLoadMore: count != 0 && updatedCount > strongSelf.gifts.count && nextOffset != nil, nextOffset: nextOffset)
|
strongSelf.dataState = .ready(canLoadMore: count != 0 && updatedCount > strongSelf.gifts.count && nextOffset != nil, nextOffset: nextOffset)
|
||||||
|
strongSelf.notificationsEnabled = notificationsEnabled
|
||||||
strongSelf.pushState()
|
strongSelf.pushState()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -1082,8 +1099,8 @@ private final class ProfileGiftsContextImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func pushState() {
|
private func pushState() {
|
||||||
self._state = ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState)
|
self._state = ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled)
|
||||||
self.stateValue.set(.single(ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState)))
|
self.stateValue.set(.single(ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1246,6 +1263,7 @@ public final class ProfileGiftsContext {
|
|||||||
public var gifts: [ProfileGiftsContext.State.StarGift]
|
public var gifts: [ProfileGiftsContext.State.StarGift]
|
||||||
public var count: Int32?
|
public var count: Int32?
|
||||||
public var dataState: ProfileGiftsContext.State.DataState
|
public var dataState: ProfileGiftsContext.State.DataState
|
||||||
|
public var notificationsEnabled: Bool?
|
||||||
}
|
}
|
||||||
|
|
||||||
private let queue: Queue = .mainQueue()
|
private let queue: Queue = .mainQueue()
|
||||||
@ -1591,3 +1609,24 @@ func _internal_requestStarGiftWithdrawalUrl(account: Account, reference: StarGif
|
|||||||
|> mapError { _ -> RequestStarGiftWithdrawalError in }
|
|> mapError { _ -> RequestStarGiftWithdrawalError in }
|
||||||
|> switchToLatest
|
|> switchToLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _internal_toggleStarGiftsNotifications(account: Account, peerId: EnginePeer.Id, enabled: Bool) -> Signal<Never, NoError> {
|
||||||
|
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
|
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
|
}
|
||||||
|
|> mapToSignal { inputPeer in
|
||||||
|
guard let inputPeer else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
var flags: Int32 = 0
|
||||||
|
if enabled {
|
||||||
|
flags |= (1 << 0)
|
||||||
|
}
|
||||||
|
return account.network.request(Api.functions.payments.toggleChatStarGiftNotifications(flags: flags, peer: inputPeer))
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -140,5 +140,13 @@ public extension TelegramEngine {
|
|||||||
public func checkStarGiftWithdrawalAvailability(reference: StarGiftReference) -> Signal<Never, RequestStarGiftWithdrawalError> {
|
public func checkStarGiftWithdrawalAvailability(reference: StarGiftReference) -> Signal<Never, RequestStarGiftWithdrawalError> {
|
||||||
return _internal_checkStarGiftWithdrawalAvailability(account: self.account, reference: reference)
|
return _internal_checkStarGiftWithdrawalAvailability(account: self.account, reference: reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func requestStarGiftWithdrawalUrl(reference: StarGiftReference, password: String) -> Signal<String, RequestStarGiftWithdrawalError> {
|
||||||
|
return _internal_requestStarGiftWithdrawalUrl(account: account, reference: reference, password: password)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func toggleStarGiftsNotifications(peerId: EnginePeer.Id, enabled: Bool) -> Signal<Never, NoError> {
|
||||||
|
return _internal_toggleStarGiftsNotifications(account: self.account, peerId: peerId, enabled: enabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ public final class GiftItemComponent: Component {
|
|||||||
public enum Color: Equatable {
|
public enum Color: Equatable {
|
||||||
case red
|
case red
|
||||||
case blue
|
case blue
|
||||||
|
case purple
|
||||||
case custom(Int32, Int32)
|
case custom(Int32, Int32)
|
||||||
|
|
||||||
func colors(theme: PresentationTheme) -> [UIColor] {
|
func colors(theme: PresentationTheme) -> [UIColor] {
|
||||||
@ -54,6 +55,11 @@ public final class GiftItemComponent: Component {
|
|||||||
UIColor(rgb: 0x6fd3ff)
|
UIColor(rgb: 0x6fd3ff)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
case .purple:
|
||||||
|
return [
|
||||||
|
UIColor(rgb: 0x747bf6),
|
||||||
|
UIColor(rgb: 0xe367d8)
|
||||||
|
]
|
||||||
case let .custom(topColor, _):
|
case let .custom(topColor, _):
|
||||||
return [
|
return [
|
||||||
UIColor(rgb: UInt32(bitPattern: topColor)).withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89),
|
UIColor(rgb: UInt32(bitPattern: topColor)).withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89),
|
||||||
@ -489,7 +495,7 @@ public final class GiftItemComponent: Component {
|
|||||||
ribbonTextView.bounds = CGRect(origin: .zero, size: ribbonTextSize)
|
ribbonTextView.bounds = CGRect(origin: .zero, size: ribbonTextSize)
|
||||||
|
|
||||||
if self.ribbon.image == nil || themeUpdated || previousComponent?.ribbon?.color != component.ribbon?.color {
|
if self.ribbon.image == nil || themeUpdated || previousComponent?.ribbon?.color != component.ribbon?.color {
|
||||||
var direction: GradientImageDirection = .diagonal
|
var direction: GradientImageDirection = .mirroredDiagonal
|
||||||
if case .custom = ribbon.color {
|
if case .custom = ribbon.color {
|
||||||
direction = .mirroredDiagonal
|
direction = .mirroredDiagonal
|
||||||
}
|
}
|
||||||
|
@ -781,7 +781,7 @@ final class GiftOptionsScreenComponent: Component {
|
|||||||
ribbon: product.discount.flatMap {
|
ribbon: product.discount.flatMap {
|
||||||
GiftItemComponent.Ribbon(
|
GiftItemComponent.Ribbon(
|
||||||
text: "-\($0)%",
|
text: "-\($0)%",
|
||||||
color: .red
|
color: .purple
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
isLoading: self.inProgressPremiumGift == product.id
|
isLoading: self.inProgressPremiumGift == product.id
|
||||||
|
@ -44,6 +44,8 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||||
"//submodules/MoreButtonNode",
|
"//submodules/MoreButtonNode",
|
||||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||||
|
"//submodules/PasswordSetupUI",
|
||||||
|
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -3,6 +3,7 @@ import UIKit
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
|
import SwiftSignalKit
|
||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
@ -12,6 +13,9 @@ import AppBundle
|
|||||||
import Markdown
|
import Markdown
|
||||||
import GiftItemComponent
|
import GiftItemComponent
|
||||||
import StarsAvatarComponent
|
import StarsAvatarComponent
|
||||||
|
import PasswordSetupUI
|
||||||
|
import OwnershipTransferController
|
||||||
|
import PresentationDataUtils
|
||||||
|
|
||||||
private final class GiftWithdrawAlertContentNode: AlertContentNode {
|
private final class GiftWithdrawAlertContentNode: AlertContentNode {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -292,3 +296,104 @@ public func giftWithdrawAlertController(context: AccountContext, gift: StarGift.
|
|||||||
}
|
}
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func confirmGiftWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, reference: StarGiftReference, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController {
|
||||||
|
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
var dismissImpl: (() -> Void)?
|
||||||
|
var proceedImpl: (() -> Void)?
|
||||||
|
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.Gift_Withdraw_EnterPassword_Title, text: presentationData.strings.Gift_Withdraw_EnterPassword_Text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
|
dismissImpl?()
|
||||||
|
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Withdraw_EnterPassword_Done, action: {
|
||||||
|
proceedImpl?()
|
||||||
|
})])
|
||||||
|
|
||||||
|
contentNode.complete = {
|
||||||
|
proceedImpl?()
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
||||||
|
let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in
|
||||||
|
controller?.theme = AlertControllerTheme(presentationData: presentationData)
|
||||||
|
contentNode?.theme = presentationData.theme
|
||||||
|
})
|
||||||
|
controller.dismissed = { _ in
|
||||||
|
presentationDataDisposable.dispose()
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
dismissImpl = { [weak controller, weak contentNode] in
|
||||||
|
contentNode?.dismissInput()
|
||||||
|
controller?.dismissAnimated()
|
||||||
|
}
|
||||||
|
proceedImpl = { [weak contentNode] in
|
||||||
|
guard let contentNode = contentNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
contentNode.updateIsChecking(true)
|
||||||
|
|
||||||
|
let signal = context.engine.payments.requestStarGiftWithdrawalUrl(reference: reference, password: contentNode.password)
|
||||||
|
disposable.set((signal |> deliverOnMainQueue).start(next: { url in
|
||||||
|
dismissImpl?()
|
||||||
|
completion(url)
|
||||||
|
}, error: { [weak contentNode] error in
|
||||||
|
var errorTextAndActions: (String, [TextAlertAction])?
|
||||||
|
switch error {
|
||||||
|
case .invalidPassword:
|
||||||
|
contentNode?.animateError()
|
||||||
|
case .limitExceeded:
|
||||||
|
errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||||
|
default:
|
||||||
|
errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||||
|
}
|
||||||
|
contentNode?.updateIsChecking(false)
|
||||||
|
|
||||||
|
if let (text, actions) = errorTextAndActions {
|
||||||
|
dismissImpl?()
|
||||||
|
present(textAlertController(context: context, title: nil, text: text, actions: actions), nil)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
public func giftWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, reference: StarGiftReference, initialError: RequestStarGiftWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController {
|
||||||
|
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let theme = AlertControllerTheme(presentationData: presentationData)
|
||||||
|
|
||||||
|
var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.Gift_Withdraw_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||||
|
|
||||||
|
var text = presentationData.strings.Gift_Withdraw_SecurityRequirements
|
||||||
|
let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0
|
||||||
|
|
||||||
|
var actions: [TextAlertAction] = []
|
||||||
|
switch initialError {
|
||||||
|
case .requestPassword:
|
||||||
|
return confirmGiftWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, reference: reference, present: present, completion: completion)
|
||||||
|
case .twoStepAuthTooFresh, .authSessionTooFresh:
|
||||||
|
text = text + presentationData.strings.Gift_Withdraw_ComeBackLater
|
||||||
|
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
|
||||||
|
case .twoStepAuthMissing:
|
||||||
|
actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.Gift_Withdraw_SetupTwoStepAuth, action: {
|
||||||
|
let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in
|
||||||
|
if shouldDismiss {
|
||||||
|
controller.dismiss()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})]
|
||||||
|
default:
|
||||||
|
title = nil
|
||||||
|
text = presentationData.strings.Login_UnknownError
|
||||||
|
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor)
|
||||||
|
let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor)
|
||||||
|
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||||
|
|
||||||
|
return richTextAlertController(context: context, title: title, text: attributedText, actions: actions)
|
||||||
|
}
|
||||||
|
@ -72,7 +72,6 @@ public func confirmStarsRevenueWithdrawalController(context: AccountContext, upd
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public func starsRevenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, amount: Int64, initialError: RequestStarsRevenueWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController {
|
public func starsRevenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, amount: Int64, initialError: RequestStarsRevenueWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController {
|
||||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let theme = AlertControllerTheme(presentationData: presentationData)
|
let theme = AlertControllerTheme(presentationData: presentationData)
|
||||||
|
@ -2450,13 +2450,34 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
presentExportAlertImpl = { [weak controller] in
|
presentExportAlertImpl = { [weak controller] in
|
||||||
guard let controller, case let .starGiftTransfer(_, _, gift, _, canExportDate) = source, let canExportDate else {
|
guard let controller, case let .starGiftTransfer(_, reference, gift, _, canExportDate) = source, let canExportDate else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
if currentTime > canExportDate || "".isEmpty {
|
if currentTime > canExportDate || "".isEmpty {
|
||||||
let alertController = giftWithdrawAlertController(context: context, gift: gift, commit: {
|
let alertController = giftWithdrawAlertController(context: context, gift: gift, commit: {
|
||||||
|
let _ = (context.engine.payments.checkStarGiftWithdrawalAvailability(reference: reference)
|
||||||
|
|> deliverOnMainQueue).start(error: { [weak controller] error in
|
||||||
|
switch error {
|
||||||
|
case .serverProvided:
|
||||||
|
return
|
||||||
|
case .requestPassword:
|
||||||
|
let alertController = confirmGiftWithdrawalController(context: context, reference: reference, present: { [weak controller] c, a in
|
||||||
|
controller?.present(c, in: .window(.root))
|
||||||
|
}, completion: { url in
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||||
|
})
|
||||||
|
controller?.present(alertController, in: .window(.root))
|
||||||
|
default:
|
||||||
|
let alertController = giftWithdrawalController(context: context, reference: reference, initialError: error, present: { [weak controller] c, a in
|
||||||
|
controller?.present(c, in: .window(.root))
|
||||||
|
}, completion: { _ in
|
||||||
|
|
||||||
|
})
|
||||||
|
controller?.present(alertController, in: .window(.root))
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
controller.present(alertController, in: .window(.root))
|
controller.present(alertController, in: .window(.root))
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user