mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various improvements
This commit is contained in:
parent
bcbcc94b58
commit
ec0f8028aa
@ -663,9 +663,10 @@ public final class ContactSelectionControllerParams {
|
|||||||
public let displayDeviceContacts: Bool
|
public let displayDeviceContacts: Bool
|
||||||
public let displayCallIcons: Bool
|
public let displayCallIcons: Bool
|
||||||
public let multipleSelection: Bool
|
public let multipleSelection: Bool
|
||||||
|
public let requirePhoneNumbers: Bool
|
||||||
public let confirmation: (ContactListPeer) -> Signal<Bool, NoError>
|
public let confirmation: (ContactListPeer) -> Signal<Bool, NoError>
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: [ContactListAdditionalOption] = [], displayDeviceContacts: Bool = false, displayCallIcons: Bool = false, multipleSelection: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal<Bool, NoError> = { _ in .single(true) }) {
|
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: [ContactListAdditionalOption] = [], displayDeviceContacts: Bool = false, displayCallIcons: Bool = false, multipleSelection: Bool = false, requirePhoneNumbers: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal<Bool, NoError> = { _ in .single(true) }) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.updatedPresentationData = updatedPresentationData
|
self.updatedPresentationData = updatedPresentationData
|
||||||
self.autoDismiss = autoDismiss
|
self.autoDismiss = autoDismiss
|
||||||
@ -674,6 +675,7 @@ public final class ContactSelectionControllerParams {
|
|||||||
self.displayDeviceContacts = displayDeviceContacts
|
self.displayDeviceContacts = displayDeviceContacts
|
||||||
self.displayCallIcons = displayCallIcons
|
self.displayCallIcons = displayCallIcons
|
||||||
self.multipleSelection = multipleSelection
|
self.multipleSelection = multipleSelection
|
||||||
|
self.requirePhoneNumbers = requirePhoneNumbers
|
||||||
self.confirmation = confirmation
|
self.confirmation = confirmation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@ public enum ContactMultiselectionControllerMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum ContactListFilter {
|
public enum ContactListFilter {
|
||||||
|
case excludeWithoutPhoneNumbers
|
||||||
case excludeSelf
|
case excludeSelf
|
||||||
case exclude([EnginePeer.Id])
|
case exclude([EnginePeer.Id])
|
||||||
case disable([EnginePeer.Id])
|
case disable([EnginePeer.Id])
|
||||||
|
@ -113,7 +113,7 @@ private final class ContentNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
let image = generateImage(size, rotatedContext: { size, context in
|
let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id)
|
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id, nameColor: peer.nameColor)
|
||||||
})!
|
})!
|
||||||
self.updateImage(image: image, size: size, spacing: spacing)
|
self.updateImage(image: image, size: size, spacing: spacing)
|
||||||
}
|
}
|
||||||
@ -346,7 +346,7 @@ public final class AnimatedAvatarSetView: UIView {
|
|||||||
} else {
|
} else {
|
||||||
let image = generateImage(size, rotatedContext: { size, context in
|
let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id)
|
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id, nameColor: peer.nameColor)
|
||||||
})!
|
})!
|
||||||
self.updateImage(image: image, size: size, spacing: spacing)
|
self.updateImage(image: image, size: size, spacing: spacing)
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ public func peerAvatarCompleteImage(postbox: Postbox, network: Network, peer: En
|
|||||||
iconSignal = Signal { subscriber in
|
iconSignal = Signal { subscriber in
|
||||||
let image = generateImage(size, rotatedContext: { size, context in
|
let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), round: round, font: font, letters: displayLetters, peerId: peerId)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), round: round, font: font, letters: displayLetters, peerId: peerId, nameColor: peer.nameColor)
|
||||||
if blurred {
|
if blurred {
|
||||||
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
|
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
|
||||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||||
@ -346,7 +346,7 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool = true, font: UIFont, letters: [String], peerId: EnginePeer.Id) {
|
public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool = true, font: UIFont, letters: [String], peerId: EnginePeer.Id, nameColor: PeerNameColor?) {
|
||||||
if round {
|
if round {
|
||||||
context.beginPath()
|
context.beginPath()
|
||||||
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height:
|
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height:
|
||||||
@ -365,7 +365,11 @@ public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool
|
|||||||
if colorIndex == -1 {
|
if colorIndex == -1 {
|
||||||
colorsArray = AvatarNode.grayscaleColors.map(\.cgColor) as NSArray
|
colorsArray = AvatarNode.grayscaleColors.map(\.cgColor) as NSArray
|
||||||
} else {
|
} else {
|
||||||
colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count].map(\.cgColor) as NSArray
|
var index = colorIndex % AvatarNode.gradientColors.count
|
||||||
|
if let nameColor {
|
||||||
|
index = Int(nameColor.rawValue) % AvatarNode.gradientColors.count
|
||||||
|
}
|
||||||
|
colorsArray = AvatarNode.gradientColors[index].map(\.cgColor) as NSArray
|
||||||
}
|
}
|
||||||
|
|
||||||
var locations: [CGFloat] = [1.0, 0.0]
|
var locations: [CGFloat] = [1.0, 0.0]
|
||||||
|
@ -1085,15 +1085,18 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
|
|
||||||
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
||||||
var excludeSelf = false
|
var excludeSelf = false
|
||||||
|
var requirePhoneNumbers = false
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
switch filter {
|
switch filter {
|
||||||
case .excludeSelf:
|
case .excludeSelf:
|
||||||
excludeSelf = true
|
excludeSelf = true
|
||||||
existingPeerIds.insert(context.account.peerId)
|
existingPeerIds.insert(context.account.peerId)
|
||||||
case let .exclude(peerIds):
|
case let .exclude(peerIds):
|
||||||
existingPeerIds = existingPeerIds.union(peerIds)
|
existingPeerIds = existingPeerIds.union(peerIds)
|
||||||
case let .disable(peerIds):
|
case let .disable(peerIds):
|
||||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||||
|
case .excludeWithoutPhoneNumbers:
|
||||||
|
requirePhoneNumbers = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,8 +1130,13 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
for peer in remotePeers.0 {
|
for peer in remotePeers.0 {
|
||||||
let matches: Bool
|
let matches: Bool
|
||||||
if peer.peer is TelegramUser {
|
if let user = peer.peer as? TelegramUser {
|
||||||
matches = true
|
let phone = user.phone ?? ""
|
||||||
|
if requirePhoneNumbers && phone.isEmpty {
|
||||||
|
matches = false
|
||||||
|
} else {
|
||||||
|
matches = true
|
||||||
|
}
|
||||||
} else if searchGroups || searchChannels {
|
} else if searchGroups || searchChannels {
|
||||||
if peer.peer is TelegramGroup && searchGroups {
|
if peer.peer is TelegramGroup && searchGroups {
|
||||||
matches = true
|
matches = true
|
||||||
@ -1157,8 +1165,13 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
for peer in remotePeers.1 {
|
for peer in remotePeers.1 {
|
||||||
let matches: Bool
|
let matches: Bool
|
||||||
if peer.peer is TelegramUser {
|
if let user = peer.peer as? TelegramUser {
|
||||||
matches = true
|
let phone = user.phone ?? ""
|
||||||
|
if requirePhoneNumbers && phone.isEmpty {
|
||||||
|
matches = false
|
||||||
|
} else {
|
||||||
|
matches = true
|
||||||
|
}
|
||||||
} else if searchGroups || searchChannels {
|
} else if searchGroups || searchChannels {
|
||||||
if peer.peer is TelegramGroup {
|
if peer.peer is TelegramGroup {
|
||||||
matches = searchGroups
|
matches = searchGroups
|
||||||
@ -1270,23 +1283,32 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
var existingPeerIds = Set<EnginePeer.Id>()
|
var existingPeerIds = Set<EnginePeer.Id>()
|
||||||
var disabledPeerIds = Set<EnginePeer.Id>()
|
var disabledPeerIds = Set<EnginePeer.Id>()
|
||||||
|
var requirePhoneNumbers = false
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
switch filter {
|
switch filter {
|
||||||
case .excludeSelf:
|
case .excludeSelf:
|
||||||
existingPeerIds.insert(context.account.peerId)
|
existingPeerIds.insert(context.account.peerId)
|
||||||
case let .exclude(peerIds):
|
case let .exclude(peerIds):
|
||||||
existingPeerIds = existingPeerIds.union(peerIds)
|
existingPeerIds = existingPeerIds.union(peerIds)
|
||||||
case let .disable(peerIds):
|
case let .disable(peerIds):
|
||||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||||
|
case .excludeWithoutPhoneNumbers:
|
||||||
|
requirePhoneNumbers = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peers = peers.filter { contact in
|
peers = peers.filter { contact in
|
||||||
switch contact {
|
switch contact {
|
||||||
case let .peer(peer, _, _):
|
case let .peer(peer, _, _):
|
||||||
return !existingPeerIds.contains(peer.id)
|
if requirePhoneNumbers, let user = peer as? TelegramUser {
|
||||||
default:
|
let phone = user.phone ?? ""
|
||||||
return true
|
if phone.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !existingPeerIds.contains(peer.id)
|
||||||
|
default:
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,6 +322,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
var entries: [ContactListSearchEntry] = []
|
var entries: [ContactListSearchEntry] = []
|
||||||
var existingPeerIds = Set<EnginePeer.Id>()
|
var existingPeerIds = Set<EnginePeer.Id>()
|
||||||
var disabledPeerIds = Set<EnginePeer.Id>()
|
var disabledPeerIds = Set<EnginePeer.Id>()
|
||||||
|
var requirePhoneNumbers = false
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
switch filter {
|
switch filter {
|
||||||
case .excludeSelf:
|
case .excludeSelf:
|
||||||
@ -330,6 +331,8 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
existingPeerIds = existingPeerIds.union(peerIds)
|
existingPeerIds = existingPeerIds.union(peerIds)
|
||||||
case let .disable(peerIds):
|
case let .disable(peerIds):
|
||||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||||
|
case .excludeWithoutPhoneNumbers:
|
||||||
|
requirePhoneNumbers = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
||||||
@ -338,6 +341,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
if existingPeerIds.contains(peer.id) {
|
if existingPeerIds.contains(peer.id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .user(user) = peer, requirePhoneNumbers {
|
||||||
|
let phone = user.phone ?? ""
|
||||||
|
if phone.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
existingPeerIds.insert(peer.id)
|
existingPeerIds.insert(peer.id)
|
||||||
var enabled = true
|
var enabled = true
|
||||||
if onlyWriteable {
|
if onlyWriteable {
|
||||||
@ -354,6 +365,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
if !(peer.peer is TelegramUser) {
|
if !(peer.peer is TelegramUser) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
|
||||||
|
let phone = user.phone ?? ""
|
||||||
|
if phone.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !existingPeerIds.contains(peer.peer.id) {
|
if !existingPeerIds.contains(peer.peer.id) {
|
||||||
existingPeerIds.insert(peer.peer.id)
|
existingPeerIds.insert(peer.peer.id)
|
||||||
|
|
||||||
@ -373,6 +392,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
if !(peer.peer is TelegramUser) {
|
if !(peer.peer is TelegramUser) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
|
||||||
|
let phone = user.phone ?? ""
|
||||||
|
if phone.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !existingPeerIds.contains(peer.peer.id) {
|
if !existingPeerIds.contains(peer.peer.id) {
|
||||||
existingPeerIds.insert(peer.peer.id)
|
existingPeerIds.insert(peer.peer.id)
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
case timeHeader(PresentationTheme, String)
|
case timeHeader(PresentationTheme, String)
|
||||||
case timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool)
|
case timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool)
|
||||||
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?)
|
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?, Int32?, Int32?)
|
||||||
case timeInfo(PresentationTheme, String)
|
case timeInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case durationHeader(PresentationTheme, String)
|
case durationHeader(PresentationTheme, String)
|
||||||
@ -282,8 +282,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .timeCustomPicker(lhsTheme, lhsDateTimeFormat, lhsDate):
|
case let .timeCustomPicker(lhsTheme, lhsDateTimeFormat, lhsDate, lhsMinDate, lhsMaxDate):
|
||||||
if case let .timeCustomPicker(rhsTheme, rhsDateTimeFormat, rhsDate) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsDate == rhsDate {
|
if case let .timeCustomPicker(rhsTheme, rhsDateTimeFormat, rhsDate, rhsMinDate, rhsMaxDate) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsDate == rhsDate, lhsMinDate == rhsMinDate, lhsMaxDate == rhsMaxDate {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -450,8 +450,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .timeCustomPicker(_, dateTimeFormat, date):
|
case let .timeCustomPicker(_, dateTimeFormat, date, minDate, maxDate):
|
||||||
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, sectionId: self.section, style: .blocks, updated: { date in
|
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, minDate: minDate, maxDate: maxDate, sectionId: self.section, style: .blocks, updated: { date in
|
||||||
arguments.updateState({ state in
|
arguments.updateState({ state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
updatedState.time = date
|
updatedState.time = date
|
||||||
@ -499,7 +499,18 @@ private struct PremiumGiftProduct: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createGiveawayControllerEntries(peerId: EnginePeer.Id, subject: CreateGiveawaySubject, state: CreateGiveawayControllerState, presentationData: PresentationData, locale: Locale, peers: [EnginePeer.Id: EnginePeer], products: [PremiumGiftProduct], defaultPrice: (Int64, NSDecimalNumber)) -> [CreateGiveawayEntry] {
|
private func createGiveawayControllerEntries(
|
||||||
|
peerId: EnginePeer.Id,
|
||||||
|
subject: CreateGiveawaySubject,
|
||||||
|
state: CreateGiveawayControllerState,
|
||||||
|
presentationData: PresentationData,
|
||||||
|
locale: Locale,
|
||||||
|
peers: [EnginePeer.Id: EnginePeer],
|
||||||
|
products: [PremiumGiftProduct],
|
||||||
|
defaultPrice: (Int64, NSDecimalNumber),
|
||||||
|
minDate: Int32,
|
||||||
|
maxDate: Int32
|
||||||
|
) -> [CreateGiveawayEntry] {
|
||||||
var entries: [CreateGiveawayEntry] = []
|
var entries: [CreateGiveawayEntry] = []
|
||||||
|
|
||||||
switch subject {
|
switch subject {
|
||||||
@ -568,7 +579,7 @@ private func createGiveawayControllerEntries(peerId: EnginePeer.Id, subject: Cre
|
|||||||
entries.append(.timeHeader(presentationData.theme, "DATE WHEN GIVEAWAY ENDS".uppercased()))
|
entries.append(.timeHeader(presentationData.theme, "DATE WHEN GIVEAWAY ENDS".uppercased()))
|
||||||
entries.append(.timeExpiryDate(presentationData.theme, presentationData.dateTimeFormat, state.time, state.pickingTimeLimit))
|
entries.append(.timeExpiryDate(presentationData.theme, presentationData.dateTimeFormat, state.time, state.pickingTimeLimit))
|
||||||
if state.pickingTimeLimit {
|
if state.pickingTimeLimit {
|
||||||
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time))
|
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate))
|
||||||
}
|
}
|
||||||
entries.append(.timeInfo(presentationData.theme, "Choose when \(state.subscriptions) subscribers of your channel will be randomly selected to receive Telegram Premium."))
|
entries.append(.timeInfo(presentationData.theme, "Choose when \(state.subscriptions) subscribers of your channel will be randomly selected to receive Telegram Premium."))
|
||||||
}
|
}
|
||||||
@ -657,7 +668,11 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
initialSubscriptions = 5
|
initialSubscriptions = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
let expiryTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + 86400 * 5
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
let expiryTime = currentTime + 86400 * 3
|
||||||
|
let minDate = currentTime + 60 * 10
|
||||||
|
let maxDate = currentTime + context.userLimits.maxGiveawayPeriodSeconds
|
||||||
|
|
||||||
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, channels: [], peers: [], countries: [], onlyNewEligible: false, time: expiryTime)
|
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, channels: [], peers: [], countries: [], onlyNewEligible: false, time: expiryTime)
|
||||||
|
|
||||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||||
@ -801,7 +816,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(""), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(""), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createGiveawayControllerEntries(peerId: peerId, subject: subject, state: state, presentationData: presentationData, locale: locale, peers: peers, products: products, defaultPrice: defaultPrice), style: .blocks, emptyStateItem: nil, headerItem: headerItem, footerItem: footerItem, crossfadeState: false, animateChanges: animateChanges)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createGiveawayControllerEntries(peerId: peerId, subject: subject, state: state, presentationData: presentationData, locale: locale, peers: peers, products: products, defaultPrice: defaultPrice, minDate: minDate, maxDate: maxDate), style: .blocks, emptyStateItem: nil, headerItem: headerItem, footerItem: footerItem, crossfadeState: false, animateChanges: animateChanges)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let giftCode: PremiumGiftCodeInfo
|
let giftCode: PremiumGiftCodeInfo
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let cancel: () -> Void
|
let cancel: (Bool) -> Void
|
||||||
let openPeer: (EnginePeer) -> Void
|
let openPeer: (EnginePeer) -> Void
|
||||||
let openMessage: (EngineMessage.Id) -> Void
|
let openMessage: (EngineMessage.Id) -> Void
|
||||||
let copyLink: (String) -> Void
|
let copyLink: (String) -> Void
|
||||||
@ -37,7 +37,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
giftCode: PremiumGiftCodeInfo,
|
giftCode: PremiumGiftCodeInfo,
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
cancel: @escaping () -> Void,
|
cancel: @escaping (Bool) -> Void,
|
||||||
openPeer: @escaping (EnginePeer) -> Void,
|
openPeer: @escaping (EnginePeer) -> Void,
|
||||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||||
copyLink: @escaping (String) -> Void,
|
copyLink: @escaping (String) -> Void,
|
||||||
@ -149,7 +149,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
component: Button(
|
component: Button(
|
||||||
content: AnyComponent(Image(image: closeImage)),
|
content: AnyComponent(Image(image: closeImage)),
|
||||||
action: { [weak component] in
|
action: { [weak component] in
|
||||||
component?.cancel()
|
component?.cancel(true)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: 30.0, height: 30.0),
|
availableSize: CGSize(width: 30.0, height: 30.0),
|
||||||
@ -240,8 +240,10 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: fromPeer)),
|
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: fromPeer)),
|
||||||
action: {
|
action: {
|
||||||
if let peer = fromPeer {
|
if let peer = fromPeer {
|
||||||
component.cancel()
|
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -257,8 +259,10 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
||||||
action: {
|
action: {
|
||||||
if let peer = toPeer {
|
if let peer = toPeer {
|
||||||
component.cancel()
|
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -301,10 +305,12 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: giftReason, font: tableFont, textColor: giftCode.messageId != nil ? tableLinkColor : tableTextColor)))),
|
content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: giftReason, font: tableFont, textColor: giftCode.messageId != nil ? tableLinkColor : tableTextColor)))),
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
action: {
|
action: {
|
||||||
component.cancel()
|
|
||||||
if let messageId = giftCode.messageId {
|
if let messageId = giftCode.messageId {
|
||||||
component.openMessage(messageId)
|
component.openMessage(messageId)
|
||||||
}
|
}
|
||||||
|
Queue.mainQueue().after(1.0) {
|
||||||
|
component.cancel(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -362,7 +368,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
iconPosition: .left,
|
iconPosition: .left,
|
||||||
action: {
|
action: {
|
||||||
if giftCode.isUsed {
|
if giftCode.isUsed {
|
||||||
component.cancel()
|
component.cancel(true)
|
||||||
} else {
|
} else {
|
||||||
component.action()
|
component.action()
|
||||||
}
|
}
|
||||||
@ -425,7 +431,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let giftCode: PremiumGiftCodeInfo
|
let giftCode: PremiumGiftCodeInfo
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let cancel: () -> Void
|
|
||||||
let openPeer: (EnginePeer) -> Void
|
let openPeer: (EnginePeer) -> Void
|
||||||
let openMessage: (EngineMessage.Id) -> Void
|
let openMessage: (EngineMessage.Id) -> Void
|
||||||
let copyLink: (String) -> Void
|
let copyLink: (String) -> Void
|
||||||
@ -435,7 +440,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
giftCode: PremiumGiftCodeInfo,
|
giftCode: PremiumGiftCodeInfo,
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
cancel: @escaping () -> Void,
|
|
||||||
openPeer: @escaping (EnginePeer) -> Void,
|
openPeer: @escaping (EnginePeer) -> Void,
|
||||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||||
copyLink: @escaping (String) -> Void,
|
copyLink: @escaping (String) -> Void,
|
||||||
@ -444,7 +448,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.giftCode = giftCode
|
self.giftCode = giftCode
|
||||||
self.action = action
|
self.action = action
|
||||||
self.cancel = cancel
|
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.openMessage = openMessage
|
self.openMessage = openMessage
|
||||||
self.copyLink = copyLink
|
self.copyLink = copyLink
|
||||||
@ -475,7 +478,7 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
context: context.component.context,
|
context: context.component.context,
|
||||||
giftCode: context.component.giftCode,
|
giftCode: context.component.giftCode,
|
||||||
action: context.component.action,
|
action: context.component.action,
|
||||||
cancel: {
|
cancel: { animate in
|
||||||
animateOut.invoke(Action { _ in
|
animateOut.invoke(Action { _ in
|
||||||
if let controller = controller() {
|
if let controller = controller() {
|
||||||
controller.dismiss(completion: nil)
|
controller.dismiss(completion: nil)
|
||||||
@ -535,7 +538,6 @@ public class PremiumGiftCodeScreen: ViewControllerComponentContainer {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
giftCode: PremiumGiftCodeInfo,
|
giftCode: PremiumGiftCodeInfo,
|
||||||
forceDark: Bool = false,
|
forceDark: Bool = false,
|
||||||
cancel: @escaping () -> Void = {},
|
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
openPeer: @escaping (EnginePeer) -> Void = { _ in },
|
openPeer: @escaping (EnginePeer) -> Void = { _ in },
|
||||||
openMessage: @escaping (EngineMessage.Id) -> Void = { _ in },
|
openMessage: @escaping (EngineMessage.Id) -> Void = { _ in },
|
||||||
@ -544,7 +546,7 @@ public class PremiumGiftCodeScreen: ViewControllerComponentContainer {
|
|||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
var copyLinkImpl: ((String) -> Void)?
|
var copyLinkImpl: ((String) -> Void)?
|
||||||
super.init(context: context, component: PremiumGiftCodeSheetComponent(context: context, giftCode: giftCode, action: action, cancel: cancel, openPeer: openPeer, openMessage: openMessage, copyLink: { link in
|
super.init(context: context, component: PremiumGiftCodeSheetComponent(context: context, giftCode: giftCode, action: action, openPeer: openPeer, openMessage: openMessage, copyLink: { link in
|
||||||
copyLinkImpl?(link)
|
copyLinkImpl?(link)
|
||||||
}, shareLink: shareLink), navigationBarAppearance: .none, statusBarStyle: .ignore, theme: forceDark ? .dark : .default)
|
}, shareLink: shareLink), navigationBarAppearance: .none, statusBarStyle: .ignore, theme: forceDark ? .dark : .default)
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public final class ChannelBoostStatus: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _internal_getChannelBoostStatus(account: Account, peerId: PeerId) -> Signal<ChannelBoostStatus?, NoError> {
|
func _internal_getChannelBoostStatus(account: Account, peerId: PeerId) -> Signal<ChannelBoostStatus?, NoError> {
|
||||||
return .single(nil)
|
return .single(ChannelBoostStatus(level: 0, boosts: 0, currentLevelBoosts: 0, nextLevelBoosts: nil, premiumAudience: nil, url: "", prepaidGiveaways: []))
|
||||||
// return account.postbox.transaction { transaction -> Api.InputPeer? in
|
// return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
// return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
// return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
// }
|
// }
|
||||||
|
@ -24,6 +24,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
public let maxStoriesSuggestedReactions: Int32
|
public let maxStoriesSuggestedReactions: Int32
|
||||||
public let maxGiveawayChannelsCount: Int32
|
public let maxGiveawayChannelsCount: Int32
|
||||||
public let maxGiveawayCountriesCount: Int32
|
public let maxGiveawayCountriesCount: Int32
|
||||||
|
public let maxGiveawayPeriodSeconds: Int32
|
||||||
public let minChannelNameColorLevel: Int32
|
public let minChannelNameColorLevel: Int32
|
||||||
|
|
||||||
public static var defaultValue: UserLimitsConfiguration {
|
public static var defaultValue: UserLimitsConfiguration {
|
||||||
@ -50,6 +51,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
maxStoriesSuggestedReactions: 1,
|
maxStoriesSuggestedReactions: 1,
|
||||||
maxGiveawayChannelsCount: 10,
|
maxGiveawayChannelsCount: 10,
|
||||||
maxGiveawayCountriesCount: 10,
|
maxGiveawayCountriesCount: 10,
|
||||||
|
maxGiveawayPeriodSeconds: 86400 * 7,
|
||||||
minChannelNameColorLevel: 10
|
minChannelNameColorLevel: 10
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -77,6 +79,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
maxStoriesSuggestedReactions: Int32,
|
maxStoriesSuggestedReactions: Int32,
|
||||||
maxGiveawayChannelsCount: Int32,
|
maxGiveawayChannelsCount: Int32,
|
||||||
maxGiveawayCountriesCount: Int32,
|
maxGiveawayCountriesCount: Int32,
|
||||||
|
maxGiveawayPeriodSeconds: Int32,
|
||||||
minChannelNameColorLevel: Int32
|
minChannelNameColorLevel: Int32
|
||||||
) {
|
) {
|
||||||
self.maxPinnedChatCount = maxPinnedChatCount
|
self.maxPinnedChatCount = maxPinnedChatCount
|
||||||
@ -101,6 +104,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
||||||
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
||||||
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
||||||
|
self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds
|
||||||
self.minChannelNameColorLevel = minChannelNameColorLevel
|
self.minChannelNameColorLevel = minChannelNameColorLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +152,7 @@ extension UserLimitsConfiguration {
|
|||||||
self.maxStoriesSuggestedReactions = getValue("stories_suggested_reactions_limit", orElse: defaultValue.maxStoriesMonthlyCount)
|
self.maxStoriesSuggestedReactions = getValue("stories_suggested_reactions_limit", orElse: defaultValue.maxStoriesMonthlyCount)
|
||||||
self.maxGiveawayChannelsCount = getGeneralValue("giveaway_add_peers_max", orElse: defaultValue.maxGiveawayChannelsCount)
|
self.maxGiveawayChannelsCount = getGeneralValue("giveaway_add_peers_max", orElse: defaultValue.maxGiveawayChannelsCount)
|
||||||
self.maxGiveawayCountriesCount = getGeneralValue("giveaway_countries_max", orElse: defaultValue.maxGiveawayCountriesCount)
|
self.maxGiveawayCountriesCount = getGeneralValue("giveaway_countries_max", orElse: defaultValue.maxGiveawayCountriesCount)
|
||||||
|
self.maxGiveawayPeriodSeconds = getGeneralValue("giveaway_period_max", orElse: defaultValue.maxGiveawayPeriodSeconds)
|
||||||
self.minChannelNameColorLevel = getGeneralValue("channel_color_level_min", orElse: defaultValue.minChannelNameColorLevel)
|
self.minChannelNameColorLevel = getGeneralValue("channel_color_level_min", orElse: defaultValue.minChannelNameColorLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ public enum EngineConfiguration {
|
|||||||
public let maxStoriesSuggestedReactions: Int32
|
public let maxStoriesSuggestedReactions: Int32
|
||||||
public let maxGiveawayChannelsCount: Int32
|
public let maxGiveawayChannelsCount: Int32
|
||||||
public let maxGiveawayCountriesCount: Int32
|
public let maxGiveawayCountriesCount: Int32
|
||||||
|
public let maxGiveawayPeriodSeconds: Int32
|
||||||
public let minChannelNameColorLevel: Int32
|
public let minChannelNameColorLevel: Int32
|
||||||
|
|
||||||
public static var defaultValue: UserLimits {
|
public static var defaultValue: UserLimits {
|
||||||
@ -87,6 +88,7 @@ public enum EngineConfiguration {
|
|||||||
maxStoriesSuggestedReactions: Int32,
|
maxStoriesSuggestedReactions: Int32,
|
||||||
maxGiveawayChannelsCount: Int32,
|
maxGiveawayChannelsCount: Int32,
|
||||||
maxGiveawayCountriesCount: Int32,
|
maxGiveawayCountriesCount: Int32,
|
||||||
|
maxGiveawayPeriodSeconds: Int32,
|
||||||
minChannelNameColorLevel: Int32
|
minChannelNameColorLevel: Int32
|
||||||
) {
|
) {
|
||||||
self.maxPinnedChatCount = maxPinnedChatCount
|
self.maxPinnedChatCount = maxPinnedChatCount
|
||||||
@ -111,6 +113,7 @@ public enum EngineConfiguration {
|
|||||||
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
||||||
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
||||||
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
||||||
|
self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds
|
||||||
self.minChannelNameColorLevel = minChannelNameColorLevel
|
self.minChannelNameColorLevel = minChannelNameColorLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,6 +174,7 @@ public extension EngineConfiguration.UserLimits {
|
|||||||
maxStoriesSuggestedReactions: userLimitsConfiguration.maxStoriesSuggestedReactions,
|
maxStoriesSuggestedReactions: userLimitsConfiguration.maxStoriesSuggestedReactions,
|
||||||
maxGiveawayChannelsCount: userLimitsConfiguration.maxGiveawayChannelsCount,
|
maxGiveawayChannelsCount: userLimitsConfiguration.maxGiveawayChannelsCount,
|
||||||
maxGiveawayCountriesCount: userLimitsConfiguration.maxGiveawayCountriesCount,
|
maxGiveawayCountriesCount: userLimitsConfiguration.maxGiveawayCountriesCount,
|
||||||
|
maxGiveawayPeriodSeconds: userLimitsConfiguration.maxGiveawayPeriodSeconds,
|
||||||
minChannelNameColorLevel: userLimitsConfiguration.minChannelNameColorLevel
|
minChannelNameColorLevel: userLimitsConfiguration.minChannelNameColorLevel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ public func donateSendMessageIntent(account: Account, sharedContext: SharedAccou
|
|||||||
if let image = generateImage(size, rotatedContext: { size, context in
|
if let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), font: avatarFont, letters: peer.displayLetters, peerId: peer.id)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), font: avatarFont, letters: peer.displayLetters, peerId: peer.id, nameColor: peer.nameColor)
|
||||||
})?.withRenderingMode(.alwaysOriginal) {
|
})?.withRenderingMode(.alwaysOriginal) {
|
||||||
avatarImage = image
|
avatarImage = image
|
||||||
}
|
}
|
||||||
|
@ -771,6 +771,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
context.add(frontFlash
|
context.add(frontFlash
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||||
|
.scale(1.5 - component.cameraState.flashTintSize * 0.5)
|
||||||
.appear(.default(alpha: true))
|
.appear(.default(alpha: true))
|
||||||
.disappear(.default(alpha: true))
|
.disappear(.default(alpha: true))
|
||||||
)
|
)
|
||||||
|
@ -1708,12 +1708,12 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum PeerAvatarReference: Equatable {
|
private enum PeerAvatarReference: Equatable {
|
||||||
case letters(PeerId, [String])
|
case letters(PeerId, PeerNameColor?, [String])
|
||||||
case image(PeerReference, TelegramMediaImageRepresentation)
|
case image(PeerReference, TelegramMediaImageRepresentation)
|
||||||
|
|
||||||
var peerId: PeerId {
|
var peerId: PeerId {
|
||||||
switch self {
|
switch self {
|
||||||
case let .letters(value, _):
|
case let .letters(value, _, _):
|
||||||
return value
|
return value
|
||||||
case let .image(value, _):
|
case let .image(value, _):
|
||||||
return value.id
|
return value.id
|
||||||
@ -1726,7 +1726,7 @@ private extension PeerAvatarReference {
|
|||||||
if let photo = peer.smallProfileImage, let peerReference = PeerReference(peer) {
|
if let photo = peer.smallProfileImage, let peerReference = PeerReference(peer) {
|
||||||
self = .image(peerReference, photo)
|
self = .image(peerReference, photo)
|
||||||
} else {
|
} else {
|
||||||
self = .letters(peer.id, peer.displayLetters)
|
self = .letters(peer.id, peer.nameColor, peer.displayLetters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1885,9 +1885,9 @@ public final class MergedAvatarsNode: ASDisplayNode {
|
|||||||
|
|
||||||
context.saveGState()
|
context.saveGState()
|
||||||
switch parameters.peers[i] {
|
switch parameters.peers[i] {
|
||||||
case let .letters(peerId, letters):
|
case let .letters(peerId, nameColor, letters):
|
||||||
context.translateBy(x: currentX, y: 0.0)
|
context.translateBy(x: currentX, y: 0.0)
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: mergedImageSize, height: mergedImageSize), font: avatarFont, letters: letters, peerId: peerId)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: mergedImageSize, height: mergedImageSize), font: avatarFont, letters: letters, peerId: peerId, nameColor: nameColor)
|
||||||
context.translateBy(x: -currentX, y: 0.0)
|
context.translateBy(x: -currentX, y: 0.0)
|
||||||
case .image:
|
case .image:
|
||||||
if let image = parameters.images[parameters.peers[i].peerId] {
|
if let image = parameters.images[parameters.peers[i].peerId] {
|
||||||
|
@ -11,6 +11,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
|||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
let dateTimeFormat: PresentationDateTimeFormat
|
let dateTimeFormat: PresentationDateTimeFormat
|
||||||
let date: Int32?
|
let date: Int32?
|
||||||
|
let minDate: Int32?
|
||||||
|
let maxDate: Int32?
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let style: ItemListStyle
|
let style: ItemListStyle
|
||||||
let updated: ((Int32) -> Void)?
|
let updated: ((Int32) -> Void)?
|
||||||
@ -20,6 +22,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
|||||||
presentationData: ItemListPresentationData,
|
presentationData: ItemListPresentationData,
|
||||||
dateTimeFormat: PresentationDateTimeFormat,
|
dateTimeFormat: PresentationDateTimeFormat,
|
||||||
date: Int32?,
|
date: Int32?,
|
||||||
|
minDate: Int32? = nil,
|
||||||
|
maxDate: Int32? = nil,
|
||||||
sectionId: ItemListSectionId,
|
sectionId: ItemListSectionId,
|
||||||
style: ItemListStyle,
|
style: ItemListStyle,
|
||||||
updated: ((Int32) -> Void)?,
|
updated: ((Int32) -> Void)?,
|
||||||
@ -28,6 +32,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
|||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.date = date
|
self.date = date
|
||||||
|
self.minDate = minDate
|
||||||
|
self.maxDate = maxDate
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.style = style
|
self.style = style
|
||||||
self.updated = updated
|
self.updated = updated
|
||||||
@ -223,7 +229,15 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.item?.updated?(Int32(date.timeIntervalSince1970))
|
strongSelf.item?.updated?(Int32(date.timeIntervalSince1970))
|
||||||
}
|
}
|
||||||
|
|
||||||
datePickerNode.minimumDate = Date()
|
if let minDate = item.minDate {
|
||||||
|
datePickerNode.minimumDate = Date(timeIntervalSince1970: TimeInterval(minDate))
|
||||||
|
} else {
|
||||||
|
datePickerNode.minimumDate = Date()
|
||||||
|
}
|
||||||
|
if let maxDate = item.maxDate {
|
||||||
|
datePickerNode.maximumDate = Date(timeIntervalSince1970: TimeInterval(maxDate))
|
||||||
|
}
|
||||||
|
|
||||||
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) }
|
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) }
|
||||||
|
|
||||||
let datePickerSize = CGSize(width: width, height: contentSize.height)
|
let datePickerSize = CGSize(width: width, height: contentSize.height)
|
||||||
|
@ -164,6 +164,8 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
var currentBackgroundNode = self.backgroundNode
|
var currentBackgroundNode = self.backgroundNode
|
||||||
|
|
||||||
|
let currentItem = self.item
|
||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
@ -214,7 +216,9 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
itemNode.frame = nodeFrame
|
itemNode.frame = nodeFrame
|
||||||
itemNode.isUserInteractionEnabled = false
|
itemNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
apply(ListViewItemApply(isOnScreen: true))
|
Queue.mainQueue().after(0.01) {
|
||||||
|
apply(ListViewItemApply(isOnScreen: true))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -249,6 +253,15 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||||
|
|
||||||
|
if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor {
|
||||||
|
if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
strongSelf.view.addSubview(snapshot)
|
||||||
|
snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||||
|
snapshot.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.messageNodes = nodes
|
strongSelf.messageNodes = nodes
|
||||||
var topOffset: CGFloat = 4.0
|
var topOffset: CGFloat = 4.0
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
|
@ -184,8 +184,10 @@ private final class PeerNameColorIconItemNode : ListViewItemNode {
|
|||||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate
|
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate
|
||||||
if selected {
|
if selected {
|
||||||
transition.updateTransformScale(node: self.fillNode, scale: 0.8)
|
transition.updateTransformScale(node: self.fillNode, scale: 0.8)
|
||||||
|
transition.updateTransformScale(node: self.ringNode, scale: 1.0)
|
||||||
} else {
|
} else {
|
||||||
transition.updateTransformScale(node: self.fillNode, scale: 1.0)
|
transition.updateTransformScale(node: self.fillNode, scale: 1.0)
|
||||||
|
transition.updateTransformScale(node: self.ringNode, scale: 0.99)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,6 +336,7 @@ public func PeerNameColorScreen(
|
|||||||
}
|
}
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
} else {
|
} else {
|
||||||
|
HapticFeedback().error()
|
||||||
let controller = UndoOverlayController(
|
let controller = UndoOverlayController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
content: .premiumPaywall(
|
content: .premiumPaywall(
|
||||||
|
@ -10958,7 +10958,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
|
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.presentAttachmentPremiumGift()
|
strongSelf.presentAttachmentMenu(subject: .gift)
|
||||||
Queue.mainQueue().after(0.5) {
|
Queue.mainQueue().after(0.5) {
|
||||||
let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId).startStandalone()
|
let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId).startStandalone()
|
||||||
}
|
}
|
||||||
@ -13389,10 +13389,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentAttachmentPremiumGift() {
|
|
||||||
self.presentAttachmentMenu(subject: .gift)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AttachMenuSubject {
|
enum AttachMenuSubject {
|
||||||
case `default`
|
case `default`
|
||||||
case edit(mediaOptions: MessageMediaEditingOptions, mediaReference: AnyMediaReference)
|
case edit(mediaOptions: MessageMediaEditingOptions, mediaReference: AnyMediaReference)
|
||||||
@ -13720,7 +13716,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let _ = currentLocationController.swap(controller)
|
let _ = currentLocationController.swap(controller)
|
||||||
})
|
})
|
||||||
case .contact:
|
case .contact:
|
||||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true))
|
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true, requirePhoneNumbers: true))
|
||||||
contactsController.presentScheduleTimePicker = { [weak self] completion in
|
contactsController.presentScheduleTimePicker = { [weak self] completion in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.presentScheduleTimePicker(completion: completion)
|
strongSelf.presentScheduleTimePicker(completion: completion)
|
||||||
|
@ -1177,17 +1177,19 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for media in message.media {
|
if !isCopyProtected {
|
||||||
if let file = media as? TelegramMediaFile {
|
for media in message.media {
|
||||||
if file.isMusic {
|
if let file = media as? TelegramMediaFile {
|
||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveToFiles, icon: { theme in
|
if file.isMusic {
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveToFiles, icon: { theme in
|
||||||
}, action: { _, f in
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
||||||
controllerInteraction.saveMediaToFiles(message.id)
|
}, action: { _, f in
|
||||||
f(.default)
|
controllerInteraction.saveMediaToFiles(message.id)
|
||||||
})))
|
f(.default)
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
private let displayDeviceContacts: Bool
|
private let displayDeviceContacts: Bool
|
||||||
private let displayCallIcons: Bool
|
private let displayCallIcons: Bool
|
||||||
private let multipleSelection: Bool
|
private let multipleSelection: Bool
|
||||||
|
private let requirePhoneNumbers: Bool
|
||||||
|
|
||||||
private var _ready = Promise<Bool>()
|
private var _ready = Promise<Bool>()
|
||||||
override var ready: Promise<Bool> {
|
override var ready: Promise<Bool> {
|
||||||
@ -91,6 +92,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
self.displayCallIcons = params.displayCallIcons
|
self.displayCallIcons = params.displayCallIcons
|
||||||
self.confirmation = params.confirmation
|
self.confirmation = params.confirmation
|
||||||
self.multipleSelection = params.multipleSelection
|
self.multipleSelection = params.multipleSelection
|
||||||
|
self.requirePhoneNumbers = params.requirePhoneNumbers
|
||||||
|
|
||||||
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
@ -178,7 +180,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func loadDisplayNode() {
|
override func loadDisplayNode() {
|
||||||
self.displayNode = ContactSelectionControllerNode(context: self.context, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection)
|
self.displayNode = ContactSelectionControllerNode(context: self.context, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection, requirePhoneNumbers: self.requirePhoneNumbers)
|
||||||
self._ready.set(self.contactsNode.contactListNode.ready)
|
self._ready.set(self.contactsNode.contactListNode.ready)
|
||||||
|
|
||||||
self.contactsNode.navigationBar = self.navigationBar
|
self.contactsNode.navigationBar = self.navigationBar
|
||||||
|
@ -23,6 +23,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let displayDeviceContacts: Bool
|
private let displayDeviceContacts: Bool
|
||||||
private let displayCallIcons: Bool
|
private let displayCallIcons: Bool
|
||||||
|
private let filters: [ContactListFilter]
|
||||||
|
|
||||||
let contactListNode: ContactListNode
|
let contactListNode: ContactListNode
|
||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
@ -53,14 +54,20 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
var searchContainerNode: ContactsSearchContainerNode?
|
var searchContainerNode: ContactsSearchContainerNode?
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, options: [ContactListAdditionalOption], displayDeviceContacts: Bool, displayCallIcons: Bool, multipleSelection: Bool) {
|
init(context: AccountContext, presentationData: PresentationData, options: [ContactListAdditionalOption], displayDeviceContacts: Bool, displayCallIcons: Bool, multipleSelection: Bool, requirePhoneNumbers: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.displayDeviceContacts = displayDeviceContacts
|
self.displayDeviceContacts = displayDeviceContacts
|
||||||
self.displayCallIcons = displayCallIcons
|
self.displayCallIcons = displayCallIcons
|
||||||
|
|
||||||
|
var filters: [ContactListFilter] = [.excludeSelf]
|
||||||
|
if requirePhoneNumbers {
|
||||||
|
filters.append(.excludeWithoutPhoneNumbers)
|
||||||
|
}
|
||||||
|
self.filters = filters
|
||||||
|
|
||||||
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||||
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in
|
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), filters: filters, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in
|
||||||
contextActionImpl?(peer, node, gesture, nil)
|
contextActionImpl?(peer, node, gesture, nil)
|
||||||
} : nil, multipleSelection: multipleSelection)
|
} : nil, multipleSelection: multipleSelection)
|
||||||
|
|
||||||
@ -186,7 +193,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
categories.insert(.global)
|
categories.insert(.global)
|
||||||
}
|
}
|
||||||
|
|
||||||
let searchContainerNode = ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, addContact: nil, openPeer: { [weak self] peer in
|
let searchContainerNode = ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, addContact: nil, openPeer: { [weak self] peer in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
var updated = false
|
var updated = false
|
||||||
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in
|
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in
|
||||||
|
@ -10304,7 +10304,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
context.translateBy(x: inset, y: inset)
|
context.translateBy(x: inset, y: inset)
|
||||||
|
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id, nameColor: primary.1.nameColor)
|
||||||
})?.withRenderingMode(.alwaysOriginal)
|
})?.withRenderingMode(.alwaysOriginal)
|
||||||
if let image = image {
|
if let image = image {
|
||||||
subscriber.putNext((image, image))
|
subscriber.putNext((image, image))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user