Update API [skip ci]

This commit is contained in:
Ilya Laktyushin 2022-12-13 17:42:32 +04:00
parent 21121e1924
commit 47ffcd2e4d
41 changed files with 867 additions and 302 deletions

View File

@ -8450,6 +8450,7 @@ Sorry for the inconvenience.";
"UserInfo.SuggestPhoto" = "Suggest Photo for %@";
"UserInfo.SetCustomPhoto" = "Set Photo for %@";
"UserInfo.ResetCustomPhoto" = "Reset to Original Photo";
"UserInfo.CustomPhotoInfo" = "You can replace %@s photo with another photo that only you will see.";
"UserInfo.SuggestPhotoTitle" = "Do you want to suggest a profile picture for %@?";
"UserInfo.SetCustomPhotoTitle" = "Do you want to set a custom profile picture for %@?";
@ -8467,6 +8468,9 @@ Sorry for the inconvenience.";
"UserInfo.CustomPhoto" = "photo set by you";
"UserInfo.CustomVideo" = "video set by you";
"UserInfo.PublicPhoto" = "public photo";
"UserInfo.PublicVideo" = "public video";
"UserInfo.ResetToOriginalAlertText" = "Are you sure you want to reset to %@ original photo?";
"UserInfo.ResetToOriginalAlertReset" = "Reset";
@ -8491,3 +8495,12 @@ Sorry for the inconvenience.";
"PhotoEditor.SetAsMyPhoto" = "Set as My Photo";
"PhotoEditor.SetAsMyVideo" = "Set as My Video";
"Notification.BotWriteAllowed" = "You allowed this bot to message you when you added it in the attachment menu.";
"Privacy.ProfilePhoto.SetPublicPhoto" = "Set Public Photo";
"Privacy.ProfilePhoto.UpdatePublicPhoto" = "Update Public Photo";
"Privacy.ProfilePhoto.RemovePublicPhoto" = "Remove Public Photo";
"Privacy.ProfilePhoto.PublicPhotoInfo" = "You can upload a public photo for those who are restricted from viewing your real profile photo.";
"WebApp.AddToAttachmentAllowMessages" = "Allow **%@** to send me messages";

View File

@ -116,7 +116,7 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
switch action.action {
case let .photoUpdated(image), let .suggestedProfilePhoto(image):
if let peer = messageMainPeer(EngineMessage(message)), let image = image {
let promise: Promise<[AvatarGalleryEntry]> = Promise([AvatarGalleryEntry.image(image.imageId, image.reference, image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), peer._asPeer(), message.timestamp, nil, message.id, image.immediateThumbnailData, "action")])
let promise: Promise<[AvatarGalleryEntry]> = Promise([AvatarGalleryEntry.image(image.imageId, image.reference, image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), peer._asPeer(), message.timestamp, nil, message.id, image.immediateThumbnailData, "action", false)])
let sourceCorners: AvatarGalleryController.SourceCorners
if case .photoUpdated = action.action {

View File

@ -198,7 +198,7 @@ public func inviteRequestsController(context: AccountContext, updatedPresentatio
} else {
string = presentationData.strings.MemberRequests_UserAddedToGroup(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
}
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: peer, text: string, action: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: peer, text: string, action: nil, duration: 3), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
})
}

View File

@ -190,13 +190,13 @@ final class MediaPickerGridItemNode: GridItemNode {
func animateFadeIn(animateCheckNode: Bool, animateSpoilerNode: Bool) {
if animateCheckNode {
self.checkNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.checkNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
self.gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.typeIconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.durationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.typeIconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.durationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
if animateSpoilerNode {
self.spoilerNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.spoilerNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
}

View File

@ -285,18 +285,18 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
self.isHidden = self.interaction?.hiddenMediaId == asset.uniqueIdentifier
if !self.isHidden && wasHidden {
if let checkNode = self.checkNode, checkNode.alpha > 0.0 {
checkNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
checkNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
if let durationTextNode = self.durationTextNode, durationTextNode.alpha > 0.0 {
durationTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
durationTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
if let durationBackgroundNode = self.durationBackgroundNode, durationBackgroundNode.alpha > 0.0 {
durationBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
durationBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
if let spoilerNode = self.spoilerNode, spoilerNode.alpha > 0.0 {
spoilerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
spoilerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
}
}

View File

@ -37,10 +37,16 @@ public func peerInfoProfilePhotos(context: AccountContext, peerId: EnginePeer.Id
|> mapToSignal { peerView -> Signal<(Bool, [AvatarGalleryEntry])?, NoError>in
if let peer = peerViewMainPeer(peerView) {
var secondEntry: TelegramMediaImage?
if firstEntry.representations.first?.representation.isPersonal == true, let cachedData = peerView.cachedData as? CachedUserData, case let .known(photo) = cachedData.photo {
secondEntry = photo
var lastEntry: TelegramMediaImage?
if let cachedData = peerView.cachedData as? CachedUserData {
if firstEntry.representations.first?.representation.isPersonal == true, case let .known(photo) = cachedData.photo {
secondEntry = photo
}
if case let .known(photo) = cachedData.fallbackPhoto {
lastEntry = photo
}
}
return fetchedAvatarGalleryEntries(engine: context.engine, account: context.account, peer: peer, firstEntry: firstEntry, secondEntry: secondEntry)
return fetchedAvatarGalleryEntries(engine: context.engine, account: context.account, peer: peer, firstEntry: firstEntry, secondEntry: secondEntry, lastEntry: lastEntry)
|> map(Optional.init)
} else {
return .single(nil)
@ -74,7 +80,7 @@ public func peerInfoProfilePhotosWithCache(context: AccountContext, peerId: Peer
public enum AvatarGalleryEntry: Equatable {
case topImage([ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, GalleryItemIndexData?, Data?, String?)
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, Int32?, GalleryItemIndexData?, MessageId?, Data?, String?)
case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, Int32?, GalleryItemIndexData?, MessageId?, Data?, String?, Bool)
public init(representation: TelegramMediaImageRepresentation, peer: Peer) {
self = .topImage([ImageRepresentationWithReference(representation: representation, reference: MediaResourceReference.standalone(resource: representation.resource))], [], peer, nil, nil, nil)
@ -87,7 +93,7 @@ public enum AvatarGalleryEntry: Equatable {
return .resource(last.representation.resource.id.stringRepresentation)
}
return .topImage
case let .image(id, _, representations, _, _, _, _, _, _, _):
case let .image(id, _, representations, _, _, _, _, _, _, _, _):
if let last = representations.last {
return .resource(last.representation.resource.id.stringRepresentation)
}
@ -99,7 +105,7 @@ public enum AvatarGalleryEntry: Equatable {
switch self {
case let .topImage(_, _, peer, _, _, _):
return peer
case let .image(_, _, _, _, peer, _, _, _, _, _):
case let .image(_, _, _, _, peer, _, _, _, _, _, _):
return peer
}
}
@ -108,7 +114,7 @@ public enum AvatarGalleryEntry: Equatable {
switch self {
case let .topImage(representations, _, _, _, _, _):
return representations
case let .image(_, _, representations, _, _, _, _, _, _, _):
case let .image(_, _, representations, _, _, _, _, _, _, _, _):
return representations
}
}
@ -117,7 +123,7 @@ public enum AvatarGalleryEntry: Equatable {
switch self {
case let .topImage(_, _, _, _, immediateThumbnailData, _):
return immediateThumbnailData
case let .image(_, _, _, _, _, _, _, _, immediateThumbnailData, _):
case let .image(_, _, _, _, _, _, _, _, immediateThumbnailData, _, _):
return immediateThumbnailData
}
}
@ -126,7 +132,7 @@ public enum AvatarGalleryEntry: Equatable {
switch self {
case let .topImage(_, videoRepresentations, _, _, _, _):
return videoRepresentations
case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _):
case let .image(_, _, _, videoRepresentations, _, _, _, _, _, _, _):
return videoRepresentations
}
}
@ -135,7 +141,7 @@ public enum AvatarGalleryEntry: Equatable {
switch self {
case let .topImage(_, _, _, indexData, _, _):
return indexData
case let .image(_, _, _, _, _, _, indexData, _, _, _):
case let .image(_, _, _, _, _, _, indexData, _, _, _, _):
return indexData
}
}
@ -148,8 +154,8 @@ public enum AvatarGalleryEntry: Equatable {
} else {
return false
}
case let .image(lhsId, lhsImageReference, lhsRepresentations, lhsVideoRepresentations, lhsPeer, lhsDate, lhsIndexData, lhsMessageId, lhsImmediateThumbnailData, lhsCategory):
if case let .image(rhsId, rhsImageReference, rhsRepresentations, rhsVideoRepresentations, rhsPeer, rhsDate, rhsIndexData, rhsMessageId, rhsImmediateThumbnailData, rhsCategory) = rhs, lhsId == rhsId, lhsImageReference == rhsImageReference, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, arePeersEqual(lhsPeer, rhsPeer), lhsDate == rhsDate, lhsIndexData == rhsIndexData, lhsMessageId == rhsMessageId, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory {
case let .image(lhsId, lhsImageReference, lhsRepresentations, lhsVideoRepresentations, lhsPeer, lhsDate, lhsIndexData, lhsMessageId, lhsImmediateThumbnailData, lhsCategory, lhsIsFallback):
if case let .image(rhsId, rhsImageReference, rhsRepresentations, rhsVideoRepresentations, rhsPeer, rhsDate, rhsIndexData, rhsMessageId, rhsImmediateThumbnailData, rhsCategory, rhsIsFallback) = rhs, lhsId == rhsId, lhsImageReference == rhsImageReference, lhsRepresentations == rhsRepresentations, lhsVideoRepresentations == rhsVideoRepresentations, arePeersEqual(lhsPeer, rhsPeer), lhsDate == rhsDate, lhsIndexData == rhsIndexData, lhsMessageId == rhsMessageId, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsCategory == rhsCategory, lhsIsFallback == rhsIsFallback {
return true
} else {
return false
@ -176,8 +182,8 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE
let indexData = GalleryItemIndexData(position: index, totalCount: count)
if case let .topImage(representations, videoRepresentations, peer, _, immediateThumbnailData, category) = entry {
updatedEntries.append(.topImage(representations, videoRepresentations, peer, indexData, immediateThumbnailData, category))
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData, category) = entry {
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData, category))
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData, category, isFallback) = entry {
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData, category, isFallback))
}
index += 1
}
@ -203,7 +209,7 @@ public func initialAvatarGalleryEntries(account: Account, engine: TelegramEngine
if photo.immediateThumbnailData == nil, let firstEntry = initialEntries.first, let firstRepresentation = firstEntry.representations.first {
representations.insert(firstRepresentation, at: 0)
}
return [.image(photo.imageId, photo.reference, representations, photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, nil, nil, nil, photo.immediateThumbnailData, nil)]
return [.image(photo.imageId, photo.reference, representations, photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, nil, nil, nil, photo.immediateThumbnailData, nil, false)]
} else {
if case .known = peerPhoto {
return []
@ -235,7 +241,7 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) {
var initialMediaIds = Set<MediaId>()
for entry in initialEntries {
if case let .image(mediaId, _, _, _, _, _, _, _, _, _) = entry {
if case let .image(mediaId, _, _, _, _, _, _, _, _, _, _) = entry {
initialMediaIds.insert(mediaId)
}
}
@ -247,24 +253,24 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account
photosCount += 1
for entry in initialEntries {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
if case let .image(mediaId, imageReference, representations, videoRepresentations, peer, _, _, _, thumbnailData, _) = entry {
result.append(.image(mediaId, imageReference, representations, videoRepresentations, peer, nil, indexData, nil, thumbnailData, nil))
if case let .image(mediaId, imageReference, representations, videoRepresentations, peer, _, _, _, thumbnailData, _, _) = entry {
result.append(.image(mediaId, imageReference, representations, videoRepresentations, peer, nil, indexData, nil, thumbnailData, nil, false))
index += 1
}
}
}
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil, false))
index += 1
}
} else {
for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil, false))
} else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil, false))
}
index += 1
}
@ -276,7 +282,7 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account
}
}
public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account, peer: Peer, firstEntry: AvatarGalleryEntry, secondEntry: TelegramMediaImage?) -> Signal<(Bool, [AvatarGalleryEntry]), NoError> {
public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account, peer: Peer, firstEntry: AvatarGalleryEntry, secondEntry: TelegramMediaImage?, lastEntry: TelegramMediaImage?) -> Signal<(Bool, [AvatarGalleryEntry]), NoError> {
let initialEntries = [firstEntry]
return Signal<(Bool, [AvatarGalleryEntry]), NoError>.single((false, initialEntries))
|> then(
@ -292,7 +298,7 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) {
var initialMediaIds = Set<MediaId>()
for entry in initialEntries {
if case let .image(mediaId, _, _, _, _, _, _, _, _, _) = entry {
if case let .image(mediaId, _, _, _, _, _, _, _, _, _, _) = entry {
initialMediaIds.insert(mediaId)
}
}
@ -306,8 +312,8 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account
photosCount += 1
for entry in initialEntries {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
if case let .image(mediaId, imageReference, representations, videoRepresentations, peer, _, _, _, thumbnailData, _) = entry {
result.append(.image(mediaId, imageReference, representations, videoRepresentations, peer, nil, indexData, nil, thumbnailData, nil))
if case let .image(mediaId, imageReference, representations, videoRepresentations, peer, _, _, _, thumbnailData, _, _) = entry {
result.append(.image(mediaId, imageReference, representations, videoRepresentations, peer, nil, indexData, nil, thumbnailData, nil, false))
index += 1
}
}
@ -317,7 +323,7 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account
}
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
result.append(.image(photo.image.imageId, photo.image.reference, representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
result.append(.image(photo.image.imageId, photo.image.reference, representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil, false))
index += 1
}
} else {
@ -325,12 +331,15 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account
if let secondEntry {
photos.insert(TelegramPeerPhoto(image: secondEntry, reference: secondEntry.reference, date: 0, index: 1, totalCount: 0, messageId: nil), at: 1)
}
if let lastEntry {
photos.append(TelegramPeerPhoto(image: lastEntry, reference: lastEntry.reference, date: 0, index: photos.count, totalCount: 0, messageId: nil))
}
for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil, false))
} else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil, photo.image.id == lastEntry?.id))
}
index += 1
}
@ -441,8 +450,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
let isFirstTime = strongSelf.entries.isEmpty
var entries = entries
if !isFirstTime, let updated = entries.first, case let .image(mediaId, imageReference, _, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption) = updated, !videoRepresentations.isEmpty, let previous = strongSelf.entries.first, case let .topImage(representations, _, _, _, _, _) = previous {
let firstEntry = AvatarGalleryEntry.image(mediaId, imageReference, representations, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption)
if !isFirstTime, let updated = entries.first, case let .image(mediaId, imageReference, _, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption, _) = updated, !videoRepresentations.isEmpty, let previous = strongSelf.entries.first, case let .topImage(representations, _, _, _, _, _) = previous {
let firstEntry = AvatarGalleryEntry.image(mediaId, imageReference, representations, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption, false)
entries.remove(at: 0)
entries.insert(firstEntry, at: 0)
}
@ -758,13 +767,13 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
if self.peer.id == self.context.account.peerId {
} else {
}
case let .image(_, reference, _, _, _, _, _, _, _, _):
case let .image(_, reference, _, _, _, _, _, _, _, _, _):
if self.peer.id == self.context.account.peerId, let peerReference = PeerReference(self.peer) {
if let reference = reference {
let _ = (self.context.engine.accountData.updatePeerPhotoExisting(reference: reference)
|> deliverOnMainQueue).start(next: { [weak self] photo in
if let strongSelf = self, let photo = photo, let firstEntry = strongSelf.entries.first, case let .image(_, _, _, _, _, index, indexData, messageId, _, caption) = firstEntry {
let updatedEntry = AvatarGalleryEntry.image(photo.imageId, photo.reference, photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), strongSelf.peer, index, indexData, messageId, photo.immediateThumbnailData, caption)
if let strongSelf = self, let photo = photo, let firstEntry = strongSelf.entries.first, case let .image(_, _, _, _, _, index, indexData, messageId, _, caption, _) = firstEntry {
let updatedEntry = AvatarGalleryEntry.image(photo.imageId, photo.reference, photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), strongSelf.peer, index, indexData, messageId, photo.immediateThumbnailData, caption, false)
for (lhs, rhs) in zip(firstEntry.representations, updatedEntry.representations) {
if lhs.representation.dimensions == rhs.representation.dimensions {
@ -882,7 +891,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
}
}
}
case let .image(_, reference, _, _, _, _, _, messageId, _, _):
case let .image(_, reference, _, _, _, _, _, messageId, _, _, _):
if self.peer.id == self.context.account.peerId {
if let reference = reference {
let _ = self.context.engine.accountData.removeAccountPhoto(reference: reference).start()

View File

@ -107,7 +107,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
var buttonText: String?
var canShare = true
switch entry {
case let .image(_, _, _, videoRepresentations, peer, date, _, _, _, _):
case let .image(_, _, _, videoRepresentations, peer, date, _, _, _, _, _):
nameText = peer.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
if let date = date {
dateText = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: date).string

View File

@ -106,7 +106,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
switch self.entry {
case let .topImage(representations, _, _, _, _, _):
content = representations
case let .image(_, _, representations, _, _, _, _, _, _, _):
case let .image(_, _, representations, _, _, _, _, _, _, _, _):
content = representations
}
@ -268,7 +268,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
var id: Int64
var category: String?
if case let .image(mediaId, _, _, _, _, _, _, _, _, categoryValue) = entry {
if case let .image(mediaId, _, _, _, _, _, _, _, _, categoryValue, _) = entry {
id = mediaId.id
category = categoryValue
} else {
@ -608,7 +608,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
switch entry {
case let .topImage(topRepresentations, _, _, _, _, _):
representations = topRepresentations
case let .image(_, _, imageRepresentations, _, _, _, _, _, _, _):
case let .image(_, _, imageRepresentations, _, _, _, _, _, _, _, _):
representations = imageRepresentations
}

View File

@ -15,6 +15,7 @@ import GalleryUI
import UniversalMediaPlayer
import RadialStatusNode
import TelegramUIPreferences
import AvatarNode
private class PeerInfoAvatarListLoadingStripNode: ASImageNode {
private var currentInHierarchy = false
@ -90,7 +91,7 @@ private struct CustomListItemResourceId {
public enum PeerInfoAvatarListItem: Equatable {
case custom(ASDisplayNode)
case topImage([ImageRepresentationWithReference], [VideoRepresentationWithReference], Data?)
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Data?)
case image(TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Data?, Bool)
var id: MediaResourceId {
switch self {
@ -99,7 +100,7 @@ public enum PeerInfoAvatarListItem: Equatable {
case let .topImage(representations, _, _):
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
return representation.resource.id
case let .image(_, representations, _, _):
case let .image(_, representations, _, _, _):
let representation = largestImageRepresentation(representations.map { $0.representation }) ?? representations[representations.count - 1].representation
return representation.resource.id
}
@ -114,7 +115,7 @@ public enum PeerInfoAvatarListItem: Equatable {
} else {
return false
}
} else if case let .image(_, rhsRepresentations, _, _) = self {
} else if case let .image(_, rhsRepresentations, _, _, _) = self {
if let lhsRepresentation = largestImageRepresentation(lhsRepresentations.map { $0.representation }),
let rhsRepresentation = largestImageRepresentation(rhsRepresentations.map { $0.representation }) {
return lhsRepresentation.isSemanticallyEqual(to: rhsRepresentation)
@ -124,7 +125,7 @@ public enum PeerInfoAvatarListItem: Equatable {
} else {
return false
}
} else if case let .image(_, lhsRepresentations, _, _) = self {
} else if case let .image(_, lhsRepresentations, _, _, _) = self {
if case let .topImage(rhsRepresentations, _, _) = self {
if let lhsRepresentation = largestImageRepresentation(lhsRepresentations.map { $0.representation }),
let rhsRepresentation = largestImageRepresentation(rhsRepresentations.map { $0.representation }) {
@ -132,7 +133,7 @@ public enum PeerInfoAvatarListItem: Equatable {
} else {
return false
}
} else if case let .image(_, rhsRepresentations, _, _) = self {
} else if case let .image(_, rhsRepresentations, _, _, _) = self {
if let lhsRepresentation = largestImageRepresentation(lhsRepresentations.map { $0.representation }),
let rhsRepresentation = largestImageRepresentation(rhsRepresentations.map { $0.representation }) {
return lhsRepresentation.isSemanticallyEqual(to: rhsRepresentation)
@ -153,7 +154,7 @@ public enum PeerInfoAvatarListItem: Equatable {
return []
case let .topImage(representations, _, _):
return representations
case let .image(_, representations, _, _):
case let .image(_, representations, _, _, _):
return representations
}
}
@ -165,20 +166,29 @@ public enum PeerInfoAvatarListItem: Equatable {
return []
case let .topImage(_, videoRepresentations, _):
return videoRepresentations
case let .image(_, _, videoRepresentations, _):
case let .image(_, _, videoRepresentations, _, _):
return videoRepresentations
}
}
var isFallback: Bool {
switch self {
case .custom, .topImage:
return false
case let .image(_, _, _, _, isFallback):
return isFallback
}
}
public init?(entry: AvatarGalleryEntry) {
switch entry {
case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _):
self = .topImage(representations, videoRepresentations, immediateThumbnailData)
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _, isFallback):
if representations.isEmpty {
return nil
}
self = .image(reference, representations, videoRepresentations, immediateThumbnailData)
self = .image(reference, representations, videoRepresentations, immediateThumbnailData, isFallback)
}
}
}
@ -441,7 +451,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource {
id = id &+ resource.photoId
}
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _):
representations = imageRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
@ -523,6 +533,9 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
public let stripContainerNode: ASDisplayNode
public let highlightContainerNode: ASDisplayNode
private let setByYouNode: ImmediateTextNode
private let setByYouImageNode: ImageNode
private var setByYouTapRecognizer: UITapGestureRecognizer?
public private(set) var galleryEntries: [AvatarGalleryEntry] = []
private var items: [PeerInfoAvatarListItem] = []
private var itemNodes: [MediaResourceId: PeerInfoAvatarListItemNode] = [:]
@ -694,6 +707,10 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
self.setByYouNode.alpha = 0.0
self.setByYouNode.isUserInteractionEnabled = false
self.setByYouImageNode = ImageNode()
self.setByYouImageNode.alpha = 0.0
self.setByYouImageNode.isUserInteractionEnabled = false
self.controlsContainerNode = ASDisplayNode()
self.controlsContainerNode.isUserInteractionEnabled = false
@ -764,6 +781,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
self.controlsClippingNode.addSubnode(self.controlsContainerNode)
self.controlsClippingOffsetNode.addSubnode(self.controlsClippingNode)
self.stripContainerNode.addSubnode(self.setByYouNode)
self.stripContainerNode.addSubnode(self.setByYouImageNode)
self.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
guard let strongSelf = self else {
@ -830,6 +848,19 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
self.positionDisposable.dispose()
}
public override func didLoad() {
super.didLoad()
let setByYouTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.setByYouTapped))
self.setByYouNode.isUserInteractionEnabled = true
self.setByYouNode.view.addGestureRecognizer(setByYouTapRecognizer)
self.setByYouTapRecognizer = setByYouTapRecognizer
}
@objc private func setByYouTapped() {
self.selectLastItem()
}
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return super.hitTest(point, with: event)
}
@ -845,6 +876,17 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
}
}
public func selectLastItem() {
let previousIndex = self.currentIndex
self.currentIndex = self.items.count - 1
if self.currentIndex != previousIndex {
self.currentIndexUpdated?()
}
if let size = self.validLayout {
self.updateItems(size: size, transition: .immediate, stripTransition: .animated(duration: 0.3, curve: .spring))
}
}
public func updateEntryIsHidden(entry: AvatarGalleryEntry?) {
if let entry = entry, let index = self.galleryEntries.firstIndex(of: entry) {
self.currentItemNode?.isHidden = index == self.currentIndex
@ -956,7 +998,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
}
func setMainItem(_ item: PeerInfoAvatarListItem) {
guard case let .image(imageReference, _, _, _) = item else {
guard case let .image(imageReference, _, _, _, _) = item else {
return
}
var items: [PeerInfoAvatarListItem] = []
@ -966,16 +1008,16 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _):
entries.append(entry)
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _, isFallback):
if representations.isEmpty {
continue
}
if imageReference == reference {
entries.insert(entry, at: 0)
items.insert(.image(reference, representations, videoRepresentations, immediateThumbnailData), at: 0)
items.insert(.image(reference, representations, videoRepresentations, immediateThumbnailData, isFallback), at: 0)
} else {
entries.append(entry)
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData))
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData, isFallback))
}
}
}
@ -994,7 +1036,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
}
public func deleteItem(_ item: PeerInfoAvatarListItem) -> Bool {
guard case let .image(imageReference, _, _, _) = item else {
guard case let .image(imageReference, _, _, _, _) = item else {
return false
}
@ -1009,13 +1051,13 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _):
entries.append(entry)
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _, isFallback):
if representations.isEmpty {
continue
}
if imageReference != reference {
entries.append(entry)
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData))
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData, isFallback))
} else {
deletedIndex = index
}
@ -1081,8 +1123,8 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
}
var synchronous = false
if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(mediaId, reference, _, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption) = updated, !videoRepresentations.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(representations, _, _, _, _, _) = previous {
let firstEntry = AvatarGalleryEntry.image(mediaId, reference, representations, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption)
if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(mediaId, reference, _, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption, _) = updated, !videoRepresentations.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(representations, _, _, _, _, _) = previous {
let firstEntry = AvatarGalleryEntry.image(mediaId, reference, representations, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption, false)
entries.remove(at: 0)
entries.insert(firstEntry, at: 0)
synchronous = true
@ -1264,13 +1306,39 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
if !self.items.isEmpty, self.currentIndex >= 0 && self.currentIndex < self.items.count {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let currentItem = self.items[self.currentIndex]
var photoTitle: String?
var hasLink = false
var fallbackImageSignal: Signal<UIImage?, NoError>?
if let representation = currentItem.representations.first?.representation, representation.isPersonal {
photoTitle = representation.hasVideo ? presentationData.strings.UserInfo_CustomVideo : presentationData.strings.UserInfo_CustomPhoto
} else if currentItem.isFallback, let representation = currentItem.representations.first?.representation {
photoTitle = representation.hasVideo ? presentationData.strings.UserInfo_PublicVideo : presentationData.strings.UserInfo_PublicPhoto
} else if self.currentIndex == 0, let lastItem = self.items.last, lastItem.isFallback, let representation = lastItem.representations.first?.representation {
photoTitle = representation.hasVideo ? presentationData.strings.UserInfo_PublicVideo : presentationData.strings.UserInfo_PublicPhoto
hasLink = true
if let peer = self.peer {
fallbackImageSignal = peerAvatarCompleteImage(account: self.context.account, peer: EnginePeer(peer), forceProvidedRepresentation: true, representation: representation, size: CGSize(width: 28.0, height: 28.0))
}
}
if let photoTitle = photoTitle {
transition.updateAlpha(node: self.setByYouNode, alpha: 0.7)
self.setByYouNode.attributedText = NSAttributedString(string: representation.hasVideo ? presentationData.strings.UserInfo_CustomVideo : presentationData.strings.UserInfo_CustomPhoto, font: Font.regular(12.0), textColor: UIColor.white)
self.setByYouNode.attributedText = NSAttributedString(string: photoTitle, font: Font.regular(12.0), textColor: UIColor.white)
let setByYouSize = self.setByYouNode.updateLayout(size)
self.setByYouNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - setByYouSize.width) / 2.0), y: 17.0), size: setByYouSize)
self.setByYouNode.isUserInteractionEnabled = hasLink
} else {
transition.updateAlpha(node: self.setByYouNode, alpha: 0.0)
self.setByYouNode.isUserInteractionEnabled = false
}
if let fallbackImageSignal = fallbackImageSignal {
self.setByYouImageNode.setSignal(fallbackImageSignal)
transition.updateAlpha(node: self.setByYouImageNode, alpha: 1.0)
self.setByYouImageNode.frame = CGRect(origin: CGPoint(x: self.setByYouNode.frame.minX - 32.0, y: 11.0), size: CGSize(width: 28.0, height: 28.0))
} else {
transition.updateAlpha(node: self.setByYouImageNode, alpha: 0.0)
}
}

View File

@ -82,6 +82,7 @@ public final class QrCodeScanScreen: ViewController {
public enum Subject {
case authTransfer(activeSessionsContext: ActiveSessionsContext)
case peer
case custom(info: String)
}
private let context: AccountContext
@ -97,9 +98,12 @@ public final class QrCodeScanScreen: ViewController {
}
public var showMyCode: () -> Void = {}
public var completion: (String?) -> Void = { _ in }
private var codeResolved = false
private var validLayout: ContainerViewLayout?
public init(context: AccountContext, subject: QrCodeScanScreen.Subject) {
self.context = context
self.subject = subject
@ -126,7 +130,9 @@ public final class QrCodeScanScreen: ViewController {
(strongSelf.displayNode as! QrCodeScanScreenNode).updateInForeground(inForeground)
})
if case .peer = subject {
if case .custom = subject {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
} else if case .peer = subject {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Contacts_QrCode_MyCode, style: .plain, target: self, action: #selector(self.myCodePressed))
} else {
#if DEBUG
@ -145,6 +151,16 @@ public final class QrCodeScanScreen: ViewController {
self.approveDisposable.dispose()
}
@objc private func cancelPressed() {
guard let layout = self.validLayout else {
return
}
self.completion(nil)
self.controllerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: layout.size.height), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in
self.dismiss()
})
}
@objc private func myCodePressed() {
self.showMyCode()
}
@ -153,6 +169,16 @@ public final class QrCodeScanScreen: ViewController {
self.dismissWithSession(session: nil)
}
private var animatedIn = false
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if case .custom = self.subject, !self.animatedIn, let layout = self.validLayout {
self.animatedIn = true
self.controllerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: layout.size.height), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
}
private func dismissWithSession(session: RecentAccountSession?) {
guard case let .authTransfer(activeSessionsContext) = self.subject else {
return
@ -184,6 +210,14 @@ public final class QrCodeScanScreen: ViewController {
}
}
private func completeWithCode(_ code: String) {
guard case .custom = self.subject else {
return
}
self.completion(code)
self.dismiss()
}
override public func loadDisplayNode() {
self.displayNode = QrCodeScanScreenNode(context: self.context, presentationData: self.presentationData, controller: self, subject: self.subject)
@ -235,6 +269,8 @@ public final class QrCodeScanScreen: ViewController {
}
})
}
case .custom:
strongSelf.completeWithCode(code)
}
})
@ -246,6 +282,8 @@ public final class QrCodeScanScreen: ViewController {
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.validLayout = layout
(self.displayNode as! QrCodeScanScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
}
}
@ -344,6 +382,9 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie
case .peer:
title = ""
text = ""
case let .custom(info):
title = presentationData.strings.AuthSessions_AddDevice_ScanTitle
text = info
}
self.titleNode = ImmediateTextNode()
@ -445,6 +486,8 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie
filteredCodes = codes.filter { $0.message.hasPrefix("tg://") }
case .peer:
filteredCodes = codes.filter { $0.message.hasPrefix("https://t.me/") || $0.message.hasPrefix("t.me/") }
case .custom:
filteredCodes = codes
}
if let code = filteredCodes.first, CGRect(x: 0.3, y: 0.3, width: 0.4, height: 0.4).contains(code.boundingBox.center) {
if strongSelf.codeWithError != code.message {

View File

@ -614,7 +614,22 @@ class PrivacyAndSecurityControllerImpl: ItemListController, ASAuthorizationContr
}
}
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil, updatedHasTwoStepAuth: ((Bool) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil, activeSessionsContext: ActiveSessionsContext? = nil, webSessionsContext: WebSessionsContext? = nil, blockedPeersContext: BlockedPeersContext? = nil, hasTwoStepAuth: Bool? = nil, loginEmailPattern: Signal<String?, NoError>? = nil, updatedTwoStepAuthData: (() -> Void)? = nil) -> ViewController {
public func privacyAndSecurityController(
context: AccountContext,
initialSettings: AccountPrivacySettings? = nil,
updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil,
updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil,
updatedHasTwoStepAuth: ((Bool) -> Void)? = nil,
focusOnItemTag: PrivacyAndSecurityEntryTag? = nil,
activeSessionsContext: ActiveSessionsContext? = nil,
webSessionsContext: WebSessionsContext? = nil,
blockedPeersContext: BlockedPeersContext? = nil,
hasTwoStepAuth: Bool? = nil,
loginEmailPattern: Signal<String?, NoError>? = nil,
updatedTwoStepAuthData: (() -> Void)? = nil,
requestPublicPhotoSetup: (() -> Void)? = nil,
requestPublicPhotoRemove: (() -> Void)? = nil
) -> ViewController {
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
let updateState: ((PrivacyAndSecurityControllerState) -> PrivacyAndSecurityControllerState) -> Void = { f in
@ -804,7 +819,11 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
if let info = info {
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .profilePhoto, current: info.profilePhoto, updated: { updated, _, _ in
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .profilePhoto, current: info.profilePhoto, requestPublicPhotoSetup: {
requestPublicPhotoSetup?()
}, requestPublicPhotoRemove: {
requestPublicPhotoRemove?()
}, updated: { updated, _, _ in
if let currentInfoDisposable = currentInfoDisposable {
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|> filter { $0 != nil }

View File

@ -10,6 +10,7 @@ import ItemListUI
import PresentationDataUtils
import AccountContext
import UndoUI
import ItemListPeerActionItem
enum SelectivePrivacySettingsKind {
case presence
@ -54,7 +55,10 @@ private final class SelectivePrivacySettingsControllerArguments {
let updatePhoneDiscovery: ((Bool) -> Void)?
let copyPhoneLink: ((String) -> Void)?
init(context: AccountContext, updateType: @escaping (SelectivePrivacySettingType) -> Void, openSelective: @escaping (SelectivePrivacySettingsPeerTarget, Bool) -> Void, updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?, updateCallIntegrationEnabled: ((Bool) -> Void)?, updatePhoneDiscovery: ((Bool) -> Void)?, copyPhoneLink: ((String) -> Void)?) {
let setPublicPhoto: (() -> Void)?
let removePublicPhoto: (() -> Void)?
init(context: AccountContext, updateType: @escaping (SelectivePrivacySettingType) -> Void, openSelective: @escaping (SelectivePrivacySettingsPeerTarget, Bool) -> Void, updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?, updateCallIntegrationEnabled: ((Bool) -> Void)?, updatePhoneDiscovery: ((Bool) -> Void)?, copyPhoneLink: ((String) -> Void)?, setPublicPhoto: (() -> Void)?, removePublicPhoto: (() -> Void)?) {
self.context = context
self.updateType = updateType
self.openSelective = openSelective
@ -63,12 +67,16 @@ private final class SelectivePrivacySettingsControllerArguments {
self.updateCallIntegrationEnabled = updateCallIntegrationEnabled
self.updatePhoneDiscovery = updatePhoneDiscovery
self.copyPhoneLink = copyPhoneLink
self.setPublicPhoto = setPublicPhoto
self.removePublicPhoto = removePublicPhoto
}
}
private enum SelectivePrivacySettingsSection: Int32 {
case forwards
case setting
case photo
case peers
case callsP2P
case callsP2PPeers
@ -96,6 +104,9 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
case contacts(PresentationTheme, String, Bool)
case nobody(PresentationTheme, String, Bool)
case settingInfo(PresentationTheme, String, String)
case setPublicPhoto(PresentationTheme, String)
case removePublicPhoto(PresentationTheme, String, EnginePeer, TelegramMediaImage?)
case publicPhotoInfo(PresentationTheme, String)
case exceptionsHeader(PresentationTheme, String)
case disableFor(PresentationTheme, String, String)
case enableFor(PresentationTheme, String, String)
@ -121,6 +132,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return SelectivePrivacySettingsSection.forwards.rawValue
case .settingHeader, .everybody, .contacts, .nobody, .settingInfo:
return SelectivePrivacySettingsSection.setting.rawValue
case .setPublicPhoto, .removePublicPhoto, .publicPhotoInfo:
return SelectivePrivacySettingsSection.photo.rawValue
case .exceptionsHeader, .disableFor, .enableFor, .peersInfo:
return SelectivePrivacySettingsSection.peers.rawValue
case .callsP2PHeader, .callsP2PAlways, .callsP2PContacts, .callsP2PNever, .callsP2PInfo:
@ -150,42 +163,48 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return 5
case .settingInfo:
return 6
case .phoneDiscoveryHeader:
case .setPublicPhoto:
return 7
case .phoneDiscoveryEverybody:
case .removePublicPhoto:
return 8
case .phoneDiscoveryMyContacts:
case .publicPhotoInfo:
return 9
case .phoneDiscoveryInfo:
case .phoneDiscoveryHeader:
return 10
case .exceptionsHeader:
case .phoneDiscoveryEverybody:
return 11
case .disableFor:
case .phoneDiscoveryMyContacts:
return 12
case .enableFor:
case .phoneDiscoveryInfo:
return 13
case .peersInfo:
case .exceptionsHeader:
return 14
case .callsP2PHeader:
case .disableFor:
return 15
case .callsP2PAlways:
case .enableFor:
return 16
case .callsP2PContacts:
case .peersInfo:
return 17
case .callsP2PNever:
case .callsP2PHeader:
return 18
case .callsP2PInfo:
case .callsP2PAlways:
return 19
case .callsP2PDisableFor:
case .callsP2PContacts:
return 20
case .callsP2PEnableFor:
case .callsP2PNever:
return 21
case .callsP2PPeersInfo:
case .callsP2PInfo:
return 22
case .callsIntegrationEnabled:
case .callsP2PDisableFor:
return 23
case .callsIntegrationInfo:
case .callsP2PEnableFor:
return 24
case .callsP2PPeersInfo:
return 25
case .callsIntegrationEnabled:
return 26
case .callsIntegrationInfo:
return 27
}
}
@ -222,7 +241,25 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return false
}
case let .nobody(lhsTheme, lhsText, lhsValue):
if case let nobody(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
if case let .nobody(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .setPublicPhoto(lhsTheme, lhsText):
if case let .setPublicPhoto(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .removePublicPhoto(lhsTheme, lhsText, lhsPeer, lhsRep):
if case let .removePublicPhoto(rhsTheme, rhsText, rhsPeer, rhsRep) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsPeer == rhsPeer, lhsRep == rhsRep {
return true
} else {
return false
}
case let .publicPhotoInfo(lhsTheme, lhsText):
if case let .publicPhotoInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
@ -373,6 +410,17 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
arguments.copyPhoneLink?(link)
})
case let .setPublicPhoto(theme, text):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPhotoIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
arguments.setPublicPhoto?()
})
case let .removePublicPhoto(theme, text, _, _):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.deleteIconImage(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
arguments.removePublicPhoto?()
})
case let .publicPhotoInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
})
case let .exceptionsHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .disableFor(_, title, value):
@ -539,7 +587,7 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
}
}
private func selectivePrivacySettingsControllerEntries(presentationData: PresentationData, kind: SelectivePrivacySettingsKind, state: SelectivePrivacySettingsControllerState, peerName: String, phoneNumber: String) -> [SelectivePrivacySettingsEntry] {
private func selectivePrivacySettingsControllerEntries(presentationData: PresentationData, kind: SelectivePrivacySettingsKind, state: SelectivePrivacySettingsControllerState, peerName: String, phoneNumber: String, peer: EnginePeer?, publicPhoto: TelegramMediaImage?) -> [SelectivePrivacySettingsEntry] {
var entries: [SelectivePrivacySettingsEntry] = []
let settingTitle: String
@ -628,6 +676,16 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
entries.append(.phoneDiscoveryInfo(presentationData.theme, state.phoneDiscoveryEnabled != false ? presentationData.strings.PrivacyPhoneNumberSettings_CustomPublicLink("+\(phoneNumber)").string : presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp, phoneLink))
}
if case .profilePhoto = kind, let peer = peer {
if let publicPhoto = publicPhoto {
entries.append(.setPublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_UpdatePublicPhoto))
entries.append(.removePublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_RemovePublicPhoto, peer, publicPhoto))
} else {
entries.append(.setPublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_SetPublicPhoto))
}
entries.append(.publicPhotoInfo(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_PublicPhotoInfo))
}
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_Exceptions))
switch state.setting {
@ -671,7 +729,18 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
return entries
}
func selectivePrivacySettingsController(context: AccountContext, kind: SelectivePrivacySettingsKind, current: SelectivePrivacySettings, callSettings: (SelectivePrivacySettings, VoiceCallSettings)? = nil, phoneDiscoveryEnabled: Bool? = nil, voipConfiguration: VoipConfiguration? = nil, callIntegrationAvailable: Bool? = nil, updated: @escaping (SelectivePrivacySettings, (SelectivePrivacySettings, VoiceCallSettings)?, Bool?) -> Void) -> ViewController {
func selectivePrivacySettingsController(
context: AccountContext,
kind: SelectivePrivacySettingsKind,
current: SelectivePrivacySettings,
callSettings: (SelectivePrivacySettings, VoiceCallSettings)? = nil,
phoneDiscoveryEnabled: Bool? = nil,
voipConfiguration: VoipConfiguration? = nil,
callIntegrationAvailable: Bool? = nil,
requestPublicPhotoSetup: (() -> Void)? = nil,
requestPublicPhotoRemove: (() -> Void)? = nil,
updated: @escaping (SelectivePrivacySettings, (SelectivePrivacySettings, VoiceCallSettings)?, Bool?) -> Void
) -> ViewController {
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
var initialEnableFor: [PeerId: SelectivePrivacyPeer] = [:]
@ -965,12 +1034,29 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}, setPublicPhoto: {
requestPublicPhotoSetup?()
}, removePublicPhoto: {
requestPublicPhotoRemove?()
})
let peer = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
let peer = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
let publicPhoto = context.account.postbox.peerView(id: context.account.peerId)
|> map { view -> TelegramMediaImage? in
if let cachedUserData = view.cachedData as? CachedUserData, case let .known(photo) = cachedUserData.fallbackPhoto {
return photo
} else {
return nil
}
}
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peer) |> deliverOnMainQueue
|> map { presentationData, state, peer -> (ItemListControllerState, (ItemListNodeState, Any)) in
let signal = combineLatest(
context.sharedContext.presentationData,
statePromise.get(),
peer,
publicPhoto
) |> deliverOnMainQueue
|> map { presentationData, state, peer, publicPhoto -> (ItemListControllerState, (ItemListNodeState, Any)) in
let peerName = peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
var phoneNumber = ""
if case let .user(user) = peer {
@ -995,7 +1081,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
title = presentationData.strings.Privacy_VoiceMessages
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName ?? "", phoneNumber: phoneNumber), style: .blocks, animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName ?? "", phoneNumber: phoneNumber, peer: peer, publicPhoto: publicPhoto), style: .blocks, animateChanges: false)
return (controllerState, (listState, arguments))
} |> afterDisposed {

View File

@ -427,6 +427,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[940666592] = { return Api.Message.parse_message($0) }
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
dict[721967202] = { return Api.Message.parse_messageService($0) }
dict[-404267113] = { return Api.MessageAction.parse_messageActionAttachMenuBotAllowed($0) }
dict[-1410748418] = { return Api.MessageAction.parse_messageActionBotAllowed($0) }
dict[-1781355374] = { return Api.MessageAction.parse_messageActionChannelCreate($0) }
dict[-365344535] = { return Api.MessageAction.parse_messageActionChannelMigrateFrom($0) }
@ -886,7 +887,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) }
dict[-1885878744] = { return Api.User.parse_user($0) }
dict[-742634630] = { return Api.User.parse_userEmpty($0) }
dict[-328384029] = { return Api.UserFull.parse_userFull($0) }
dict[-120378643] = { return Api.UserFull.parse_userFull($0) }
dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) }
dict[1326562017] = { return Api.UserProfilePhoto.parse_userProfilePhotoEmpty($0) }
dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) }

View File

@ -988,6 +988,7 @@ public extension Api {
}
public extension Api {
enum MessageAction: TypeConstructorDescription {
case messageActionAttachMenuBotAllowed
case messageActionBotAllowed(domain: String)
case messageActionChannelCreate(title: String)
case messageActionChannelMigrateFrom(title: String, chatId: Int64)
@ -1027,6 +1028,12 @@ public extension Api {
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messageActionAttachMenuBotAllowed:
if boxed {
buffer.appendInt32(-404267113)
}
break
case .messageActionBotAllowed(let domain):
if boxed {
buffer.appendInt32(-1410748418)
@ -1302,6 +1309,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messageActionAttachMenuBotAllowed:
return ("messageActionAttachMenuBotAllowed", [])
case .messageActionBotAllowed(let domain):
return ("messageActionBotAllowed", [("domain", String(describing: domain))])
case .messageActionChannelCreate(let title):
@ -1377,6 +1386,9 @@ public extension Api {
}
}
public static func parse_messageActionAttachMenuBotAllowed(_ reader: BufferReader) -> MessageAction? {
return Api.MessageAction.messageActionAttachMenuBotAllowed
}
public static func parse_messageActionBotAllowed(_ reader: BufferReader) -> MessageAction? {
var _1: String?
_1 = parseString(reader)

View File

@ -586,13 +586,13 @@ public extension Api {
}
public extension Api {
enum UserFull: TypeConstructorDescription {
case userFull(flags: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, premiumGifts: [Api.PremiumGiftOption]?)
case userFull(flags: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, fallbackPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, premiumGifts: [Api.PremiumGiftOption]?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts):
case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts):
if boxed {
buffer.appendInt32(-328384029)
buffer.appendInt32(-120378643)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
@ -600,6 +600,7 @@ public extension Api {
settings.serialize(buffer, true)
if Int(flags) & Int(1 << 21) != 0 {personalPhoto!.serialize(buffer, true)}
if Int(flags) & Int(1 << 2) != 0 {profilePhoto!.serialize(buffer, true)}
if Int(flags) & Int(1 << 22) != 0 {fallbackPhoto!.serialize(buffer, true)}
notifySettings.serialize(buffer, true)
if Int(flags) & Int(1 << 3) != 0 {botInfo!.serialize(buffer, true)}
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
@ -621,8 +622,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts):
return ("userFull", [("flags", String(describing: flags)), ("id", String(describing: id)), ("about", String(describing: about)), ("settings", String(describing: settings)), ("personalPhoto", String(describing: personalPhoto)), ("profilePhoto", String(describing: profilePhoto)), ("notifySettings", String(describing: notifySettings)), ("botInfo", String(describing: botInfo)), ("pinnedMsgId", String(describing: pinnedMsgId)), ("commonChatsCount", String(describing: commonChatsCount)), ("folderId", String(describing: folderId)), ("ttlPeriod", String(describing: ttlPeriod)), ("themeEmoticon", String(describing: themeEmoticon)), ("privateForwardName", String(describing: privateForwardName)), ("botGroupAdminRights", String(describing: botGroupAdminRights)), ("botBroadcastAdminRights", String(describing: botBroadcastAdminRights)), ("premiumGifts", String(describing: premiumGifts))])
case .userFull(let flags, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let premiumGifts):
return ("userFull", [("flags", String(describing: flags)), ("id", String(describing: id)), ("about", String(describing: about)), ("settings", String(describing: settings)), ("personalPhoto", String(describing: personalPhoto)), ("profilePhoto", String(describing: profilePhoto)), ("fallbackPhoto", String(describing: fallbackPhoto)), ("notifySettings", String(describing: notifySettings)), ("botInfo", String(describing: botInfo)), ("pinnedMsgId", String(describing: pinnedMsgId)), ("commonChatsCount", String(describing: commonChatsCount)), ("folderId", String(describing: folderId)), ("ttlPeriod", String(describing: ttlPeriod)), ("themeEmoticon", String(describing: themeEmoticon)), ("privateForwardName", String(describing: privateForwardName)), ("botGroupAdminRights", String(describing: botGroupAdminRights)), ("botBroadcastAdminRights", String(describing: botBroadcastAdminRights)), ("premiumGifts", String(describing: premiumGifts))])
}
}
@ -645,37 +646,41 @@ public extension Api {
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _7: Api.PeerNotifySettings?
var _7: Api.Photo?
if Int(_1!) & Int(1 << 22) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _8: Api.PeerNotifySettings?
if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
_8 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
}
var _8: Api.BotInfo?
var _9: Api.BotInfo?
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.BotInfo
_9 = Api.parse(reader, signature: signature) as? Api.BotInfo
} }
var _9: Int32?
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt32() }
var _10: Int32?
_10 = reader.readInt32()
if Int(_1!) & Int(1 << 6) != 0 {_10 = reader.readInt32() }
var _11: Int32?
if Int(_1!) & Int(1 << 11) != 0 {_11 = reader.readInt32() }
_11 = reader.readInt32()
var _12: Int32?
if Int(_1!) & Int(1 << 14) != 0 {_12 = reader.readInt32() }
var _13: String?
if Int(_1!) & Int(1 << 15) != 0 {_13 = parseString(reader) }
if Int(_1!) & Int(1 << 11) != 0 {_12 = reader.readInt32() }
var _13: Int32?
if Int(_1!) & Int(1 << 14) != 0 {_13 = reader.readInt32() }
var _14: String?
if Int(_1!) & Int(1 << 16) != 0 {_14 = parseString(reader) }
var _15: Api.ChatAdminRights?
if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() {
_15 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights
} }
if Int(_1!) & Int(1 << 15) != 0 {_14 = parseString(reader) }
var _15: String?
if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) }
var _16: Api.ChatAdminRights?
if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() {
if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() {
_16 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights
} }
var _17: [Api.PremiumGiftOption]?
var _17: Api.ChatAdminRights?
if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() {
_17 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights
} }
var _18: [Api.PremiumGiftOption]?
if Int(_1!) & Int(1 << 19) != 0 {if let _ = reader.readInt32() {
_17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumGiftOption.self)
_18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumGiftOption.self)
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
@ -683,19 +688,20 @@ public extension Api {
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 21) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
let _c10 = _10 != nil
let _c11 = (Int(_1!) & Int(1 << 11) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
let _c14 = (Int(_1!) & Int(1 << 16) == 0) || _14 != nil
let _c15 = (Int(_1!) & Int(1 << 17) == 0) || _15 != nil
let _c16 = (Int(_1!) & Int(1 << 18) == 0) || _16 != nil
let _c17 = (Int(_1!) & Int(1 << 19) == 0) || _17 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, personalPhoto: _5, profilePhoto: _6, notifySettings: _7!, botInfo: _8, pinnedMsgId: _9, commonChatsCount: _10!, folderId: _11, ttlPeriod: _12, themeEmoticon: _13, privateForwardName: _14, botGroupAdminRights: _15, botBroadcastAdminRights: _16, premiumGifts: _17)
let _c7 = (Int(_1!) & Int(1 << 22) == 0) || _7 != nil
let _c8 = _8 != nil
let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 6) == 0) || _10 != nil
let _c11 = _11 != nil
let _c12 = (Int(_1!) & Int(1 << 11) == 0) || _12 != nil
let _c13 = (Int(_1!) & Int(1 << 14) == 0) || _13 != nil
let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil
let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil
let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil
let _c17 = (Int(_1!) & Int(1 << 18) == 0) || _17 != nil
let _c18 = (Int(_1!) & Int(1 << 19) == 0) || _18 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 {
return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, personalPhoto: _5, profilePhoto: _6, fallbackPhoto: _7, notifySettings: _8!, botInfo: _9, pinnedMsgId: _10, commonChatsCount: _11!, folderId: _12, ttlPeriod: _13, themeEmoticon: _14, privateForwardName: _15, botGroupAdminRights: _16, botBroadcastAdminRights: _17, premiumGifts: _18)
}
else {
return nil

View File

@ -6559,12 +6559,13 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.messages {
static func toggleBotInAttachMenu(bot: Api.InputUser, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
static func toggleBotInAttachMenu(flags: Int32, bot: Api.InputUser, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(451818415)
buffer.appendInt32(1777704297)
serializeInt32(flags, buffer: buffer, boxed: false)
bot.serialize(buffer, true)
enabled.serialize(buffer, true)
return (FunctionDescription(name: "messages.toggleBotInAttachMenu", parameters: [("bot", String(describing: bot)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
return (FunctionDescription(name: "messages.toggleBotInAttachMenu", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
let reader = BufferReader(buffer)
var result: Api.Bool?
if let signature = reader.readInt32() {
@ -7548,11 +7549,12 @@ public extension Api.functions.photos {
}
}
public extension Api.functions.photos {
static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.photos.Photo>) {
static func updateProfilePhoto(flags: Int32, id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.photos.Photo>) {
let buffer = Buffer()
buffer.appendInt32(1926525996)
buffer.appendInt32(473782614)
serializeInt32(flags, buffer: buffer, boxed: false)
id.serialize(buffer, true)
return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("flags", String(describing: flags)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
let reader = BufferReader(buffer)
var result: Api.photos.Photo?
if let signature = reader.readInt32() {

View File

@ -1254,7 +1254,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
} else {
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(participant.peer), text: text, action: nil), action: { _ in return false })
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(participant.peer), text: text, action: nil, duration: 3), action: { _ in return false })
}
} else {
if case let .channel(groupPeer) = groupPeer, let listenerLink = inviteLinks?.listenerLink, !groupPeer.hasPermission(.inviteMembers) {
@ -1362,7 +1362,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
} else {
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil), action: { _ in return false })
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil, duration: 3), action: { _ in return false })
}
}))
} else if case let .legacyGroup(groupPeer) = groupPeer {
@ -1430,7 +1430,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
} else {
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil), action: { _ in return false })
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil, duration: 3), action: { _ in return false })
}
}))
}
@ -2262,7 +2262,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
return
}
let text = strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(EnginePeer(event.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(event.peer), text: text, action: nil), action: { _ in return false })
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(event.peer), text: text, action: nil, duration: 3), action: { _ in return false })
}
}))
@ -2277,7 +2277,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
} else {
text = strongSelf.presentationData.strings.VoiceChat_DisplayAsSuccess(EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
}
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(peer), text: text, action: nil), action: { _ in return false })
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(peer), text: text, action: nil, duration: 3), action: { _ in return false })
}))
self.stateVersionDisposable.set((self.call.stateVersion

View File

@ -76,6 +76,9 @@ extension ReplyMarkupMessageAttribute {
if (markupFlags & (1 << 2)) != 0 {
flags.insert(.personal)
}
if (markupFlags & (1 << 4)) != 0 {
flags.insert(.persistent)
}
placeholder = apiPlaceholder
case let .replyInlineMarkup(apiRows):
rows = apiRows.map { ReplyMarkupRow(apiRow: $0) }

View File

@ -205,7 +205,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
}
switch action {
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto:
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto, .messageActionAttachMenuBotAllowed:
break
case let .messageActionChannelMigrateFrom(_, chatId):
result.append(PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(chatId)))

View File

@ -106,6 +106,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
return TelegramMediaAction(action: .topicEdited(components: components))
case let.messageActionSuggestProfilePhoto(photo):
return TelegramMediaAction(action: .suggestedProfilePhoto(image: telegramMediaImageFromApiPhoto(photo)))
case .messageActionAttachMenuBotAllowed:
return TelegramMediaAction(action: .attachMenuBotAllowed)
}
}

View File

@ -153,6 +153,7 @@ public final class CachedUserData: CachedPeerData {
public let themeEmoticon: String?
public let photo: CachedPeerProfilePhoto
public let personalPhoto: CachedPeerProfilePhoto
public let fallbackPhoto: CachedPeerProfilePhoto
public let premiumGiftOptions: [CachedPremiumGiftOption]
public let voiceMessagesAvailable: Bool
@ -176,13 +177,14 @@ public final class CachedUserData: CachedPeerData {
self.themeEmoticon = nil
self.photo = .unknown
self.personalPhoto = .unknown
self.fallbackPhoto = .unknown
self.premiumGiftOptions = []
self.voiceMessagesAvailable = true
self.peerIds = Set()
self.messageIds = Set()
}
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, premiumGiftOptions: [CachedPremiumGiftOption], voiceMessagesAvailable: Bool) {
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, premiumGiftOptions: [CachedPremiumGiftOption], voiceMessagesAvailable: Bool) {
self.about = about
self.botInfo = botInfo
self.peerStatusSettings = peerStatusSettings
@ -198,6 +200,7 @@ public final class CachedUserData: CachedPeerData {
self.themeEmoticon = themeEmoticon
self.photo = photo
self.personalPhoto = personalPhoto
self.fallbackPhoto = fallbackPhoto
self.premiumGiftOptions = premiumGiftOptions
self.voiceMessagesAvailable = voiceMessagesAvailable
@ -237,6 +240,7 @@ public final class CachedUserData: CachedPeerData {
self.photo = decoder.decodeObjectForKey("phv", decoder: CachedPeerProfilePhoto.init(decoder:)) as? CachedPeerProfilePhoto ?? .unknown
self.personalPhoto = decoder.decodeObjectForKey("pphv", decoder: CachedPeerProfilePhoto.init(decoder:)) as? CachedPeerProfilePhoto ?? .unknown
self.fallbackPhoto = decoder.decodeObjectForKey("fphv", decoder: CachedPeerProfilePhoto.init(decoder:)) as? CachedPeerProfilePhoto ?? .unknown
self.premiumGiftOptions = decoder.decodeObjectArrayWithDecoderForKey("pgo") as [CachedPremiumGiftOption]
self.voiceMessagesAvailable = decoder.decodeInt32ForKey("vma", orElse: 0) != 0
@ -291,6 +295,7 @@ public final class CachedUserData: CachedPeerData {
encoder.encodeObject(self.photo, forKey: "phv")
encoder.encodeObject(self.personalPhoto, forKey: "pphv")
encoder.encodeObject(self.fallbackPhoto, forKey: "fphv")
encoder.encodeObjectArray(self.premiumGiftOptions, forKey: "pgo")
encoder.encodeInt32(self.voiceMessagesAvailable ? 1 : 0, forKey: "vma")
@ -308,74 +313,78 @@ public final class CachedUserData: CachedPeerData {
return false
}
return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.themeEmoticon == other.themeEmoticon && self.photo == other.photo && self.personalPhoto == other.personalPhoto && self.premiumGiftOptions == other.premiumGiftOptions && self.voiceMessagesAvailable == other.voiceMessagesAvailable
return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.themeEmoticon == other.themeEmoticon && self.photo == other.photo && self.personalPhoto == other.personalPhoto && self.fallbackPhoto == other.fallbackPhoto && self.premiumGiftOptions == other.premiumGiftOptions && self.voiceMessagesAvailable == other.voiceMessagesAvailable
}
public func withUpdatedAbout(_ about: String?) -> CachedUserData {
return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedPhoto(_ photo: CachedPeerProfilePhoto) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedPersonalPhoto(_ personalPhoto: CachedPeerProfilePhoto) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedFallbackPhoto(_ fallbackPhoto: CachedPeerProfilePhoto) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedPremiumGiftOptions(_ premiumGiftOptions: [CachedPremiumGiftOption]) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: premiumGiftOptions, voiceMessagesAvailable: self.voiceMessagesAvailable)
}
public func withUpdatedVoiceMessagesAvailable(_ voiceMessagesAvailable: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: voiceMessagesAvailable)
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, premiumGiftOptions: self.premiumGiftOptions, voiceMessagesAvailable: voiceMessagesAvailable)
}
}

View File

@ -158,6 +158,7 @@ public struct ReplyMarkupMessageFlags: OptionSet {
public static let setupReply = ReplyMarkupMessageFlags(rawValue: 1 << 2)
public static let inline = ReplyMarkupMessageFlags(rawValue: 1 << 3)
public static let fit = ReplyMarkupMessageFlags(rawValue: 1 << 4)
public static let persistent = ReplyMarkupMessageFlags(rawValue: 1 << 5)
}
public class ReplyMarkupMessageAttribute: MessageAttribute, Equatable {

View File

@ -99,6 +99,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case topicCreated(title: String, iconColor: Int32, iconFileId: Int64?)
case topicEdited(components: [ForumTopicEditComponent])
case suggestedProfilePhoto(image: TelegramMediaImage?)
case attachMenuBotAllowed
public init(decoder: PostboxDecoder) {
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
@ -175,6 +176,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
self = .topicEdited(components: decoder.decodeObjectArrayWithDecoderForKey("components"))
case 30:
self = .suggestedProfilePhoto(image: decoder.decodeObjectForKey("image") as? TelegramMediaImage)
case 31:
self = .attachMenuBotAllowed
default:
self = .unknown
}
@ -326,6 +329,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
if let image = image {
encoder.encodeObject(image, forKey: "image")
}
case .attachMenuBotAllowed:
encoder.encodeInt32(31, forKey: "_rawValue")
}
}

View File

@ -44,7 +44,7 @@ public extension TelegramEngine {
}
public func updateAccountPhoto(resource: MediaResource?, videoResource: MediaResource?, videoStartTimestamp: Double?, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return _internal_updateAccountPhoto(account: self.account, resource: resource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: mapResourceToAvatarSizes)
return _internal_updateAccountPhoto(account: self.account, resource: resource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, fallback: false, mapResourceToAvatarSizes: mapResourceToAvatarSizes)
}
public func updatePeerPhotoExisting(reference: TelegramMediaImageReference) -> Signal<TelegramMediaImage?, NoError> {
@ -52,7 +52,15 @@ public extension TelegramEngine {
}
public func removeAccountPhoto(reference: TelegramMediaImageReference?) -> Signal<Void, NoError> {
return _internal_removeAccountPhoto(network: self.account.network, reference: reference)
return _internal_removeAccountPhoto(account: self.account, reference: reference, fallback: false)
}
public func updateFallbackPhoto(resource: MediaResource?, videoResource: MediaResource?, videoStartTimestamp: Double?, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return _internal_updateAccountPhoto(account: self.account, resource: resource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, fallback: true, mapResourceToAvatarSizes: mapResourceToAvatarSizes)
}
public func removeFallbackPhoto(reference: TelegramMediaImageReference?) -> Signal<Void, NoError> {
return _internal_removeAccountPhoto(account: self.account, reference: reference, fallback: true)
}
public func setEmojiStatus(file: TelegramMediaFile?, expirationDate: Int32?) -> Signal<Never, NoError> {

View File

@ -11,6 +11,7 @@ public final class AttachMenuBots: Equatable, Codable {
case botIcons
case peerTypes
case hasSettings
case flags
}
public enum IconName: Int32, Codable {
@ -38,6 +39,21 @@ public final class AttachMenuBots: Equatable, Codable {
}
}
public struct Flags: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let hasSettings = Flags(rawValue: 1 << 0)
public static let requiresWriteAccess = Flags(rawValue: 1 << 1)
}
public struct PeerFlags: OptionSet, Codable {
public var rawValue: UInt32
@ -94,20 +110,20 @@ public final class AttachMenuBots: Equatable, Codable {
public let name: String
public let icons: [IconName: TelegramMediaFile]
public let peerTypes: PeerFlags
public let hasSettings: Bool
public let flags: Flags
public init(
peerId: PeerId,
name: String,
icons: [IconName: TelegramMediaFile],
peerTypes: PeerFlags,
hasSettings: Bool
flags: Flags
) {
self.peerId = peerId
self.name = name
self.icons = icons
self.peerTypes = peerTypes
self.hasSettings = hasSettings
self.flags = flags
}
public static func ==(lhs: Bot, rhs: Bot) -> Bool {
@ -123,7 +139,7 @@ public final class AttachMenuBots: Equatable, Codable {
if lhs.peerTypes != rhs.peerTypes {
return false
}
if lhs.hasSettings != rhs.hasSettings {
if lhs.flags != rhs.flags {
return false
}
return true
@ -147,7 +163,12 @@ public final class AttachMenuBots: Equatable, Codable {
let value = try container.decodeIfPresent(Int32.self, forKey: .peerTypes) ?? Int32(PeerFlags.default.rawValue)
self.peerTypes = PeerFlags(rawValue: UInt32(value))
self.hasSettings = try container.decodeIfPresent(Bool.self, forKey: .hasSettings) ?? false
if let flags = try container.decodeIfPresent(Int32.self, forKey: .flags) {
self.flags = Flags(rawValue: flags)
} else {
let hasSettings = try container.decodeIfPresent(Bool.self, forKey: .hasSettings) ?? false
self.flags = hasSettings ? [.hasSettings] : []
}
}
public func encode(to encoder: Encoder) throws {
@ -164,7 +185,7 @@ public final class AttachMenuBots: Equatable, Codable {
try container.encode(Int32(self.peerTypes.rawValue), forKey: .peerTypes)
try container.encode(self.hasSettings, forKey: .hasSettings)
try container.encode(Int32(self.flags.rawValue), forKey: .flags)
}
}
@ -276,7 +297,7 @@ func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network, force:
var resultBots: [AttachMenuBots.Bot] = []
for bot in bots {
switch bot {
case let .attachMenuBot(flags, botId, name, apiPeerTypes, botIcons):
case let .attachMenuBot(apiFlags, botId, name, apiPeerTypes, botIcons):
var icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile] = [:]
for icon in botIcons {
switch icon {
@ -302,7 +323,14 @@ func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network, force:
peerTypes.insert(.channel)
}
}
resultBots.append(AttachMenuBots.Bot(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), name: name, icons: icons, peerTypes: peerTypes, hasSettings: (flags & (1 << 1)) != 0))
var flags: AttachMenuBots.Bot.Flags = []
if (apiFlags & (1 << 1)) != 0 {
flags.insert(.hasSettings)
}
if (apiFlags & (1 << 2)) != 0 {
flags.insert(.requiresWriteAccess)
}
resultBots.append(AttachMenuBots.Bot(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), name: name, icons: icons, peerTypes: peerTypes, flags: flags))
}
}
}
@ -340,12 +368,16 @@ public enum AddBotToAttachMenuError {
}
func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: PeerId) -> Signal<Bool, AddBotToAttachMenuError> {
func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: PeerId, allowWrite: Bool) -> Signal<Bool, AddBotToAttachMenuError> {
return postbox.transaction { transaction -> Signal<Bool, AddBotToAttachMenuError> in
guard let peer = transaction.getPeer(botId), let inputUser = apiInputUser(peer) else {
return .complete()
}
return network.request(Api.functions.messages.toggleBotInAttachMenu(bot: inputUser, enabled: .boolTrue))
var flags: Int32 = 0
if allowWrite {
flags |= (1 << 0)
}
return network.request(Api.functions.messages.toggleBotInAttachMenu(flags: flags, bot: inputUser, enabled: .boolTrue))
|> map { value -> Bool in
switch value {
case .boolTrue:
@ -379,7 +411,7 @@ func _internal_removeBotFromAttachMenu(postbox: Postbox, network: Network, botId
guard let peer = transaction.getPeer(botId), let inputUser = apiInputUser(peer) else {
return .complete()
}
return network.request(Api.functions.messages.toggleBotInAttachMenu(bot: inputUser, enabled: .boolFalse))
return network.request(Api.functions.messages.toggleBotInAttachMenu(flags: 0, bot: inputUser, enabled: .boolFalse))
|> map { value -> Bool in
switch value {
case .boolTrue:
@ -406,14 +438,14 @@ public struct AttachMenuBot {
public let shortName: String
public let icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile]
public let peerTypes: AttachMenuBots.Bot.PeerFlags
public let hasSettings: Bool
public let flags: AttachMenuBots.Bot.Flags
init(peer: Peer, shortName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], peerTypes: AttachMenuBots.Bot.PeerFlags, hasSettings: Bool) {
init(peer: Peer, shortName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], peerTypes: AttachMenuBots.Bot.PeerFlags, flags: AttachMenuBots.Bot.Flags) {
self.peer = peer
self.shortName = shortName
self.icons = icons
self.peerTypes = peerTypes
self.hasSettings = hasSettings
self.flags = flags
}
}
@ -425,7 +457,7 @@ func _internal_attachMenuBots(postbox: Postbox) -> Signal<[AttachMenuBot], NoErr
var resultBots: [AttachMenuBot] = []
for bot in cachedBots {
if let peer = transaction.getPeer(bot.peerId) {
resultBots.append(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, hasSettings: bot.hasSettings))
resultBots.append(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags))
}
}
return resultBots
@ -440,7 +472,7 @@ public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId
return postbox.transaction { transaction -> Signal<AttachMenuBot, GetAttachMenuBotError> in
if cached, let cachedBots = cachedAttachMenuBots(transaction: transaction)?.bots {
if let bot = cachedBots.first(where: { $0.peerId == botId }), let peer = transaction.getPeer(bot.peerId) {
return .single(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, hasSettings: bot.hasSettings))
return .single(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags))
}
}
@ -474,7 +506,7 @@ public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId
}
switch bot {
case let .attachMenuBot(flags, _, name, apiPeerTypes, botIcons):
case let .attachMenuBot(apiFlags, _, name, apiPeerTypes, botIcons):
var icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile] = [:]
for icon in botIcons {
switch icon {
@ -499,7 +531,14 @@ public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId
peerTypes.insert(.channel)
}
}
return .single(AttachMenuBot(peer: peer, shortName: name, icons: icons, peerTypes: peerTypes, hasSettings: (flags & (1 << 1)) != 0))
var flags: AttachMenuBots.Bot.Flags = []
if (apiFlags & (1 << 1)) != 0 {
flags.insert(.hasSettings)
}
if (apiFlags & (1 << 2)) != 0 {
flags.insert(.requiresWriteAccess)
}
return .single(AttachMenuBot(peer: peer, shortName: name, icons: icons, peerTypes: peerTypes, flags: flags))
}
}
}

View File

@ -395,8 +395,8 @@ public extension TelegramEngine {
return _internal_sendWebViewData(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, botId: botId, buttonText: buttonText, data: data)
}
public func addBotToAttachMenu(botId: PeerId) -> Signal<Bool, AddBotToAttachMenuError> {
return _internal_addBotToAttachMenu(postbox: self.account.postbox, network: self.account.network, botId: botId)
public func addBotToAttachMenu(botId: PeerId, allowWrite: Bool) -> Signal<Bool, AddBotToAttachMenuError> {
return _internal_addBotToAttachMenu(postbox: self.account.postbox, network: self.account.network, botId: botId, allowWrite: allowWrite)
}
public func removeBotFromAttachMenu(botId: PeerId) -> Signal<Bool, NoError> {

View File

@ -14,8 +14,8 @@ public enum UploadPeerPhotoError {
case generic
}
func _internal_updateAccountPhoto(account: Account, resource: MediaResource?, videoResource: MediaResource?, videoStartTimestamp: Double?, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return _internal_updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: account.peerId, photo: resource.flatMap({ _internal_uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: $0) }), video: videoResource.flatMap({ _internal_uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: $0) |> map(Optional.init) }), videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: mapResourceToAvatarSizes)
func _internal_updateAccountPhoto(account: Account, resource: MediaResource?, videoResource: MediaResource?, videoStartTimestamp: Double?, fallback: Bool, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return _internal_updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: account.peerId, photo: resource.flatMap({ _internal_uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: $0) }), video: videoResource.flatMap({ _internal_uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: $0) |> map(Optional.init) }), videoStartTimestamp: videoStartTimestamp, fallback: fallback, mapResourceToAvatarSizes: mapResourceToAvatarSizes)
}
public enum SetCustomPeerPhotoMode {
@ -76,11 +76,11 @@ func _internal_uploadedPeerVideo(postbox: Postbox, network: Network, messageMedi
}
}
func _internal_updatePeerPhoto(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peerId: PeerId, photo: Signal<UploadedPeerPhotoData, NoError>?, video: Signal<UploadedPeerPhotoData?, NoError>? = nil, videoStartTimestamp: Double? = nil, customPeerPhotoMode: SetCustomPeerPhotoMode? = nil, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return _internal_updatePeerPhotoInternal(postbox: postbox, network: network, stateManager: stateManager, accountPeerId: accountPeerId, peer: postbox.loadedPeerWithId(peerId), photo: photo, video: video, videoStartTimestamp: videoStartTimestamp, customPeerPhotoMode: customPeerPhotoMode, mapResourceToAvatarSizes: mapResourceToAvatarSizes)
func _internal_updatePeerPhoto(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peerId: PeerId, photo: Signal<UploadedPeerPhotoData, NoError>?, video: Signal<UploadedPeerPhotoData?, NoError>? = nil, videoStartTimestamp: Double? = nil, fallback: Bool = false, customPeerPhotoMode: SetCustomPeerPhotoMode? = nil, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return _internal_updatePeerPhotoInternal(postbox: postbox, network: network, stateManager: stateManager, accountPeerId: accountPeerId, peer: postbox.loadedPeerWithId(peerId), photo: photo, video: video, videoStartTimestamp: videoStartTimestamp, fallback: fallback, customPeerPhotoMode: customPeerPhotoMode, mapResourceToAvatarSizes: mapResourceToAvatarSizes)
}
func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peer: Signal<Peer, NoError>, photo: Signal<UploadedPeerPhotoData, NoError>?, video: Signal<UploadedPeerPhotoData?, NoError>?, videoStartTimestamp: Double?, customPeerPhotoMode: SetCustomPeerPhotoMode? = nil, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peer: Signal<Peer, NoError>, photo: Signal<UploadedPeerPhotoData, NoError>?, video: Signal<UploadedPeerPhotoData?, NoError>?, videoStartTimestamp: Double?, fallback: Bool = false, customPeerPhotoMode: SetCustomPeerPhotoMode? = nil, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return peer
|> mapError { _ -> UploadPeerPhotoError in }
|> mapToSignal { peer -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
@ -150,9 +150,11 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
flags |= (1 << 2)
}
}
let request: Signal<Api.photos.Photo, MTRpcError>
if peer.id == accountPeerId {
if fallback {
flags |= (1 << 3)
}
request = network.request(Api.functions.photos.uploadProfilePhoto(flags: flags, file: file, video: videoFile, videoStartTs: videoStartTimestamp))
} else if let inputUser = apiInputUser(peer) {
if let customPeerPhotoMode = customPeerPhotoMode {
@ -177,8 +179,10 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
|> mapToSignal { photo -> Signal<(UpdatePeerPhotoStatus, MediaResource?, MediaResource?), UploadPeerPhotoError> in
var representations: [TelegramMediaImageRepresentation] = []
var videoRepresentations: [TelegramMediaImage.VideoRepresentation] = []
var image: TelegramMediaImage?
switch photo {
case let .photo(apiPhoto, _):
image = telegramMediaImageFromApiPhoto(apiPhoto)
switch apiPhoto {
case .photoEmpty:
break
@ -225,7 +229,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
if let peer = transaction.getPeer(peer.id) {
updatePeers(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in
if let peer = peer as? TelegramUser {
if customPeerPhotoMode == .suggest {
if customPeerPhotoMode == .suggest || fallback {
return peer
} else {
return peer.withUpdatedPhoto(representations)
@ -234,6 +238,16 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
return peer
}
})
if fallback {
transaction.updatePeerCachedData(peerIds: Set([peer.id])) { peerId, cachedPeerData in
if let cachedPeerData = cachedPeerData as? CachedUserData {
return cachedPeerData.withUpdatedFallbackPhoto(.known(image))
} else {
return nil
}
}
}
}
return (.complete(representations), photoResult.resource, videoResult?.resource)
} |> mapError { _ -> UploadPeerPhotoError in }
@ -327,7 +341,11 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
if let _ = peer as? TelegramUser {
let request: Signal<Api.photos.Photo, MTRpcError>
if peer.id == accountPeerId {
request = network.request(Api.functions.photos.updateProfilePhoto(id: Api.InputPhoto.inputPhotoEmpty))
var flags: Int32 = 0
if fallback {
flags |= (1 << 0)
}
request = network.request(Api.functions.photos.updateProfilePhoto(flags: flags, id: Api.InputPhoto.inputPhotoEmpty))
} else if let inputUser = apiInputUser(peer) {
let flags: Int32 = (1 << 4)
request = network.request(Api.functions.photos.uploadContactProfilePhoto(flags: flags, userId: inputUser, file: nil, video: nil, videoStartTs: nil))
@ -350,6 +368,15 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
updatePeers(transaction: transaction, peers: updatedUsers, update: { (_, updatedPeer) -> Peer? in
return updatedPeer
})
if fallback {
transaction.updatePeerCachedData(peerIds: Set([peer.id])) { peerId, cachedPeerData in
if let cachedPeerData = cachedPeerData as? CachedUserData {
return cachedPeerData.withUpdatedFallbackPhoto(.known(nil))
} else {
return nil
}
}
}
return .complete([])
} |> mapError { _ -> UploadPeerPhotoError in }
}
@ -405,7 +432,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state
func _internal_updatePeerPhotoExisting(network: Network, reference: TelegramMediaImageReference) -> Signal<TelegramMediaImage?, NoError> {
switch reference {
case let .cloud(imageId, accessHash, fileReference):
return network.request(Api.functions.photos.updateProfilePhoto(id: .inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))))
return network.request(Api.functions.photos.updateProfilePhoto(flags: 0, id: .inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))))
|> `catch` { _ -> Signal<Api.photos.Photo, NoError> in
return .complete()
}
@ -419,12 +446,12 @@ func _internal_updatePeerPhotoExisting(network: Network, reference: TelegramMedi
}
}
func _internal_removeAccountPhoto(network: Network, reference: TelegramMediaImageReference?) -> Signal<Void, NoError> {
func _internal_removeAccountPhoto(account: Account, reference: TelegramMediaImageReference?, fallback: Bool) -> Signal<Void, NoError> {
if let reference = reference {
switch reference {
case let .cloud(imageId, accessHash, fileReference):
if let fileReference = fileReference {
return network.request(Api.functions.photos.deletePhotos(id: [.inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))]))
return account.network.request(Api.functions.photos.deletePhotos(id: [.inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))]))
|> `catch` { _ -> Signal<[Int64], NoError> in
return .single([])
}
@ -436,7 +463,29 @@ func _internal_removeAccountPhoto(network: Network, reference: TelegramMediaImag
}
}
} else {
let api = Api.functions.photos.updateProfilePhoto(id: Api.InputPhoto.inputPhotoEmpty)
return network.request(api) |> map { _ in } |> retryRequest
var flags: Int32 = 0
if fallback {
flags |= (1 << 0)
}
let api = Api.functions.photos.updateProfilePhoto(flags: flags, id: Api.InputPhoto.inputPhotoEmpty)
return account.network.request(api)
|> map { _ in }
|> retryRequest
|> mapToSignal { _ -> Signal<Void, NoError> in
if fallback {
return account.postbox.transaction { transaction -> Void in
transaction.updatePeerCachedData(peerIds: Set([account.peerId]), update: { _, current in
if let current = current as? CachedUserData {
return current.withUpdatedFallbackPhoto(.known(nil))
} else {
return current
}
})
return Void()
}
} else {
return .complete()
}
}
}
}

View File

@ -212,7 +212,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
}
switch fullUser {
case let .userFull(_, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _):
case let .userFull(_, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _):
updatePeers(transaction: transaction, peers: peers, update: { previous, updated -> Peer in
if previous?.id == accountPeerId, let accountUser = accountUser, let user = TelegramUser.merge(previous as? TelegramUser, rhs: accountUser) {
return user
@ -230,7 +230,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
previous = CachedUserData()
}
switch fullUser {
case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, userPremiumGiftOptions):
case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, fallbackPhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, userPremiumGiftOptions):
let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:))
let isBlocked = (userFullFlags & (1 << 0)) != 0
let voiceCallsAvailable = (userFullFlags & (1 << 4)) != 0
@ -250,6 +250,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
let personalPhoto = personalPhoto.flatMap { telegramMediaImageFromApiPhoto($0) }
let photo = profilePhoto.flatMap { telegramMediaImageFromApiPhoto($0) }
let fallbackPhoto = fallbackPhoto.flatMap { telegramMediaImageFromApiPhoto($0) }
let premiumGiftOptions: [CachedPremiumGiftOption]
if let userPremiumGiftOptions = userPremiumGiftOptions {
@ -270,6 +271,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
.withUpdatedThemeEmoticon(userFullThemeEmoticon)
.withUpdatedPhoto(.known(photo))
.withUpdatedPersonalPhoto(.known(personalPhoto))
.withUpdatedFallbackPhoto(.known(fallbackPhoto))
.withUpdatedPremiumGiftOptions(premiumGiftOptions)
.withUpdatedVoiceMessagesAvailable(voiceMessagesAvailable)
}

View File

@ -51,6 +51,7 @@ public enum PresentationResourceKey: Int32 {
case itemListCreateGroupIcon
case itemListAddExceptionIcon
case itemListAddPhoneIcon
case itemListAddPhotoIcon
case itemListClearInputIcon
case itemListStickerItemUnreadDot
case itemListVerifiedPeerIcon

View File

@ -169,6 +169,12 @@ public struct PresentationResourcesItemList {
})
}
public static func addPhotoIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListAddPhotoIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Settings/SetAvatar"), color: theme.list.itemAccentColor)
})
}
public static func itemListClearInputIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListClearInputIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.list.inputClearButtonColor)

View File

@ -831,6 +831,8 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} else {
attributedString = NSAttributedString(string: strings.Notification_SuggestedProfileVideo, font: titleFont, textColor: primaryTextColor)
}
case .attachMenuBotAllowed:
attributedString = NSAttributedString(string: strings.Notification_BotWriteAllowed, font: titleFont, textColor: primaryTextColor)
case .unknown:
attributedString = nil
}

View File

@ -9518,7 +9518,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let hapticFeedback = HapticFeedback()
hapticFeedback.impact()
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action), elevatedLayout: false, action: { [weak self] action in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action, duration: 3), elevatedLayout: false, action: { [weak self] action in
guard let strongSelf = self else {
return true
}
@ -12063,8 +12063,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = (context.engine.messages.getAttachMenuBot(botId: botId)
|> deliverOnMainQueue).start(next: { bot in
let peer = EnginePeer(bot.peer)
let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), icons: bot.icons, completion: {
let _ = (context.engine.messages.addBotToAttachMenu(botId: botId)
let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), icons: bot.icons, requestWriteAccess: bot.flags.contains(.requiresWriteAccess), completion: { allowWrite in
let _ = (context.engine.messages.addBotToAttachMenu(botId: botId, allowWrite: allowWrite)
|> deliverOnMainQueue).start(error: { _ in
}, completed: {

View File

@ -1993,7 +1993,9 @@ private final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
let _ = strongSelf.controllerInteraction.sendSticker(fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets)
}
if let chatPeerId = strongSelf.chatPeerId {
let isLocked = file.isPremiumSticker && !hasPremium
if let chatPeerId = strongSelf.chatPeerId, !isLocked {
if chatPeerId != strongSelf.context.account.peerId && chatPeerId.namespace != Namespaces.Peer.SecretChat {
menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendSilently, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.actionSheet.primaryTextColor)
@ -2093,8 +2095,8 @@ private final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
guard let view = view else {
return nil
}
return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: {
return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked && !isStarred, menu: menuItems, openPremiumIntro: {
guard let strongSelf = self else {
return
}

View File

@ -632,8 +632,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
let choose = filterChooseTypes(choose, peerTypes: bot.peerTypes)
let botPeer = EnginePeer(bot.peer)
let controller = addWebAppToAttachmentController(context: context, peerName: botPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), icons: bot.icons, completion: {
let _ = (context.engine.messages.addBotToAttachMenu(botId: peerId)
let controller = addWebAppToAttachmentController(context: context, peerName: botPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), icons: bot.icons, requestWriteAccess: bot.flags.contains(.requiresWriteAccess), completion: { allowWrite in
let _ = (context.engine.messages.addBotToAttachMenu(botId: peerId, allowWrite: allowWrite)
|> deliverOnMainQueue).start(error: { _ in
presentError(presentationData.strings.WebApp_AddToAttachmentUnavailableError)
}, completed: {

View File

@ -399,7 +399,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
if peer.isDeleted {
overrideImage = .deletedIcon
} else if let previousItem = previousItem, item == nil {
if case let .image(_, representations, _, _) = previousItem, let rep = representations.last {
if case let .image(_, representations, _, _, _) = previousItem, let rep = representations.last {
self.removedPhotoResourceIds.insert(rep.representation.resource.id.stringRepresentation)
}
overrideImage = AvatarNodeImageOverride.none
@ -499,7 +499,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource {
videoId = videoId &+ resource.photoId
}
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _):
representations = imageRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail
@ -784,7 +784,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
if canEdit, peer.profileImageRepresentations.isEmpty {
overrideImage = .editAvatarIcon(forceNone: true)
} else if let previousItem = previousItem, item == nil {
if case let .image(_, representations, _, _) = previousItem, let rep = representations.last {
if case let .image(_, representations, _, _, _) = previousItem, let rep = representations.last {
self.removedPhotoResourceIds.insert(rep.representation.resource.id.stringRepresentation)
}
overrideImage = canEdit ? .editAvatarIcon(forceNone: true) : AvatarNodeImageOverride.none
@ -831,7 +831,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource {
id = id &+ resource.photoId
}
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail):
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _):
representations = imageRepresentations
videoRepresentations = videoRepresentationsValue
immediateThumbnailData = immediateThumbnail

View File

@ -1359,7 +1359,8 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
let ItemSuggest = 0
let ItemCustom = 1
let ItemReset = 2
let ItemDelete = 3
let ItemInfo = 3
let ItemDelete = 4
let compactName = EnginePeer(user).compactDisplayTitle
@ -1381,6 +1382,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
interaction.resetCustomPhoto()
}))
}
items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemInfo, text: presentationData.strings.UserInfo_CustomPhotoInfo(compactName).string))
if data.isContact {
items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemDelete, text: presentationData.strings.UserInfo_DeleteContact, color: .destructive, action: {
@ -3476,7 +3478,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
var currentIsVideo = false
let item = strongSelf.headerNode.avatarListNode.listContainerNode.currentItemNode?.item
if let item = item, case let .image(_, _, videoRepresentations, _) = item {
if let item = item, case let .image(_, _, videoRepresentations, _, _) = item {
currentIsVideo = !videoRepresentations.isEmpty
}
@ -6754,7 +6756,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false)
if case .suggest = mode {
if [.suggest, .fallback].contains(mode) {
} else {
self.state = self.state.withUpdatingAvatar(.image(representation))
}
@ -6767,9 +6769,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let postbox = self.context.account.postbox
let signal: Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError>
if self.isSettings {
signal = self.context.engine.accountData.updateAccountPhoto(resource: resource, videoResource: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
})
if case .fallback = mode {
signal = self.context.engine.accountData.updateFallbackPhoto(resource: resource, videoResource: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
})
} else {
signal = self.context.engine.accountData.updateAccountPhoto(resource: resource, videoResource: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
})
}
} else if case .custom = mode {
signal = self.context.engine.contacts.updateContactPhoto(peerId: self.peerId, resource: resource, videoResource: nil, videoStartTimestamp: nil, mode: .custom, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
@ -6803,7 +6811,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, let peer {
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessPhotoText(peer.compactDisplayTitle).string, action: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessPhotoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}
})
}
@ -6826,7 +6834,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false)
if case .suggest = mode {
if [.suggest, .fallback].contains(mode) {
} else {
self.state = self.state.withUpdatingAvatar(.image(representation))
}
@ -6927,9 +6935,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self.updateAvatarDisposable.set((signal
|> mapToSignal { videoResource -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
if isSettings {
return context.engine.accountData.updateAccountPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
})
if case .fallback = mode {
return context.engine.accountData.updateFallbackPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
})
} else {
return context.engine.accountData.updateAccountPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
})
}
} else if case .custom = mode {
return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mode: .custom, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
@ -6962,7 +6976,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, let peer {
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}
})
}
@ -6973,16 +6987,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
case generic
case suggest
case custom
case fallback
}
private func openAvatarForEditing(mode: AvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping () -> Void = {}) {
fileprivate func openAvatarForEditing(mode: AvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping () -> Void = {}) {
guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else {
return
}
var currentIsVideo = false
let item = self.headerNode.avatarListNode.listContainerNode.currentItemNode?.item
if let item = item, case let .image(_, _, videoRepresentations, _) = item {
if let item = item, case let .image(_, _, videoRepresentations, _, _) = item {
currentIsVideo = !videoRepresentations.isEmpty
}
@ -7009,7 +7024,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
legacyController.bind(controller: navigationController)
strongSelf.view.endEditing(true)
strongSelf.controller?.present(legacyController, in: .window(.root))
(strongSelf.controller?.navigationController?.topViewController as? ViewController)?.present(legacyController, in: .window(.root))
var hasPhotos = false
if !peer.profileImageRepresentations.isEmpty {
@ -7026,7 +7041,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
return true
})
strongSelf.controller?.present(controller, in: .window(.root))
(strongSelf.controller?.navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root))
return controller
}
@ -7040,6 +7055,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
hasDeleteButton = hasPhotos && !fromGallery
} else if case .custom = mode {
hasDeleteButton = peer.profileImageRepresentations.first?.isPersonal == true
} else if case .fallback = mode {
if let cachedData = strongSelf.data?.cachedData as? CachedUserData, case let .known(photo) = cachedData.fallbackPhoto {
hasDeleteButton = photo != nil
}
}
let title: String?
@ -7076,7 +7095,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self?.updateProfilePhoto(result, mode: mode)
}))
controller.navigationPresentation = .modal
strongSelf.controller?.push(controller)
(strongSelf.controller?.navigationController?.topViewController as? ViewController)?.push(controller)
if fromGallery {
completion()
@ -7088,7 +7107,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let controller = photoUpdateConfirmationController(context: strongSelf.context, peer: peer, image: image, text: confirmationTextPhoto, doneTitle: confirmationAction, commit: {
commit?()
})
strongSelf.controller?.presentInGlobalOverlay(controller)
(strongSelf.controller?.navigationController?.topViewController as? ViewController)?.presentInGlobalOverlay(controller)
}
}
}
@ -7098,7 +7117,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let controller = photoUpdateConfirmationController(context: strongSelf.context, peer: peer, image: image, text: confirmationTextVideo, doneTitle: confirmationAction, commit: {
commit?()
})
strongSelf.controller?.presentInGlobalOverlay(controller)
(strongSelf.controller?.navigationController?.topViewController as? ViewController)?.presentInGlobalOverlay(controller)
}
}
}
@ -7119,55 +7138,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return
}
let proceed = {
if let item = item {
strongSelf.deleteProfilePhoto(item)
}
let _ = strongSelf.currentAvatarMixin.swap(nil)
if let _ = peer.smallProfileImage {
strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
}
let postbox = strongSelf.context.account.postbox
strongSelf.updateAvatarDisposable.set((strongSelf.context.engine.peers.updatePeerPhoto(peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
})
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
switch result {
case .complete:
strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
case .progress:
break
}
}))
}
let actionSheet = ActionSheetController(presentationData: presentationData)
let items: [ActionSheetItem] = [
ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
proceed()
})
]
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
strongSelf.controller?.present(actionSheet, in: .window(.root))
strongSelf.openAvatarRemoval(mode: mode, peer: peer, item: item)
}
mixin.didDismiss = { [weak legacyController] in
guard let strongSelf = self else {
@ -7185,6 +7156,79 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
})
}
fileprivate func openAvatarRemoval(mode: AvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil) {
let proceed = { [weak self] in
guard let strongSelf = self else {
return
}
if let item = item {
strongSelf.deleteProfilePhoto(item)
}
let _ = strongSelf.currentAvatarMixin.swap(nil)
if mode != .fallback {
if let peer = peer, let _ = peer.smallProfileImage {
strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
}
}
let postbox = strongSelf.context.account.postbox
let signal: Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError>
if case .custom = mode {
signal = strongSelf.context.engine.contacts.updateContactPhoto(peerId: strongSelf.peerId, resource: nil, videoResource: nil, videoStartTimestamp: nil, mode: .custom, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
})
} else if case .fallback = mode {
signal = strongSelf.context.engine.accountData.removeFallbackPhoto(reference: nil)
|> castError(UploadPeerPhotoError.self)
|> map { _ in
return .complete([])
}
} else {
signal = strongSelf.context.engine.peers.updatePeerPhoto(peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
})
}
strongSelf.updateAvatarDisposable.set((signal
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
switch result {
case .complete:
strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
case .progress:
break
}
}))
}
let presentationData = self.presentationData
let actionSheet = ActionSheetController(presentationData: presentationData)
let items: [ActionSheetItem] = [
ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
proceed()
})
]
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
(self.controller?.navigationController?.topViewController as? ViewController)?.present(actionSheet, in: .window(.root))
}
private func openAddMember() {
guard let data = self.data, let groupPeer = data.peer, let controller = self.controller else {
return
@ -7290,6 +7334,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
)
}
}, requestPublicPhotoSetup: { [weak self] in
if let strongSelf = self {
strongSelf.openAvatarForEditing(mode: .fallback)
}
}, requestPublicPhotoRemove: { [weak self] in
if let strongSelf = self {
strongSelf.openAvatarRemoval(mode: .fallback)
}
}))
}
})
@ -9322,18 +9374,18 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
}
func updateProfilePhoto(_ image: UIImage) {
func updateProfilePhoto(_ image: UIImage, fallback: Bool = false) {
if !self.isNodeLoaded {
self.loadDisplayNode()
}
self.controllerNode.updateProfilePhoto(image, mode: .generic)
self.controllerNode.updateProfilePhoto(image, mode: fallback ? .fallback : .generic)
}
func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?) {
func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?, fallback: Bool = false) {
if !self.isNodeLoaded {
self.loadDisplayNode()
}
self.controllerNode.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: .generic)
self.controllerNode.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: fallback ? .fallback : .generic)
}
static func displayChatNavigationMenu(context: AccountContext, chatNavigationStack: [ChatNavigationStackItem], nextFolderId: Int32?, parentController: ViewController, backButtonView: UIView, navigationController: NavigationController, gesture: ContextGesture) {

View File

@ -22,7 +22,7 @@ public enum UndoOverlayContent {
case chatRemovedFromFolder(chatTitle: String, folderTitle: String)
case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool)
case setProximityAlert(title: String, text: String, cancelled: Bool)
case invitedToVoiceChat(context: AccountContext, peer: EnginePeer, text: String, action: String?)
case invitedToVoiceChat(context: AccountContext, peer: EnginePeer, text: String, action: String?, duration: Double)
case linkCopied(text: String)
case banned(text: String)
case importedMessage(text: String)

View File

@ -518,7 +518,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
displayUndo = false
self.originalRemainingSeconds = 3
case let .invitedToVoiceChat(context, peer, text, action):
case let .invitedToVoiceChat(context, peer, text, action, duration):
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 15.0))
self.iconNode = nil
self.iconCheckNode = nil
@ -536,11 +536,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
if let action = action {
displayUndo = true
undoText = action
self.originalRemainingSeconds = 5
} else {
displayUndo = false
self.originalRemainingSeconds = 3
}
self.originalRemainingSeconds = duration
case let .audioRate(slowdown, text):
self.avatarNode = nil
self.iconNode = nil

View File

@ -28,6 +28,8 @@ swift_library(
"//submodules/BotPaymentsUI:BotPaymentsUI",
"//submodules/PromptUI:PromptUI",
"//submodules/PhoneNumberFormat:PhoneNumberFormat",
"//submodules/QrCodeUI:QrCodeUI",
"//submodules/InstantPageUI:InstantPageUI",
],
visibility = [
"//visibility:public",

View File

@ -10,6 +10,16 @@ import TelegramUIPreferences
import AccountContext
import AppBundle
import PhotoResources
import CheckNode
import Markdown
private let textFont = Font.regular(13.0)
private let boldTextFont = Font.semibold(13.0)
private func formattedText(_ text: String, color: UIColor, textAlignment: NSTextAlignment = .natural) -> NSAttributedString {
return parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: color), bold: MarkdownAttributeSet(font: boldTextFont, textColor: color), link: MarkdownAttributeSet(font: textFont, textColor: color), linkAttribute: { _ in return nil}), textAlignment: textAlignment)
}
private final class WebAppAlertContentNode: AlertContentNode {
private let strings: PresentationStrings
@ -20,6 +30,9 @@ private final class WebAppAlertContentNode: AlertContentNode {
private let appIconNode: ASImageNode
private let iconNode: ASImageNode
private let allowWriteCheckNode: InteractiveCheckNode
private let allowWriteLabelNode: ASTextNode
private let actionNodesSeparator: ASDisplayNode
private let actionNodes: [TextAlertContentActionNode]
private let actionVerticalSeparators: [ASDisplayNode]
@ -32,7 +45,13 @@ private final class WebAppAlertContentNode: AlertContentNode {
return self.isUserInteractionEnabled
}
init(account: Account, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, peerName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], actions: [TextAlertAction]) {
var allowWriteAccess: Bool = true {
didSet {
self.allowWriteCheckNode.setSelected(self.allowWriteAccess, animated: true)
}
}
init(account: Account, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, peerName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], requestWriteAccess: Bool, actions: [TextAlertAction]) {
self.strings = strings
self.peerName = peerName
@ -54,6 +73,12 @@ private final class WebAppAlertContentNode: AlertContentNode {
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
self.allowWriteCheckNode = InteractiveCheckNode(theme: CheckNodeTheme(backgroundColor: theme.accentColor, strokeColor: theme.contrastColor, borderColor: theme.controlBorderColor, overlayBorder: false, hasInset: false, hasShadow: false))
self.allowWriteCheckNode.setSelected(true, animated: false)
self.allowWriteLabelNode = ASTextNode()
self.allowWriteLabelNode.maximumNumberOfLines = 4
self.allowWriteLabelNode.isUserInteractionEnabled = true
self.actionNodesSeparator = ASDisplayNode()
self.actionNodesSeparator.isLayerBacked = true
@ -77,6 +102,12 @@ private final class WebAppAlertContentNode: AlertContentNode {
self.addSubnode(self.textNode)
self.addSubnode(self.appIconNode)
self.addSubnode(self.iconNode)
if requestWriteAccess {
self.addSubnode(self.allowWriteCheckNode)
self.addSubnode(self.allowWriteLabelNode)
}
self.addSubnode(self.actionNodesSeparator)
@ -88,6 +119,12 @@ private final class WebAppAlertContentNode: AlertContentNode {
self.addSubnode(separatorNode)
}
self.allowWriteCheckNode.valueChanged = { [weak self] value in
if let strongSelf = self {
strongSelf.allowWriteAccess = !strongSelf.allowWriteAccess
}
}
self.updateTheme(theme)
if let peerIcon = self.peerIcon {
@ -109,12 +146,26 @@ private final class WebAppAlertContentNode: AlertContentNode {
self.iconDisposable?.dispose()
}
override func didLoad() {
super.didLoad()
self.allowWriteLabelNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.allowWriteTap(_:))))
}
@objc private func allowWriteTap(_ gestureRecognizer: UITapGestureRecognizer) {
if self.allowWriteCheckNode.isUserInteractionEnabled {
self.allowWriteAccess = !self.allowWriteAccess
}
}
override func updateTheme(_ theme: AlertControllerTheme) {
self.textNode.attributedText = NSAttributedString(string: strings.WebApp_AddToAttachmentText(self.peerName).string, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.appIconNode.image = generateTintedImage(image: self.appIconNode.image, color: theme.accentColor)
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/BotPlus"), color: theme.accentColor)
self.allowWriteLabelNode.attributedText = formattedText(strings.WebApp_AddToAttachmentAllowMessages(self.peerName).string, color: theme.primaryColor)
self.actionNodesSeparator.backgroundColor = theme.separatorColor
for actionNode in self.actionNodes {
actionNode.updateTheme(theme)
@ -146,6 +197,23 @@ private final class WebAppAlertContentNode: AlertContentNode {
let textSize = self.textNode.measure(size)
var textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)
origin.y += textSize.height
var entriesHeight: CGFloat = 0.0
if self.allowWriteLabelNode.supernode != nil {
origin.y += 16.0
entriesHeight += 16.0
let checkSize = CGSize(width: 22.0, height: 22.0)
let condensedSize = CGSize(width: size.width - 76.0, height: size.height)
let allowWriteSize = self.allowWriteLabelNode.measure(condensedSize)
transition.updateFrame(node: self.allowWriteLabelNode, frame: CGRect(origin: CGPoint(x: 46.0, y: origin.y), size: allowWriteSize))
transition.updateFrame(node: self.allowWriteCheckNode, frame: CGRect(origin: CGPoint(x: 12.0, y: origin.y - 2.0), size: checkSize))
origin.y += allowWriteSize.height
entriesHeight += allowWriteSize.height
}
let actionButtonHeight: CGFloat = 44.0
var minActionsWidth: CGFloat = 0.0
@ -180,7 +248,7 @@ private final class WebAppAlertContentNode: AlertContentNode {
}
let resultWidth = contentWidth + insets.left + insets.right
let resultSize = CGSize(width: resultWidth, height: iconSize.height + textSize.height + actionsHeight + 17.0 + insets.top + insets.bottom)
let resultSize = CGSize(width: resultWidth, height: iconSize.height + textSize.height + entriesHeight + actionsHeight + 17.0 + insets.top + insets.bottom)
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
@ -227,7 +295,7 @@ private final class WebAppAlertContentNode: AlertContentNode {
nodeIndex += 1
}
iconFrame.origin.x = floorToScreenPixels((resultSize.width - iconFrame.width) / 2.0) + 19.0
iconFrame.origin.x = floorToScreenPixels((resultSize.width - iconFrame.width) / 2.0) + 21.0
transition.updateFrame(node: self.appIconNode, frame: CGRect(x: iconFrame.minX - 50.0, y: iconFrame.minY + 3.0, width: 42.0, height: 42.0))
transition.updateFrame(node: self.iconNode, frame: iconFrame)
@ -239,7 +307,7 @@ private final class WebAppAlertContentNode: AlertContentNode {
}
}
public func addWebAppToAttachmentController(context: AccountContext, peerName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], completion: @escaping () -> Void) -> AlertController {
public func addWebAppToAttachmentController(context: AccountContext, peerName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], requestWriteAccess: Bool, completion: @escaping (Bool) -> Void) -> AlertController {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let theme = presentationData.theme
let strings = presentationData.strings
@ -251,10 +319,10 @@ public func addWebAppToAttachmentController(context: AccountContext, peerName: S
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.WebApp_AddToAttachmentAdd, action: {
dismissImpl?(true)
completion()
completion(true)
})]
contentNode = WebAppAlertContentNode(account: context.account, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, peerName: peerName, icons: icons, actions: actions)
contentNode = WebAppAlertContentNode(account: context.account, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, peerName: peerName, icons: icons, requestWriteAccess: requestWriteAccess, actions: actions)
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!)
dismissImpl = { [weak controller] animated in

View File

@ -21,6 +21,8 @@ import MoreButtonNode
import BotPaymentsUI
import PromptUI
import PhoneNumberFormat
import QrCodeUI
import InstantPageUI
private let durgerKingBotIds: [Int64] = [5104055776, 2200339955]
@ -680,10 +682,27 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
case "web_app_open_link":
if let json = json, let url = json["url"] as? String {
let tryInstantView = json["try_instant_view"] as? Bool ?? false
let currentTimestamp = CACurrentMediaTime()
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
self.webView?.lastTouchTimestamp = nil
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
if tryInstantView {
let _ = (resolveInstantViewUrl(account: self.context.account, url: url)
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
switch result {
case let .instantView(webPage, anchor):
let controller = InstantPageController(context: strongSelf.context, webPage: webPage, sourcePeerType: .otherPrivate, anchor: anchor)
strongSelf.controller?.getNavigationController()?.pushViewController(controller)
default:
strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: url, forceExternal: true, presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
}
})
} else {
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
}
}
}
case "web_app_setup_back_button":
@ -799,8 +818,28 @@ public final class WebAppController: ViewController, AttachmentContainable {
if let json = json, let needConfirmation = json["need_confirmation"] as? Bool {
self.needDismissConfirmation = needConfirmation
}
case "web_app_request_phone":
break
case "web_app_open_scan_qr_popup":
var info: String = ""
if let json = json, let text = json["text"] as? String {
info = text
}
let controller = QrCodeScanScreen(context: self.context, subject: .custom(info: info))
controller.completion = { [weak self] result in
if let strongSelf = self {
strongSelf.sendQrCodeScannedEvent(data: result)
}
}
self.controller?.present(controller, in: .window(.root))
case "web_app_read_text_from_clipboard":
if let json = json, let requestId = json["req_id"] as? String {
let currentTimestamp = CACurrentMediaTime()
var fillData = false
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0, self.controller?.url == nil {
self.webView?.lastTouchTimestamp = nil
fillData = true
}
self.sendClipboardTextEvent(requestId: requestId, fillData: fillData)
}
default:
break
}
@ -930,6 +969,22 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
self.webView?.sendEvent(name: "phone_requested", data: paramsString)
}
fileprivate func sendQrCodeScannedEvent(data: String?) {
let paramsString = data.flatMap { "{data: \"\($0)\"}" } ?? "{}"
self.webView?.sendEvent(name: "scan_qr_popup_closed", data: paramsString)
}
fileprivate func sendClipboardTextEvent(requestId: String, fillData: Bool) {
var paramsString: String
if fillData {
let data = UIPasteboard.general.string ?? ""
paramsString = "{req_id: \"\(requestId)\", data: \"\(data)\"}"
} else {
paramsString = "{req_id: \"\(requestId)\"}"
}
self.webView?.sendEvent(name: "clipboard_text_received", data: paramsString)
}
}
fileprivate var controllerNode: Node {
@ -1067,7 +1122,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
let attachMenuBot = attachMenuBots.first(where: { $0.peer.id == botId})
if self?.url == nil, let attachMenuBot = attachMenuBot, attachMenuBot.hasSettings {
if self?.url == nil, let attachMenuBot = attachMenuBot, attachMenuBot.flags.contains(.hasSettings) {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_Settings, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Settings"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, _ in