Avatar improvements

This commit is contained in:
Ilya Laktyushin 2022-12-18 10:42:03 +04:00
parent 8cdd2076d4
commit 2d5df7debc
4 changed files with 93 additions and 97 deletions

View File

@ -8475,15 +8475,17 @@ Sorry for the inconvenience.";
"UserInfo.ResetToOriginalAlertReset" = "Reset";
"Conversation.SuggestedPhotoTitle" = "Suggested Photo";
"Conversation.SuggestedPhotoText" = "%@ suggests you to use this photo for your Telegram account.";
"Conversation.SuggestedPhotoTextYou" = "You suggested %@ to use this photo for their Telegram account.";
"Conversation.SuggestedPhotoView" = "View";
"Conversation.SuggestedPhotoText" = "**%@** suggests you to use this profile photo.";
"Conversation.SuggestedPhotoTextExpanded" = "%@ suggests you to use this profile photo for your Telegram account.";
"Conversation.SuggestedPhotoTextYou" = "You suggested **%@** to use this profile photo.";
"Conversation.SuggestedPhotoView" = "View Photo";
"Conversation.SuggestedPhotoSuccess" = "You have updated your Telegram account photo.";
"Conversation.SuggestedVideoTitle" = "Suggested Video";
"Conversation.SuggestedVideoText" = "%@ suggests you to use this video for your Telegram account.";
"Conversation.SuggestedVideoTextYou" = "You suggested %@ to use this video for their Telegram account.";
"Conversation.SuggestedVideoView" = "View";
"Conversation.SuggestedVideoText" = "**%@** suggests you to use this profile video.";
"Conversation.SuggestedVideoTextExpanded" = "%@ suggests you to use this profile video for your Telegram account.";
"Conversation.SuggestedVideoTextYou" = "You suggested **%@** to use this profile video.";
"Conversation.SuggestedVideoView" = "View Video";
"Conversation.SuggestedVideoSuccess" = "You have updated your Telegram account video.";
"CacheEvictionMenu.CategoryExceptions_1" = "%@ Exception";

View File

@ -202,7 +202,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
[_titleLabel sizeToFit];
[_wrapperView addSubview:_titleLabel];
NSMutableAttributedString *subtitle = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:self.item.isVideo ? TGLocalized(@"Conversation.SuggestedVideoText") : TGLocalized(@"Conversation.SuggestedPhotoText"), _senderName]];
NSMutableAttributedString *subtitle = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:self.item.isVideo ? TGLocalized(@"Conversation.SuggestedVideoTextExpanded") : TGLocalized(@"Conversation.SuggestedPhotoTextExpanded"), _senderName]];
[subtitle addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, subtitle.string.length)];
[subtitle addAttribute:NSFontAttributeName value:TGSystemFontOfSize(15.0f) range:NSMakeRange(0, subtitle.string.length)];

View File

@ -77,12 +77,12 @@ private final class SelectivePrivacySettingsControllerArguments {
private enum SelectivePrivacySettingsSection: Int32 {
case forwards
case setting
case photo
case peers
case callsP2P
case callsP2PPeers
case callsIntegrationEnabled
case phoneDiscovery
case photo
}
private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings: PresentationStrings) -> String {
@ -105,9 +105,6 @@ 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?, UIImage?)
case publicPhotoInfo(PresentationTheme, String)
case exceptionsHeader(PresentationTheme, String)
case disableFor(PresentationTheme, String, String)
case enableFor(PresentationTheme, String, String)
@ -126,6 +123,9 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
case phoneDiscoveryEverybody(PresentationTheme, String, Bool)
case phoneDiscoveryMyContacts(PresentationTheme, String, Bool)
case phoneDiscoveryInfo(PresentationTheme, String, String)
case setPublicPhoto(PresentationTheme, String)
case removePublicPhoto(PresentationTheme, String, EnginePeer, TelegramMediaImage?, UIImage?)
case publicPhotoInfo(PresentationTheme, String)
var section: ItemListSectionId {
switch self {
@ -133,8 +133,6 @@ 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:
@ -145,6 +143,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return SelectivePrivacySettingsSection.callsIntegrationEnabled.rawValue
case .phoneDiscoveryHeader, .phoneDiscoveryEverybody, .phoneDiscoveryMyContacts, .phoneDiscoveryInfo:
return SelectivePrivacySettingsSection.phoneDiscovery.rawValue
case .setPublicPhoto, .removePublicPhoto, .publicPhotoInfo:
return SelectivePrivacySettingsSection.photo.rawValue
}
}
@ -164,48 +164,48 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return 5
case .settingInfo:
return 6
case .setPublicPhoto:
return 7
case .removePublicPhoto:
return 8
case .publicPhotoInfo:
return 9
case .phoneDiscoveryHeader:
return 10
return 7
case .phoneDiscoveryEverybody:
return 11
return 8
case .phoneDiscoveryMyContacts:
return 12
return 9
case .phoneDiscoveryInfo:
return 13
return 10
case .exceptionsHeader:
return 14
return 11
case .disableFor:
return 15
return 12
case .enableFor:
return 16
return 13
case .peersInfo:
return 17
return 14
case .callsP2PHeader:
return 18
return 15
case .callsP2PAlways:
return 19
return 16
case .callsP2PContacts:
return 20
return 17
case .callsP2PNever:
return 21
return 18
case .callsP2PInfo:
return 22
return 19
case .callsP2PDisableFor:
return 23
return 20
case .callsP2PEnableFor:
return 24
return 21
case .callsP2PPeersInfo:
return 25
return 22
case .callsIntegrationEnabled:
return 26
return 23
case .callsIntegrationInfo:
return 27
return 24
case .setPublicPhoto:
return 24
case .removePublicPhoto:
return 25
case .publicPhotoInfo:
return 26
}
}
@ -247,24 +247,6 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
} 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, lhsImage):
if case let .removePublicPhoto(rhsTheme, rhsText, rhsPeer, rhsRep, rhsImage) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsPeer == rhsPeer, lhsRep == rhsRep, lhsImage === rhsImage {
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
}
case let .exceptionsHeader(lhsTheme, lhsText):
if case let .exceptionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
@ -379,6 +361,24 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
} 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, lhsImage):
if case let .removePublicPhoto(rhsTheme, rhsText, rhsPeer, rhsRep, rhsImage) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsPeer == rhsPeer, lhsRep == rhsRep, lhsImage === rhsImage {
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
}
}
}
@ -411,17 +411,6 @@ 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(_, text, peer, image, completeImage):
return ItemListPeerActionItem(presentationData: presentationData, icon: completeImage, iconSignal: completeImage == nil ? peerAvatarCompleteImage(account: arguments.context.account, peer: peer, forceProvidedRepresentation: true, representation: image?.representationForDisplayAtSize(PixelDimensions(width: 28, height: 28)), size: CGSize(width: 28.0, height: 28.0)) : nil, 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):
@ -480,6 +469,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(_, text, peer, image, completeImage):
return ItemListPeerActionItem(presentationData: presentationData, icon: completeImage, iconSignal: completeImage == nil ? peerAvatarCompleteImage(account: arguments.context.account, peer: peer, forceProvidedRepresentation: true, representation: image?.representationForDisplayAtSize(PixelDimensions(width: 28, height: 28)), size: CGSize(width: 28.0, height: 28.0)) : nil, 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
})
}
}
}
@ -670,9 +670,9 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
entries.append(.everybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.setting == .everybody))
entries.append(.contacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.setting == .contacts))
switch kind {
case .presence, .voiceCalls, .forwards, .phoneNumber, .voiceMessages:
case .presence, .voiceCalls, .forwards, .phoneNumber, .voiceMessages, .profilePhoto:
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody))
case .groupInvitations, .profilePhoto:
case .groupInvitations:
break
}
let phoneLink = "https://t.me/+\(phoneNumber)"
@ -686,17 +686,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false))
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, state.setting != .everybody {
if let publicPhoto = publicPhoto {
entries.append(.setPublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_UpdatePublicPhoto))
entries.append(.removePublicPhoto(presentationData.theme, !publicPhoto.videoRepresentations.isEmpty ? presentationData.strings.Privacy_ProfilePhoto_RemovePublicVideo : presentationData.strings.Privacy_ProfilePhoto_RemovePublicPhoto, peer, publicPhoto, state.uploadedPhoto))
} 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 {
@ -737,6 +727,16 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
}
}
if case .profilePhoto = kind, let peer = peer, state.setting != .everybody || !state.disableFor.isEmpty {
if let publicPhoto = publicPhoto {
entries.append(.setPublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_UpdatePublicPhoto))
entries.append(.removePublicPhoto(presentationData.theme, !publicPhoto.videoRepresentations.isEmpty ? presentationData.strings.Privacy_ProfilePhoto_RemovePublicVideo : presentationData.strings.Privacy_ProfilePhoto_RemovePublicPhoto, peer, publicPhoto, state.uploadedPhoto))
} else {
entries.append(.setPublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_SetPublicPhoto))
}
entries.append(.publicPhotoInfo(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_PublicPhotoInfo))
}
return entries
}

View File

@ -17,11 +17,11 @@ import PhotoResources
import UniversalMediaPlayer
import TelegramUniversalVideoContent
import GalleryUI
import Markdown
class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode {
private var mediaBackgroundContent: WallpaperBubbleBackgroundNode?
private let mediaBackgroundNode: NavigationBackgroundNode
private let titleNode: TextNode
private let subtitleNode: TextNode
private let imageNode: TransformImageNode
@ -42,10 +42,6 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
self.mediaBackgroundNode.clipsToBounds = true
self.mediaBackgroundNode.cornerRadius = 24.0
self.titleNode = TextNode()
self.titleNode.isUserInteractionEnabled = false
self.titleNode.displaysAsynchronously = false
self.subtitleNode = TextNode()
self.subtitleNode.isUserInteractionEnabled = false
self.subtitleNode.displaysAsynchronously = false
@ -65,7 +61,6 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
super.init()
self.addSubnode(self.mediaBackgroundNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.subtitleNode)
self.addSubnode(self.imageNode)
@ -152,7 +147,6 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
}
override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeImageLayout = self.imageNode.asyncLayout()
let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode)
let makeButtonTitleLayout = TextNode.asyncLayout(self.buttonTitleNode)
@ -181,8 +175,6 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
let isVideo = !(photo?.videoRepresentations.isEmpty ?? true)
let fromYou = item.message.author?.id == item.context.account.peerId
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: isVideo ? item.presentationData.strings.Conversation_SuggestedVideoTitle : item.presentationData.strings.Conversation_SuggestedPhotoTitle, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let peerName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0).compactDisplayTitle } ?? ""
let text: String
if fromYou {
@ -191,11 +183,18 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
text = isVideo ? item.presentationData.strings.Conversation_SuggestedVideoText(peerName).string : item.presentationData.strings.Conversation_SuggestedPhotoText(peerName).string
}
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: Font.regular(13.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor)
let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: primaryTextColor)
let subtitle = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in
return nil
}), textAlignment: .center)
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitle, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: isVideo ? item.presentationData.strings.Conversation_SuggestedVideoView : item.presentationData.strings.Conversation_SuggestedPhotoView, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let backgroundSize = CGSize(width: width, height: titleLayout.size.height + subtitleLayout.size.height + 182.0)
let backgroundSize = CGSize(width: width, height: subtitleLayout.size.height + 182.0)
return (backgroundSize.width, { boundingWidth in
return (backgroundSize, { [weak self] animation, synchronousLoads, _ in
@ -256,22 +255,17 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
videoNode.removeFromSupernode()
}
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - width) / 2.0), y: 0.0), size: backgroundSize)
let mediaBackgroundFrame = backgroundFrame.insetBy(dx: -2.0, dy: -2.0)
let mediaBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - width) / 2.0), y: 0.0), size: backgroundSize)
strongSelf.mediaBackgroundNode.frame = mediaBackgroundFrame
strongSelf.mediaBackgroundNode.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
strongSelf.mediaBackgroundNode.update(size: mediaBackgroundFrame.size, transition: .immediate)
strongSelf.buttonNode.backgroundColor = item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
let _ = titleApply()
let _ = subtitleApply()
let _ = buttonTitleApply()
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 127.0), size: titleLayout.size)
strongSelf.titleNode.frame = titleFrame
let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY + 2.0), size: subtitleLayout.size)
let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 127.0), size: subtitleLayout.size)
strongSelf.subtitleNode.frame = subtitleFrame
let buttonTitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - buttonTitleLayout.size.width) / 2.0), y: subtitleFrame.maxY + 18.0), size: buttonTitleLayout.size)