Autoremove improvements

This commit is contained in:
Ali 2022-11-27 15:27:05 +04:00
parent dc4a04daa7
commit 60ebcc847a
11 changed files with 327 additions and 189 deletions

View File

@ -835,6 +835,7 @@ public final class AvatarBadgeView: UIImageView {
private var originalContent: OriginalContent? private var originalContent: OriginalContent?
private var parameters: Parameters? private var parameters: Parameters?
private var hasContent: Bool = false
override public init(frame: CGRect) { override public init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
@ -845,7 +846,7 @@ public final class AvatarBadgeView: UIImageView {
} }
func update(content: OriginalContent) { func update(content: OriginalContent) {
if self.originalContent != content { if self.originalContent != content || !self.hasContent {
self.originalContent = content self.originalContent = content
self.update() self.update()
} }
@ -853,7 +854,7 @@ public final class AvatarBadgeView: UIImageView {
public func update(size: CGSize, text: String) { public func update(size: CGSize, text: String) {
let parameters = Parameters(size: size, text: text) let parameters = Parameters(size: size, text: text)
if self.parameters != parameters { if self.parameters != parameters || !self.hasContent {
self.parameters = parameters self.parameters = parameters
self.update() self.update()
} }
@ -864,6 +865,8 @@ public final class AvatarBadgeView: UIImageView {
return return
} }
self.hasContent = true
let blurredWidth = 16 let blurredWidth = 16
let blurredHeight = 16 let blurredHeight = 16
guard let blurredContext = DrawingContext(size: CGSize(width: CGFloat(blurredWidth), height: CGFloat(blurredHeight)), scale: 1.0, opaque: true) else { guard let blurredContext = DrawingContext(size: CGSize(width: CGFloat(blurredWidth), height: CGFloat(blurredHeight)), scale: 1.0, opaque: true) else {
@ -876,8 +879,11 @@ public final class AvatarBadgeView: UIImageView {
c.setFillColor(color.cgColor) c.setFillColor(color.cgColor)
c.fill(CGRect(origin: CGPoint(), size: blurredSize)) c.fill(CGRect(origin: CGPoint(), size: blurredSize))
case let .image(image): case let .image(image):
c.setFillColor(UIColor.black.cgColor)
c.fill(CGRect(origin: CGPoint(), size: blurredSize))
c.scaleBy(x: blurredSize.width / parameters.size.width, y: blurredSize.height / parameters.size.height) c.scaleBy(x: blurredSize.width / parameters.size.width, y: blurredSize.height / parameters.size.height)
let offsetFactor: CGFloat = 1.0 - 0.7071 let offsetFactor: CGFloat = 1.0 - 0.6
let imageFrame = CGRect(origin: CGPoint(x: parameters.size.width - image.size.width + offsetFactor * parameters.size.width, y: parameters.size.height - image.size.height + offsetFactor * parameters.size.height), size: image.size) let imageFrame = CGRect(origin: CGPoint(x: parameters.size.width - image.size.width + offsetFactor * parameters.size.width, y: parameters.size.height - image.size.height + offsetFactor * parameters.size.height), size: image.size)
UIGraphicsPushContext(c) UIGraphicsPushContext(c)
@ -900,7 +906,7 @@ public final class AvatarBadgeView: UIImageView {
UInt32(15), UInt32(15),
UInt32(15), UInt32(15),
nil, nil,
vImage_Flags(kvImageEdgeExtend) vImage_Flags(kvImageTruncateKernel | kvImageDoNotTile)
) )
let divisor: Int32 = 0x1000 let divisor: Int32 = 0x1000
@ -909,7 +915,7 @@ public final class AvatarBadgeView: UIImageView {
let gwgt: CGFloat = 0.6094 let gwgt: CGFloat = 0.6094
let bwgt: CGFloat = 0.0820 let bwgt: CGFloat = 0.0820
let adjustSaturation: CGFloat = 1.9 let adjustSaturation: CGFloat = 1.7
let a = (1.0 - adjustSaturation) * rwgt + adjustSaturation let a = (1.0 - adjustSaturation) * rwgt + adjustSaturation
let b = (1.0 - adjustSaturation) * rwgt let b = (1.0 - adjustSaturation) * rwgt
@ -927,8 +933,31 @@ public final class AvatarBadgeView: UIImageView {
g, h, i, 0, g, h, i, 0,
0, 0, 0, 1 0, 0, 0, 1
] ]
let brightness: CGFloat = 0.94
let brighnessMatrix: [CGFloat] = [
brightness, 0, 0, 0,
0, brightness, 0, 0,
0, 0, brightness, 0,
0, 0, 0, 1
]
func matrixMul(a: [CGFloat], b: [CGFloat], result: inout [CGFloat]) {
for i in 0 ..< 4 {
for j in 0 ..< 4 {
var sum: CGFloat = 0.0
for k in 0 ..< 4 {
sum += a[i + k * 4] * b[k + j * 4]
}
result[i + j * 4] = sum
}
}
}
var resultMatrix = Array<CGFloat>(repeating: 0.0, count: 4 * 4)
matrixMul(a: satMatrix, b: brighnessMatrix, result: &resultMatrix)
var matrix: [Int16] = satMatrix.map { value in var matrix: [Int16] = resultMatrix.map { value in
return Int16(value * CGFloat(divisor)) return Int16(value * CGFloat(divisor))
} }
@ -947,14 +976,14 @@ public final class AvatarBadgeView: UIImageView {
context.setFillColor(UIColor.black.cgColor) context.setFillColor(UIColor.black.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.sourceIn)
blurredImage.draw(in: CGRect(origin: CGPoint(), size: size), blendMode: .sourceIn, alpha: 1.0) blurredImage.draw(in: CGRect(origin: CGPoint(), size: size), blendMode: .sourceIn, alpha: 1.0)
context.setBlendMode(.normal) context.setBlendMode(.normal)
context.setFillColor(UIColor(white: 0.0, alpha: 0.05).cgColor) /*context.setFillColor(UIColor(white: 1.0, alpha: 0.08).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor(white: 0.0, alpha: 0.05).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))*/
let string = NSAttributedString(string: parameters.text, font: Font.bold(floor(parameters.size.height * 0.48)), textColor: .white) let string = NSAttributedString(string: parameters.text, font: Font.bold(floor(parameters.size.height * 0.48)), textColor: .white)
let stringBounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) let stringBounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
@ -970,7 +999,6 @@ public final class AvatarBadgeView: UIImageView {
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: CGFloat.pi * 0.5, endAngle: -CGFloat.pi * 0.5, clockwise: false) context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: CGFloat.pi * 0.5, endAngle: -CGFloat.pi * 0.5, clockwise: false)
context.strokePath() context.strokePath()
let sectionAngle: CGFloat = CGFloat.pi / 11.0 let sectionAngle: CGFloat = CGFloat.pi / 11.0
for i in 0 ..< 10 { for i in 0 ..< 10 {
@ -978,15 +1006,13 @@ public final class AvatarBadgeView: UIImageView {
continue continue
} }
let startAngle = CGFloat.pi * 0.5 - CGFloat(i) * sectionAngle - sectionAngle * 0.1 let startAngle = CGFloat.pi * 0.5 - CGFloat(i) * sectionAngle - sectionAngle * 0.15
let endAngle = startAngle - sectionAngle * 0.8 let endAngle = startAngle - sectionAngle * 0.75
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
context.strokePath() context.strokePath()
} }
//context.strokeEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: 2.0 + lineWidth * 0.5, dy: 2.0 + lineWidth * 0.5))
UIGraphicsPopContext() UIGraphicsPopContext()
}) })
} }

View File

@ -2665,19 +2665,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
avatarBadgeBackground.bounds = CGRect(origin: CGPoint(), size: avatarBadgeBackgroundFrame.size) avatarBadgeBackground.bounds = CGRect(origin: CGPoint(), size: avatarBadgeBackgroundFrame.size)
if animateIn { if animateIn {
ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: avatarBadgeNode, scale: 0.001) ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: avatarBadgeNode, scale: 0.00001)
ContainedViewLayoutTransition.immediate.updateTransformScale(layer: avatarBadgeBackground.layer, scale: 0.001) ContainedViewLayoutTransition.immediate.updateTransformScale(layer: avatarBadgeBackground.layer, scale: 0.00001)
} }
transition.updateSublayerTransformScale(node: avatarBadgeNode, scale: max(0.001, inlineNavigationLocation.progress)) transition.updateSublayerTransformScale(node: avatarBadgeNode, scale: max(0.00001, inlineNavigationLocation.progress))
transition.updateTransformScale(layer: avatarBadgeBackground.layer, scale: max(0.001, inlineNavigationLocation.progress)) transition.updateTransformScale(layer: avatarBadgeBackground.layer, scale: max(0.00001, inlineNavigationLocation.progress))
} else if let avatarBadgeNode = strongSelf.avatarBadgeNode { } else if let avatarBadgeNode = strongSelf.avatarBadgeNode {
strongSelf.avatarBadgeNode = nil strongSelf.avatarBadgeNode = nil
transition.updateSublayerTransformScale(node: avatarBadgeNode, scale: 0.001, completion: { [weak avatarBadgeNode] _ in transition.updateSublayerTransformScale(node: avatarBadgeNode, scale: 0.00001, completion: { [weak avatarBadgeNode] _ in
avatarBadgeNode?.removeFromSupernode() avatarBadgeNode?.removeFromSupernode()
}) })
if let avatarBadgeBackground = strongSelf.avatarBadgeBackground { if let avatarBadgeBackground = strongSelf.avatarBadgeBackground {
strongSelf.avatarBadgeBackground = nil strongSelf.avatarBadgeBackground = nil
transition.updateTransformScale(layer: avatarBadgeBackground.layer, scale: 0.001, completion: { [weak avatarBadgeBackground] _ in transition.updateTransformScale(layer: avatarBadgeBackground.layer, scale: 0.00001, completion: { [weak avatarBadgeBackground] _ in
avatarBadgeBackground?.removeFromSupernode() avatarBadgeBackground?.removeFromSupernode()
}) })
} }
@ -2747,7 +2747,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let onlineInlineNavigationFraction: CGFloat = item.interaction.inlineNavigationLocation?.progress ?? 0.0 let onlineInlineNavigationFraction: CGFloat = item.interaction.inlineNavigationLocation?.progress ?? 0.0
transition.updateAlpha(node: strongSelf.onlineNode, alpha: 1.0 - onlineInlineNavigationFraction) transition.updateAlpha(node: strongSelf.onlineNode, alpha: 1.0 - onlineInlineNavigationFraction)
transition.updateSublayerTransformScale(node: strongSelf.onlineNode, scale: (1.0 - onlineInlineNavigationFraction) * 1.0 + onlineInlineNavigationFraction * 0.001) transition.updateSublayerTransformScale(node: strongSelf.onlineNode, scale: (1.0 - onlineInlineNavigationFraction) * 1.0 + onlineInlineNavigationFraction * 0.00001)
let onlineIcon: UIImage? let onlineIcon: UIImage?
if strongSelf.reallyHighlighted { if strongSelf.reallyHighlighted {
@ -2775,7 +2775,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
avatarTimerTransition = .immediate avatarTimerTransition = .immediate
avatarTimerBadge = AvatarBadgeView(frame: CGRect()) avatarTimerBadge = AvatarBadgeView(frame: CGRect())
strongSelf.avatarTimerBadge = avatarTimerBadge strongSelf.avatarTimerBadge = avatarTimerBadge
strongSelf.avatarNode.badgeView = avatarTimerBadge
strongSelf.contextContainer.view.addSubview(avatarTimerBadge) strongSelf.contextContainer.view.addSubview(avatarTimerBadge)
} }
let avatarBadgeSize = CGSize(width: avatarTimerBadgeDiameter, height: avatarTimerBadgeDiameter) let avatarBadgeSize = CGSize(width: avatarTimerBadgeDiameter, height: avatarTimerBadgeDiameter)
@ -2783,7 +2782,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let avatarBadgeFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - avatarBadgeSize.width, y: avatarFrame.maxY - avatarBadgeSize.height), size: avatarBadgeSize) let avatarBadgeFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - avatarBadgeSize.width, y: avatarFrame.maxY - avatarBadgeSize.height), size: avatarBadgeSize)
avatarTimerTransition.updatePosition(layer: avatarTimerBadge.layer, position: avatarBadgeFrame.center) avatarTimerTransition.updatePosition(layer: avatarTimerBadge.layer, position: avatarBadgeFrame.center)
avatarTimerTransition.updateBounds(layer: avatarTimerBadge.layer, bounds: CGRect(origin: CGPoint(), size: avatarBadgeFrame.size)) avatarTimerTransition.updateBounds(layer: avatarTimerBadge.layer, bounds: CGRect(origin: CGPoint(), size: avatarBadgeFrame.size))
avatarTimerTransition.updateTransformScale(layer: avatarTimerBadge.layer, scale: autoremoveTimeoutFraction * 1.0 + (1.0 - autoremoveTimeoutFraction) * 0.001) avatarTimerTransition.updateTransformScale(layer: avatarTimerBadge.layer, scale: autoremoveTimeoutFraction * 1.0 + (1.0 - autoremoveTimeoutFraction) * 0.00001)
strongSelf.avatarNode.badgeView = avatarTimerBadge
} else if let avatarTimerBadge = strongSelf.avatarTimerBadge { } else if let avatarTimerBadge = strongSelf.avatarTimerBadge {
strongSelf.avatarTimerBadge = nil strongSelf.avatarTimerBadge = nil
strongSelf.avatarNode.badgeView = nil strongSelf.avatarNode.badgeView = nil

View File

@ -187,7 +187,8 @@ private func globalAutoremoveScreenEntries(presentationData: PresentationData, s
if effectiveCurrentValue == 0 { if effectiveCurrentValue == 0 {
entries.append(.info("If enabled, all new messages in chats you start will be automatically deleted for everyone at some point after they have been sent. This will not affect your existing chats.")) entries.append(.info("If enabled, all new messages in chats you start will be automatically deleted for everyone at some point after they have been sent. This will not affect your existing chats."))
} else { } else {
entries.append(.info("All new messages in chats you started will be automatically deleted for everyone 3 weeks after they have been sent. You can also [apply this setting for your existing chats]()."))
entries.append(.info("All new messages in chats you started will be automatically deleted for everyone \(timeIntervalString(strings: presentationData.strings, value: effectiveCurrentValue)) after they have been sent. You can also [apply this setting for your existing chats]()."))
} }
return entries return entries

View File

@ -207,3 +207,23 @@ extension SelectivePrivacySettings {
self = current.withEnabledPeers(enableFor).withDisabledPeers(disableFor) self = current.withEnabledPeers(enableFor).withDisabledPeers(disableFor)
} }
} }
public struct GlobalMessageAutoremoveTimeoutSettings: Equatable, Codable {
public static var `default` = GlobalMessageAutoremoveTimeoutSettings(
messageAutoremoveTimeout: nil
)
public var messageAutoremoveTimeout: Int32?
public init(messageAutoremoveTimeout: Int32?) {
self.messageAutoremoveTimeout = messageAutoremoveTimeout
}
}
func updateGlobalMessageAutoremoveTimeoutSettings(transaction: Transaction, _ f: (GlobalMessageAutoremoveTimeoutSettings) -> GlobalMessageAutoremoveTimeoutSettings) {
transaction.updatePreferencesEntry(key: PreferencesKeys.globalMessageAutoremoveTimeoutSettings, { current in
let previous = current?.get(GlobalMessageAutoremoveTimeoutSettings.self) ?? GlobalMessageAutoremoveTimeoutSettings.default
let updated = f(previous)
return PreferencesEntry(updated)
})
}

View File

@ -46,8 +46,31 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
let messageId: Int32? let messageId: Int32?
var apiMessage: Api.Message? var apiMessage: Api.Message?
var correspondingMessageId: Int32?
for update in result.allUpdates {
switch update {
case let .updateMessageID(id, randomId):
for attribute in message.attributes {
if let attribute = attribute as? OutgoingMessageInfoAttribute {
if attribute.uniqueId == randomId {
correspondingMessageId = id
break
}
}
}
default:
break
}
}
for resultMessage in result.messages { for resultMessage in result.messages {
if let id = resultMessage.id() { if let id = resultMessage.id() {
if let correspondingMessageId = correspondingMessageId {
if id.id != correspondingMessageId {
continue
}
}
if id.peerId == message.id.peerId { if id.peerId == message.id.peerId {
apiMessage = resultMessage apiMessage = resultMessage
break break

View File

@ -7,9 +7,11 @@ import MtProtoKit
func managedConfigurationUpdates(accountManager: AccountManager<TelegramAccountManagerTypes>, postbox: Postbox, network: Network) -> Signal<Void, NoError> { func managedConfigurationUpdates(accountManager: AccountManager<TelegramAccountManagerTypes>, postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = Signal<Void, NoError> { subscriber in let poll = Signal<Void, NoError> { subscriber in
return (network.request(Api.functions.help.getConfig()) return (combineLatest(
|> retryRequest network.request(Api.functions.help.getConfig()) |> retryRequest,
|> mapToSignal { result -> Signal<Void, NoError> in network.request(Api.functions.messages.getDefaultHistoryTTL()) |> retryRequest
)
|> mapToSignal { result, defaultHistoryTtl -> Signal<Void, NoError> in
return postbox.transaction { transaction -> Signal<Void, NoError> in return postbox.transaction { transaction -> Signal<Void, NoError> in
switch result { switch result {
case let .config(flags, _, _, _, _, dcOptions, _, chatSizeMax, megagroupSizeMax, forwardedCountMax, _, _, _, _, _, _, _, _, savedGifsLimit, editTimeLimit, revokeTimeLimit, revokePmTimeLimit, _, stickersRecentLimit, stickersFavedLimit, _, _, pinnedDialogsCountMax, pinnedInfolderCountMax, _, _, _, _, _, autoupdateUrlPrefix, gifSearchUsername, venueSearchUsername, imgSearchUsername, _, captionLengthMax, _, webfileDcId, suggestedLangCode, langPackVersion, baseLangPackVersion, defaultReaction): case let .config(flags, _, _, _, _, dcOptions, _, chatSizeMax, megagroupSizeMax, forwardedCountMax, _, _, _, _, _, _, _, _, savedGifsLimit, editTimeLimit, revokeTimeLimit, revokePmTimeLimit, _, stickersRecentLimit, stickersFavedLimit, _, _, pinnedDialogsCountMax, pinnedInfolderCountMax, _, _, _, _, _, autoupdateUrlPrefix, gifSearchUsername, venueSearchUsername, imgSearchUsername, _, captionLengthMax, _, webfileDcId, suggestedLangCode, langPackVersion, baseLangPackVersion, defaultReaction):
@ -71,6 +73,21 @@ func managedConfigurationUpdates(accountManager: AccountManager<TelegramAccountM
}) })
} }
let messageAutoremoveSeconds: Int32?
switch defaultHistoryTtl {
case let .defaultHistoryTTL(period):
if period != 0 {
messageAutoremoveSeconds = period
} else {
messageAutoremoveSeconds = nil
}
}
updateGlobalMessageAutoremoveTimeoutSettings(transaction: transaction, { settings in
var settings = settings
settings.messageAutoremoveTimeout = messageAutoremoveSeconds
return settings
})
return accountManager.transaction { transaction -> Signal<Void, NoError> in return accountManager.transaction { transaction -> Signal<Void, NoError> in
let (primary, secondary) = getLocalization(transaction) let (primary, secondary) = getLocalization(transaction)
var invalidateLocalization = false var invalidateLocalization = false

View File

@ -246,6 +246,7 @@ private enum PreferencesKeyValues: Int32 {
case secretChatSettings = 23 case secretChatSettings = 23
case reactionSettings = 24 case reactionSettings = 24
case premiumPromo = 26 case premiumPromo = 26
case globalMessageAutoremoveTimeoutSettings = 27
} }
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
@ -374,6 +375,12 @@ public struct PreferencesKeys {
key.setInt32(0, value: PreferencesKeyValues.premiumPromo.rawValue) key.setInt32(0, value: PreferencesKeyValues.premiumPromo.rawValue)
return key return key
}() }()
public static let globalMessageAutoremoveTimeoutSettings: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: PreferencesKeyValues.globalMessageAutoremoveTimeoutSettings.rawValue)
return key
}()
} }
private enum SharedDataKeyValues: Int32 { private enum SharedDataKeyValues: Int32 {

View File

@ -379,5 +379,26 @@ public extension TelegramEngine.EngineData.Item {
return premiumPromoConfiguration return premiumPromoConfiguration
} }
} }
public struct GlobalAutoremoveTimeout: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = Int32?
public init() {
}
var key: PostboxViewKey {
return .preferences(keys: Set([PreferencesKeys.globalMessageAutoremoveTimeoutSettings]))
}
func extract(view: PostboxView) -> Result {
guard let view = view as? PreferencesView else {
preconditionFailure()
}
guard let settings = view.values[PreferencesKeys.globalMessageAutoremoveTimeoutSettings]?.get(GlobalMessageAutoremoveTimeoutSettings.self) else {
return GlobalMessageAutoremoveTimeoutSettings.default.messageAutoremoveTimeout
}
return settings.messageAutoremoveTimeout
}
}
} }
} }

View File

@ -155,6 +155,12 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal<Account
return updated return updated
}) })
updateGlobalMessageAutoremoveTimeoutSettings(transaction: transaction, { settings in
var settings = settings
settings.messageAutoremoveTimeout = messageAutoremoveSeconds
return settings
})
return AccountPrivacySettings(presence: SelectivePrivacySettings(apiRules: lastSeenRules, peers: peerMap), groupInvitations: SelectivePrivacySettings(apiRules: groupRules, peers: peerMap), voiceCalls: SelectivePrivacySettings(apiRules: voiceRules, peers: peerMap), voiceCallsP2P: SelectivePrivacySettings(apiRules: voiceP2PRules, peers: peerMap), profilePhoto: SelectivePrivacySettings(apiRules: profilePhotoRules, peers: peerMap), forwards: SelectivePrivacySettings(apiRules: forwardRules, peers: peerMap), phoneNumber: SelectivePrivacySettings(apiRules: phoneNumberRules, peers: peerMap), phoneDiscoveryEnabled: phoneDiscoveryValue, voiceMessages: SelectivePrivacySettings(apiRules: voiceMessagesRules, peers: peerMap), automaticallyArchiveAndMuteNonContacts: automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: accountTimeoutSeconds, messageAutoremoveTimeout: messageAutoremoveSeconds) return AccountPrivacySettings(presence: SelectivePrivacySettings(apiRules: lastSeenRules, peers: peerMap), groupInvitations: SelectivePrivacySettings(apiRules: groupRules, peers: peerMap), voiceCalls: SelectivePrivacySettings(apiRules: voiceRules, peers: peerMap), voiceCallsP2P: SelectivePrivacySettings(apiRules: voiceP2PRules, peers: peerMap), profilePhoto: SelectivePrivacySettings(apiRules: profilePhotoRules, peers: peerMap), forwards: SelectivePrivacySettings(apiRules: forwardRules, peers: peerMap), phoneNumber: SelectivePrivacySettings(apiRules: phoneNumberRules, peers: peerMap), phoneDiscoveryEnabled: phoneDiscoveryValue, voiceMessages: SelectivePrivacySettings(apiRules: voiceMessagesRules, peers: peerMap), automaticallyArchiveAndMuteNonContacts: automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: accountTimeoutSeconds, messageAutoremoveTimeout: messageAutoremoveSeconds)
} }
} }

View File

@ -298,10 +298,10 @@ private struct CreateGroupState: Equatable {
var nameSetFromVenue: Bool var nameSetFromVenue: Bool
var avatar: ItemListAvatarAndNameInfoItemUpdatingAvatar? var avatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?
var location: PeerGeoLocation? var location: PeerGeoLocation?
var autoremoveTimeout: Int32 var autoremoveTimeout: Int32?
} }
private func createGroupEntries(presentationData: PresentationData, state: CreateGroupState, peerIds: [PeerId], view: MultiplePeersView, venues: [TelegramMediaMap]?) -> [CreateGroupEntry] { private func createGroupEntries(presentationData: PresentationData, state: CreateGroupState, peerIds: [PeerId], view: MultiplePeersView, venues: [TelegramMediaMap]?, globalAutoremoveTimeout: Int32) -> [CreateGroupEntry] {
var entries: [CreateGroupEntry] = [] var entries: [CreateGroupEntry] = []
let groupInfoState = ItemListAvatarAndNameInfoItemState(editingName: state.editingName, updatingName: nil) let groupInfoState = ItemListAvatarAndNameInfoItemState(editingName: state.editingName, updatingName: nil)
@ -311,11 +311,12 @@ private func createGroupEntries(presentationData: PresentationData, state: Creat
entries.append(.groupInfo(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, groupInfoState, state.avatar)) entries.append(.groupInfo(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, groupInfoState, state.avatar))
//TODO:localize //TODO:localize
let autoremoveTimeout = state.autoremoveTimeout ?? globalAutoremoveTimeout
let autoRemoveText: String let autoRemoveText: String
if state.autoremoveTimeout == 0 { if autoremoveTimeout == 0 {
autoRemoveText = "Off" autoRemoveText = "Off"
} else { } else {
autoRemoveText = timeIntervalString(strings: presentationData.strings, value: state.autoremoveTimeout) autoRemoveText = timeIntervalString(strings: presentationData.strings, value: autoremoveTimeout)
} }
entries.append(.autoDelete(title: "Auto-Delete Messages", value: autoRemoveText)) entries.append(.autoDelete(title: "Auto-Delete Messages", value: autoRemoveText))
entries.append(.autoDeleteInfo("Automatically delete messages sent in this group for everyone after a period of time.")) entries.append(.autoDeleteInfo("Automatically delete messages sent in this group for everyone after a period of time."))
@ -382,7 +383,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
location = PeerGeoLocation(latitude: latitude, longitude: longitude, address: address ?? "") location = PeerGeoLocation(latitude: latitude, longitude: longitude, address: address ?? "")
} }
let initialState = CreateGroupState(creating: false, editingName: .title(title: initialTitle ?? "", type: .group), nameSetFromVenue: false, avatar: nil, location: location, autoremoveTimeout: 0) let initialState = CreateGroupState(creating: false, editingName: .title(title: initialTitle ?? "", type: .group), nameSetFromVenue: false, avatar: nil, location: location, autoremoveTimeout: nil)
let statePromise = ValuePromise(initialState, ignoreRepeated: true) let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState) let stateValue = Atomic(value: initialState)
let updateState: ((CreateGroupState) -> CreateGroupState) -> Void = { f in let updateState: ((CreateGroupState) -> CreateGroupState) -> Void = { f in
@ -434,18 +435,21 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
} }
if !creating && !title.isEmpty { if !creating && !title.isEmpty {
updateState { current in let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.GlobalAutoremoveTimeout())
var current = current |> deliverOnMainQueue).start(next: { maybeGlobalAutoremoveTimeout in
current.creating = true updateState { current in
return current var current = current
} current.creating = true
endEditingImpl?() return current
}
let autoremoveTimeout = stateValue.with({ $0 }).autoremoveTimeout endEditingImpl?()
let ttlPeriod: Int32? = autoremoveTimeout == 0 ? nil : autoremoveTimeout
let globalAutoremoveTimeout: Int32 = maybeGlobalAutoremoveTimeout ?? 0
let createSignal: Signal<PeerId?, CreateGroupError> let autoremoveTimeout = stateValue.with({ $0 }).autoremoveTimeout ?? globalAutoremoveTimeout
switch mode { let ttlPeriod: Int32? = autoremoveTimeout == 0 ? nil : autoremoveTimeout
let createSignal: Signal<PeerId?, CreateGroupError>
switch mode {
case .generic: case .generic:
createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: ttlPeriod) createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: ttlPeriod)
case .supergroup: case .supergroup:
@ -453,16 +457,16 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|> map(Optional.init) |> map(Optional.init)
|> mapError { error -> CreateGroupError in |> mapError { error -> CreateGroupError in
switch error { switch error {
case .generic: case .generic:
return .generic return .generic
case .restricted: case .restricted:
return .restricted return .restricted
case .tooMuchJoined: case .tooMuchJoined:
return .tooMuchJoined return .tooMuchJoined
case .tooMuchLocationBasedGroups: case .tooMuchLocationBasedGroups:
return .tooMuchLocationBasedGroups return .tooMuchLocationBasedGroups
case let .serverProvided(error): case let .serverProvided(error):
return .serverProvided(error) return .serverProvided(error)
} }
} }
case .locatedGroup: case .locatedGroup:
@ -480,72 +484,72 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|> map(Optional.init) |> map(Optional.init)
|> mapError { error -> CreateGroupError in |> mapError { error -> CreateGroupError in
switch error { switch error {
case .generic: case .generic:
return .generic return .generic
case .restricted: case .restricted:
return .restricted return .restricted
case .tooMuchJoined: case .tooMuchJoined:
return .tooMuchJoined return .tooMuchJoined
case .tooMuchLocationBasedGroups: case .tooMuchLocationBasedGroups:
return .tooMuchLocationBasedGroups return .tooMuchLocationBasedGroups
case let .serverProvided(error): case let .serverProvided(error):
return .serverProvided(error) return .serverProvided(error)
} }
} }
} }
}
actionsDisposable.add((createSignal
|> mapToSignal { peerId -> Signal<PeerId?, CreateGroupError> in
guard let peerId = peerId else {
return .single(nil)
} }
let updatingAvatar = stateValue.with {
return $0.avatar actionsDisposable.add((createSignal
} |> mapToSignal { peerId -> Signal<PeerId?, CreateGroupError> in
if let _ = updatingAvatar { guard let peerId = peerId else {
return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in return .single(nil)
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
})
|> ignoreValues
|> `catch` { _ -> Signal<Never, CreateGroupError> in
return .complete()
} }
|> mapToSignal { _ -> Signal<PeerId?, CreateGroupError> in let updatingAvatar = stateValue.with {
return $0.avatar
} }
|> then(.single(peerId)) if let _ = updatingAvatar {
} else { return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: uploadedAvatar.get(), video: uploadedVideoAvatar?.0.get(), videoStartTimestamp: uploadedVideoAvatar?.1, mapResourceToAvatarSizes: { resource, representations in
return .single(peerId) return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
}
}
|> deliverOnMainQueue
|> afterDisposed {
Queue.mainQueue().async {
updateState { current in
var current = current
current.creating = false
return current
}
}
}).start(next: { peerId in
if let peerId = peerId {
if let completion = completion {
completion(peerId, {
dismissImpl?()
}) })
|> ignoreValues
|> `catch` { _ -> Signal<Never, CreateGroupError> in
return .complete()
}
|> mapToSignal { _ -> Signal<PeerId?, CreateGroupError> in
}
|> then(.single(peerId))
} else { } else {
let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: peerId)) return .single(peerId)
replaceControllerImpl?(controller)
} }
} }
}, error: { error in |> deliverOnMainQueue
if case .serverProvided = error { |> afterDisposed {
return Queue.mainQueue().async {
} updateState { current in
var current = current
let presentationData = context.sharedContext.currentPresentationData.with { $0 } current.creating = false
let text: String? return current
switch error { }
}
}).start(next: { peerId in
if let peerId = peerId {
if let completion = completion {
completion(peerId, {
dismissImpl?()
})
} else {
let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: peerId))
replaceControllerImpl?(controller)
}
}
}, error: { error in
if case .serverProvided = error {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String?
switch error {
case .privacy: case .privacy:
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelMultipleError text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelMultipleError
case .generic: case .generic:
@ -559,12 +563,13 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
text = presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch text = presentationData.strings.CreateGroup_ErrorLocatedGroupsTooMuch
default: default:
text = nil text = nil
} }
if let text = text { if let text = text {
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
} }
})) }))
})
} }
}, changeProfilePhoto: { }, changeProfilePhoto: {
endEditingImpl?() endEditingImpl?()
@ -825,89 +830,100 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}) })
ensureItemVisibleImpl?(.info, true) ensureItemVisibleImpl?(.info, true)
}, updateAutoDelete: { }, updateAutoDelete: {
var subItems: [ContextMenuItem] = [] let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.GlobalAutoremoveTimeout())
|> deliverOnMainQueue).start(next: { maybeGlobalAutoremoveTimeout in
let currentValue = stateValue.with({ $0 }).autoremoveTimeout var subItems: [ContextMenuItem] = []
let applyValue: (Int32) -> Void = { value in let globalAutoremoveTimeout: Int32 = maybeGlobalAutoremoveTimeout ?? 0
updateState { state in let currentValue: Int32 = stateValue.with({ $0 }).autoremoveTimeout ?? globalAutoremoveTimeout
var state = state
state.autoremoveTimeout = value let applyValue: (Int32) -> Void = { value in
return state updateState { state in
var state = state
state.autoremoveTimeout = value
return state
}
} }
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let presentationData = context.sharedContext.currentPresentationData.with { $0 } //TODO:localize
//TODO:localize subItems.append(.action(ContextMenuActionItem(text: "Off", icon: { theme in
subItems.append(.action(ContextMenuActionItem(text: "Off", icon: { theme in if currentValue == 0 {
if currentValue == 0 {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
}
}, action: { _, f in
applyValue(0)
f(.default)
})))
subItems.append(.separator)
var presetValues: [Int32] = [
1 * 24 * 60 * 60,
7 * 24 * 60 * 60,
31 * 24 * 60 * 60
]
if currentValue != 0 && !presetValues.contains(currentValue) {
presetValues.append(currentValue)
presetValues.sort()
}
for value in presetValues {
subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: presentationData.strings, value: value), icon: { theme in
if currentValue == value {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else { } else {
return nil return nil
} }
}, action: { _, f in }, action: { _, f in
applyValue(value) applyValue(0)
f(.default) f(.default)
}))) })))
} subItems.append(.separator)
//TODO:localize
subItems.append(.action(ContextMenuActionItem(text: "Set Custom Time...", icon: { _ in
return nil
}, action: { _, f in
f(.default)
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, style: .default, mode: .autoremove, currentTime: currentValue == 0 ? nil : currentValue, dismissByTapOutside: true, completion: { value in var presetValues: [Int32] = [
applyValue(value) 1 * 24 * 60 * 60,
}) 7 * 24 * 60 * 60,
endEditingImpl?() 31 * 24 * 60 * 60
presentControllerImpl?(controller, nil) ]
}))) if currentValue != 0 && !presetValues.contains(currentValue) {
presetValues.append(currentValue)
if let sourceNode = findAutoremoveReferenceNode?() { presetValues.sort()
let items: Signal<ContextController.Items, NoError> = .single(ContextController.Items(content: .list(subItems)))
let source: ContextContentSource = .reference(CreateGroupContextReferenceContentSource(sourceView: sourceNode.labelNode.view))
let contextController = ContextController(
account: context.account,
presentationData: presentationData,
source: source,
items: items,
gesture: nil
)
sourceNode.updateHasContextMenu(hasContextMenu: true)
contextController.dismissed = { [weak sourceNode] in
sourceNode?.updateHasContextMenu(hasContextMenu: false)
} }
presentInGlobalOverlay?(contextController)
} for value in presetValues {
subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: presentationData.strings, value: value), icon: { theme in
if currentValue == value {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
}
}, action: { _, f in
applyValue(value)
f(.default)
})))
}
//TODO:localize
subItems.append(.action(ContextMenuActionItem(text: "Set Custom Time...", icon: { _ in
return nil
}, action: { _, f in
f(.default)
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, style: .default, mode: .autoremove, currentTime: currentValue == 0 ? nil : currentValue, dismissByTapOutside: true, completion: { value in
applyValue(value)
})
endEditingImpl?()
presentControllerImpl?(controller, nil)
})))
if let sourceNode = findAutoremoveReferenceNode?() {
let items: Signal<ContextController.Items, NoError> = .single(ContextController.Items(content: .list(subItems)))
let source: ContextContentSource = .reference(CreateGroupContextReferenceContentSource(sourceView: sourceNode.labelNode.view))
let contextController = ContextController(
account: context.account,
presentationData: presentationData,
source: source,
items: items,
gesture: nil
)
sourceNode.updateHasContextMenu(hasContextMenu: true)
contextController.dismissed = { [weak sourceNode] in
sourceNode?.updateHasContextMenu(hasContextMenu: false)
}
presentInGlobalOverlay?(contextController)
}
})
}) })
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.postbox.multiplePeersView(peerIds), .single(nil) |> then(addressPromise.get()), .single(nil) |> then(venuesPromise.get())) let signal = combineLatest(queue: .mainQueue(),
|> map { presentationData, state, view, address, venues -> (ItemListControllerState, (ItemListNodeState, Any)) in context.sharedContext.presentationData,
statePromise.get(),
context.account.postbox.multiplePeersView(peerIds),
.single(nil) |> then(addressPromise.get()),
.single(nil) |> then(venuesPromise.get()),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.GlobalAutoremoveTimeout())
)
|> map { presentationData, state, view, address, venues, globalAutoremoveTimeout -> (ItemListControllerState, (ItemListNodeState, Any)) in
let rightNavigationButton: ItemListNavigationButton let rightNavigationButton: ItemListNavigationButton
if state.creating { if state.creating {
@ -919,7 +935,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Compose_NewGroupTitle), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Compose_NewGroupTitle), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createGroupEntries(presentationData: presentationData, state: state, peerIds: peerIds, view: view, venues: venues), style: .blocks, focusItemTag: CreateGroupEntryTag.info) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createGroupEntries(presentationData: presentationData, state: state, peerIds: peerIds, view: view, venues: venues, globalAutoremoveTimeout: globalAutoremoveTimeout ?? 0), style: .blocks, focusItemTag: CreateGroupEntryTag.info)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }

View File

@ -158,7 +158,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
} }
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white) self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = false displayUndo = false
self.originalRemainingSeconds = 3.5 self.originalRemainingSeconds = 4.5
case let .succeed(text): case let .succeed(text):
self.avatarNode = nil self.avatarNode = nil
self.iconNode = nil self.iconNode = nil