diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 74e5cd1115..3b9cb40f75 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12217,6 +12217,17 @@ Sorry for the inconvenience."; "Chat.Context.Phone.NotOnTelegram" = "This number is not on Telegram."; "Chat.Context.Phone.ViewProfile" = "View Profile"; +"Chat.Context.Username.SendMessage" = "Send Message"; +"Chat.Context.Username.OpenGroup" = "Open Group"; +"Chat.Context.Username.OpenChannel" = "Open Channel"; +"Chat.Context.Username.Copy" = "Copy Username"; +"Chat.Context.Username.NotOnTelegram" = "This user doesn't exist on Telegram."; + +"Chat.Context.Hashtag.Search" = "View Profile"; +"Chat.Context.Hashtag.Copy" = "Copy Hashtag"; + +"Chat.Context.Card.Copy" = "Copy Card Number"; + "Message.FactCheck" = "Fact Check"; "Message.FactCheck.WhatIsThis" = "what's this?"; diff --git a/submodules/DrawingUI/Sources/DrawingLinkEntityView.swift b/submodules/DrawingUI/Sources/DrawingLinkEntityView.swift index cc72db2116..22d81cd41d 100644 --- a/submodules/DrawingUI/Sources/DrawingLinkEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingLinkEntityView.swift @@ -110,7 +110,7 @@ public final class DrawingLinkEntityView: DrawingEntityView, UITextViewDelegate private var textSize: CGSize = .zero public override func sizeThatFits(_ size: CGSize) -> CGSize { - if self.linkEntity.webpage != nil, let image = self.linkEntity.renderImage { + if self.linkEntity.webpage != nil, let image = self.linkEntity.whiteImage { self.imageView.frame = CGRect(origin: .zero, size: image.size) return image.size } else { @@ -118,7 +118,7 @@ public final class DrawingLinkEntityView: DrawingEntityView, UITextViewDelegate self.textSize = result let widthExtension = result.height * 0.65 - result.width = floorToScreenPixels(max(224.0, ceil(result.width) + 20.0) + widthExtension) + result.width = floorToScreenPixels(max(104.0, ceil(result.width) + 20.0) + widthExtension) result.height = ceil(result.height * 1.2); return result; } @@ -260,7 +260,11 @@ public final class DrawingLinkEntityView: DrawingEntityView, UITextViewDelegate self.blurredBackgroundView.isHidden = true self.iconView.isHidden = true - self.imageView.image = self.linkEntity.style == .white ? self.linkEntity.renderImage : self.linkEntity.secondaryRenderImage + if self.linkEntity.style == .white && self.imageView.image !== self.linkEntity.whiteImage { + self.imageView.image = self.linkEntity.whiteImage + } else if self.linkEntity.style == .black && self.imageView.image !== self.linkEntity.blackImage { + self.imageView.image = self.linkEntity.blackImage + } } else { self.textView.isHidden = false self.textView.frameInsets = UIEdgeInsets(top: 0.15, left: 0.0, bottom: 0.15, right: 0.0) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 449cf98f7e..7c2e055528 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -2467,42 +2467,41 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { }))) } if selectionCount > 1 { - items.append(.action(ContextMenuActionItem(text: "Send Without Grouping", icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Media Grid/GroupingOff"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - - self?.groupedValue = false - self?.controllerNode.send(asFile: false, silently: false, scheduleTime: nil, animated: true, parameters: nil, completion: {}) - }))) - -// if !items.isEmpty { -// items.append(.separator) -// } -// items.append(.action(ContextMenuActionItem(text: strings.Attachment_Grouped, icon: { theme in -// if !grouped { -// return nil -// } -// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) +// items.append(.action(ContextMenuActionItem(text: "Send Without Grouping", icon: { theme in +// return generateTintedImage(image: UIImage(bundleImageName: "Media Grid/GroupingOff"), color: theme.contextMenu.primaryColor) // }, action: { [weak self] _, f in // f(.default) // -// self?.groupedValue = true -// }))) -// items.append(.action(ContextMenuActionItem(text: strings.Attachment_Ungrouped, icon: { theme in -// if grouped { -// return nil -// } -// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) -// }, action: { [weak self] _, f in -// f(.default) -// // self?.groupedValue = false +// self?.controllerNode.send(asFile: false, silently: false, scheduleTime: nil, animated: true, parameters: nil, completion: {}) // }))) + + if !items.isEmpty { + items.append(.separator) + } + items.append(.action(ContextMenuActionItem(text: strings.Attachment_Grouped, icon: { theme in + if !grouped { + return nil + } + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + self?.groupedValue = true + }))) + items.append(.action(ContextMenuActionItem(text: strings.Attachment_Ungrouped, icon: { theme in + if grouped { + return nil + } + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + self?.groupedValue = false + }))) } - let isPaidAvailable = true - + let isPaidAvailable = !"".isEmpty if isSpoilerAvailable || isPaidAvailable || (selectionCount > 0 && isCaptionAboveMediaAvailable) { if !items.isEmpty { items.append(.separator) diff --git a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift index b088ef293d..fc245b1f57 100644 --- a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift +++ b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift @@ -23,6 +23,7 @@ public struct UserLimitsConfiguration: Equatable { public var maxStoriesWeeklyCount: Int32 public var maxStoriesMonthlyCount: Int32 public var maxStoriesSuggestedReactions: Int32 + public var maxStoriesLinksCount: Int32 public var maxGiveawayChannelsCount: Int32 public var maxGiveawayCountriesCount: Int32 public var maxGiveawayPeriodSeconds: Int32 @@ -51,6 +52,7 @@ public struct UserLimitsConfiguration: Equatable { maxStoriesWeeklyCount: 7, maxStoriesMonthlyCount: 30, maxStoriesSuggestedReactions: 1, + maxStoriesLinksCount: 3, maxGiveawayChannelsCount: 10, maxGiveawayCountriesCount: 10, maxGiveawayPeriodSeconds: 86400 * 31, @@ -80,6 +82,7 @@ public struct UserLimitsConfiguration: Equatable { maxStoriesWeeklyCount: Int32, maxStoriesMonthlyCount: Int32, maxStoriesSuggestedReactions: Int32, + maxStoriesLinksCount: Int32, maxGiveawayChannelsCount: Int32, maxGiveawayCountriesCount: Int32, maxGiveawayPeriodSeconds: Int32, @@ -106,6 +109,7 @@ public struct UserLimitsConfiguration: Equatable { self.maxStoriesWeeklyCount = maxStoriesWeeklyCount self.maxStoriesMonthlyCount = maxStoriesMonthlyCount self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions + self.maxStoriesLinksCount = maxStoriesLinksCount self.maxGiveawayChannelsCount = maxGiveawayChannelsCount self.maxGiveawayCountriesCount = maxGiveawayCountriesCount self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds @@ -158,6 +162,7 @@ extension UserLimitsConfiguration { self.maxStoriesWeeklyCount = getValue("stories_sent_weekly_limit", orElse: defaultValue.maxStoriesWeeklyCount) self.maxStoriesMonthlyCount = getValue("stories_sent_monthly_limit", orElse: defaultValue.maxStoriesMonthlyCount) self.maxStoriesSuggestedReactions = getValue("stories_suggested_reactions_limit", orElse: defaultValue.maxStoriesMonthlyCount) + self.maxStoriesLinksCount = getGeneralValue("stories_area_url_max", orElse: defaultValue.maxStoriesLinksCount) self.maxGiveawayChannelsCount = getGeneralValue("giveaway_add_peers_max", orElse: defaultValue.maxGiveawayChannelsCount) self.maxGiveawayCountriesCount = getGeneralValue("giveaway_countries_max", orElse: defaultValue.maxGiveawayCountriesCount) self.maxGiveawayPeriodSeconds = getGeneralValue("giveaway_period_max", orElse: defaultValue.maxGiveawayPeriodSeconds) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift index 8107e653cd..7a04b9c3d1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift @@ -57,6 +57,7 @@ public enum EngineConfiguration { public let maxStoriesWeeklyCount: Int32 public let maxStoriesMonthlyCount: Int32 public let maxStoriesSuggestedReactions: Int32 + public let maxStoriesLinksCount: Int32 public let maxGiveawayChannelsCount: Int32 public let maxGiveawayCountriesCount: Int32 public let maxGiveawayPeriodSeconds: Int32 @@ -88,6 +89,7 @@ public enum EngineConfiguration { maxStoriesWeeklyCount: Int32, maxStoriesMonthlyCount: Int32, maxStoriesSuggestedReactions: Int32, + maxStoriesLinksCount: Int32, maxGiveawayChannelsCount: Int32, maxGiveawayCountriesCount: Int32, maxGiveawayPeriodSeconds: Int32, @@ -114,6 +116,7 @@ public enum EngineConfiguration { self.maxStoriesWeeklyCount = maxStoriesWeeklyCount self.maxStoriesMonthlyCount = maxStoriesMonthlyCount self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions + self.maxStoriesLinksCount = maxStoriesLinksCount self.maxGiveawayChannelsCount = maxGiveawayChannelsCount self.maxGiveawayCountriesCount = maxGiveawayCountriesCount self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds @@ -176,6 +179,7 @@ public extension EngineConfiguration.UserLimits { maxStoriesWeeklyCount: userLimitsConfiguration.maxStoriesWeeklyCount, maxStoriesMonthlyCount: userLimitsConfiguration.maxStoriesMonthlyCount, maxStoriesSuggestedReactions: userLimitsConfiguration.maxStoriesSuggestedReactions, + maxStoriesLinksCount: userLimitsConfiguration.maxStoriesLinksCount, maxGiveawayChannelsCount: userLimitsConfiguration.maxGiveawayChannelsCount, maxGiveawayCountriesCount: userLimitsConfiguration.maxGiveawayCountriesCount, maxGiveawayPeriodSeconds: userLimitsConfiguration.maxGiveawayPeriodSeconds, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift index a2e5ce6965..a45233e0bc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift @@ -107,15 +107,15 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { if selectedMedia == nil { for media in item.message.media { if let telegramImage = media as? TelegramMediaImage { - #if DEBUG - if item.message.text == "#" { - selectedMedia = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: 100, startParam: "", extendedMedia: .preview(dimensions: telegramImage.representations.first?.dimensions ?? PixelDimensions(width: 1, height: 1), immediateThumbnailData: telegramImage.immediateThumbnailData, videoDuration: nil), flags: [], version: 0) - } else { - selectedMedia = telegramImage - } - #else +// #if DEBUG +// if item.message.text == "#" { +// selectedMedia = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: 100, startParam: "", extendedMedia: .preview(dimensions: telegramImage.representations.first?.dimensions ?? PixelDimensions(width: 1, height: 1), immediateThumbnailData: telegramImage.immediateThumbnailData, videoDuration: nil), flags: [], version: 0) +// } else { +// selectedMedia = telegramImage +// } +// #else selectedMedia = telegramImage - #endif +// #endif if shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: telegramImage) { automaticDownload = .full } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingLinkEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingLinkEntity.swift index d1dced3944..684aa37810 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingLinkEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingLinkEntity.swift @@ -24,6 +24,8 @@ public final class DrawingLinkEntity: DrawingEntity, Codable { case scale case rotation case renderImage + case whiteImage + case blackImage } public enum Style: Codable, Equatable { @@ -75,9 +77,10 @@ public final class DrawingLinkEntity: DrawingEntity, Codable { return self.position } - public var renderImage: UIImage? - public var secondaryRenderImage: UIImage? + public var whiteImage: UIImage? + public var blackImage: UIImage? + public var renderImage: UIImage? public var renderSubEntities: [DrawingEntity]? public var isMedia: Bool { @@ -131,6 +134,15 @@ public final class DrawingLinkEntity: DrawingEntity, Codable { self.width = try container.decode(CGFloat.self, forKey: .width) self.scale = try container.decode(CGFloat.self, forKey: .scale) self.rotation = try container.decode(CGFloat.self, forKey: .rotation) + + if let imagePath = try container.decodeIfPresent(String.self, forKey: .whiteImage), let image = UIImage(contentsOfFile: fullEntityMediaPath(imagePath)) { + self.whiteImage = image + } + + if let imagePath = try container.decodeIfPresent(String.self, forKey: .blackImage), let image = UIImage(contentsOfFile: fullEntityMediaPath(imagePath)) { + self.blackImage = image + } + if let renderImageData = try? container.decodeIfPresent(Data.self, forKey: .renderImage) { self.renderImage = UIImage(data: renderImageData) } @@ -161,8 +173,29 @@ public final class DrawingLinkEntity: DrawingEntity, Codable { try container.encode(self.position, forKey: .position) try container.encode(self.width, forKey: .width) try container.encode(self.scale, forKey: .scale) - try container.encode(self.rotation, forKey: .rotation) - if let renderImage, let data = renderImage.pngData() { + try container.encode(self.rotation, forKey: .rotation) + + if let image = self.whiteImage { + let imagePath = "\(self.uuid)_white.png" + let fullImagePath = fullEntityMediaPath(imagePath) + if let imageData = image.pngData() { + try? FileManager.default.createDirectory(atPath: entitiesPath(), withIntermediateDirectories: true) + try? imageData.write(to: URL(fileURLWithPath: fullImagePath)) + try container.encodeIfPresent(imagePath, forKey: .whiteImage) + } + } + + if let image = self.blackImage { + let imagePath = "\(self.uuid)black.png" + let fullImagePath = fullEntityMediaPath(imagePath) + if let imageData = image.pngData() { + try? FileManager.default.createDirectory(atPath: entitiesPath(), withIntermediateDirectories: true) + try? imageData.write(to: URL(fileURLWithPath: fullImagePath)) + try container.encodeIfPresent(imagePath, forKey: .blackImage) + } + } + + if let renderImage = self.renderImage, let data = renderImage.pngData() { try container.encode(data, forKey: .renderImage) } } @@ -177,6 +210,8 @@ public final class DrawingLinkEntity: DrawingEntity, Codable { newEntity.width = self.width newEntity.scale = self.scale newEntity.rotation = self.rotation + newEntity.whiteImage = self.whiteImage + newEntity.blackImage = self.blackImage return newEntity } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift index 2ccb866433..fa8979408a 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift @@ -5,11 +5,11 @@ import AccountContext import Postbox import TelegramCore -private func entitiesPath() -> String { +func entitiesPath() -> String { return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/mediaEntities" } -private func fullEntityMediaPath(_ path: String) -> String { +func fullEntityMediaPath(_ path: String) -> String { return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/mediaEntities/" + path } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift index fbfdd54f51..ab887d4bf4 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift @@ -343,7 +343,7 @@ public final class DrawingMessageRenderer { } public func render(completion: @escaping (Result) -> Void) { - Queue.mainQueue().after(0.066) { + Queue.mainQueue().after(0.12) { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } let defaultPresentationData = defaultPresentationData() diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift index 7f692ff37f..b8afdb5081 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift @@ -23,6 +23,7 @@ import MediaEditor import UrlEscaping private let linkTag = GenericComponentViewTag() +private let nameTag = GenericComponentViewTag() private final class SheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -130,15 +131,16 @@ private final class SheetContent: CombinedComponent { Text( text: strings.Common_Done, font: Font.bold(17.0), - color: state.link.isEmpty ? theme.actionSheet.secondaryTextColor : theme.actionSheet.controlAccentColor + color: isValidLink ? theme.actionSheet.controlAccentColor : theme.actionSheet.secondaryTextColor ) ), isEnabled: isValidLink, action: { [weak state] in - if let controller = controller() as? CreateLinkScreen { - state?.complete(controller: controller) + if let controller = controller() as? CreateLinkScreen, let state { + if state.complete(controller: controller) { + component.dismiss() + } } - component.dismiss() } ), availableSize: context.availableSize, @@ -198,6 +200,11 @@ private final class SheetContent: CombinedComponent { state?.link = text state?.updated() }, + textReturned: { [weak state] in + if let controller = controller() as? CreateLinkScreen { + state?.switchToNextField(controller: controller) + } + }, tag: linkTag ) ) @@ -263,7 +270,15 @@ private final class SheetContent: CombinedComponent { placeholderText: strings.MediaEditor_Link_LinkName_Placeholder, textUpdated: { [weak state] text in state?.name = text - } + }, + textReturned: { [weak state] in + if let controller = controller() as? CreateLinkScreen, let state { + if state.complete(controller: controller) { + component.dismiss() + } + } + }, + tag: nameTag ) ) ) @@ -433,7 +448,21 @@ private final class CreateLinkSheetComponent: CombinedComponent { }) } - func complete(controller: CreateLinkScreen) { + func switchToNextField(controller: CreateLinkScreen) { + if let view = controller.node.hostView.findTaggedView(tag: nameTag) as? LinkFieldComponent.View { + view.activateInput() + } + } + + func complete(controller: CreateLinkScreen) -> Bool { + let explicitLink = explicitUrl(self.link) + if !isValidUrl(explicitLink) { + if let view = controller.node.hostView.findTaggedView(tag: linkTag) as? LinkFieldComponent.View { + view.animateError() + } + return false + } + let text = !self.name.isEmpty ? self.name : self.link var effectiveMedia: TelegramMediaWebpage? @@ -466,6 +495,7 @@ private final class CreateLinkSheetComponent: CombinedComponent { ) ) }) + return true } } @@ -638,6 +668,7 @@ private final class LinkFieldComponent: Component { let link: Bool let placeholderText: String let textUpdated: (String) -> Void + let textReturned: () -> Void let tag: AnyObject? init( @@ -647,6 +678,7 @@ private final class LinkFieldComponent: Component { link: Bool, placeholderText: String, textUpdated: @escaping (String) -> Void, + textReturned: @escaping () -> Void, tag: AnyObject? = nil ) { self.textColor = textColor @@ -655,6 +687,7 @@ private final class LinkFieldComponent: Component { self.link = link self.placeholderText = placeholderText self.textUpdated = textUpdated + self.textReturned = textReturned self.tag = tag } @@ -714,14 +747,14 @@ private final class LinkFieldComponent: Component { } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + if string == "\n" { + self.component?.textReturned() + return false + } + let newText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string) if let component = self.component, !component.link && newText.count > 48 { - textField.layer.addShakeAnimation() - let hapticFeedback = HapticFeedback() - hapticFeedback.error() - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0, execute: { - let _ = hapticFeedback - }) + self.animateError() return false } return true @@ -735,6 +768,15 @@ private final class LinkFieldComponent: Component { self.textField.selectAll(nil) } + func animateError() { + self.textField.layer.addShakeAnimation() + let hapticFeedback = HapticFeedback() + hapticFeedback.error() + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0, execute: { + let _ = hapticFeedback + }) + } + func update(component: LinkFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.textField.textColor = component.textColor self.textField.text = component.text @@ -747,6 +789,8 @@ private final class LinkFieldComponent: Component { self.textField.autocorrectionType = .no self.textField.autocapitalizationType = .none self.textField.textContentType = .URL + } else { + self.textField.returnKeyType = .done } self.component = component diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 336dedebaa..5e92d6188f 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -4486,7 +4486,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } if existingEntity == nil { - let maxLinkCount = 3 + let maxLinkCount = self.context.userLimits.maxStoriesLinksCount var currentLinkCount = 0 self.entitiesView.eachView { entityView in if entityView.entity is DrawingLinkEntity { @@ -4528,8 +4528,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } let entity = DrawingLinkEntity(url: result.url, name: result.name, webpage: result.webpage, positionBelowText: result.positionBelowText, largeMedia: result.largeMedia, style: style) - entity.renderImage = result.image - entity.secondaryRenderImage = result.nightImage + entity.whiteImage = result.image + entity.blackImage = result.nightImage if let existingEntity { self.entitiesView.remove(uuid: existingEntity.uuid, animated: true) diff --git a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift index 6b0addf939..4d501b3de1 100644 --- a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift +++ b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift @@ -2565,7 +2565,7 @@ final class StoryStickersContentView: UIView, EmojiCustomContentView { InteractiveStickerButtonContent( theme: theme, title: strings.MediaEditor_AddLink, - iconName: "Premium/Link", + iconName: self.isPremium ? "Media Editor/Link" : "Media Editor/LinkLocked", useOpaqueTheme: useOpaqueTheme, tintContainerView: self.tintContainerView ) diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Link.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Editor/Link.imageset/Contents.json new file mode 100644 index 0000000000..942d26181d --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Editor/Link.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "link.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Link.imageset/link.pdf b/submodules/TelegramUI/Images.xcassets/Media Editor/Link.imageset/link.pdf new file mode 100644 index 0000000000..4b654b898e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Editor/Link.imageset/link.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/LinkLocked.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Editor/LinkLocked.imageset/Contents.json new file mode 100644 index 0000000000..cd6a1f9151 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Editor/LinkLocked.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "linklocked.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/LinkLocked.imageset/linklocked.pdf b/submodules/TelegramUI/Images.xcassets/Media Editor/LinkLocked.imageset/linklocked.pdf new file mode 100644 index 0000000000..2c59d8d3c2 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Editor/LinkLocked.imageset/linklocked.pdf differ diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenBankCardContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenBankCardContextMenu.swift index 3eba806368..2ae05479b9 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenBankCardContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenBankCardContextMenu.swift @@ -52,26 +52,8 @@ extension ChatControllerImpl { var items: [ContextMenuItem] = [] - if let info { - for url in info.urls { - items.append( - .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_AddToContacts, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in - guard let self else { - return - } - f(.default) - self.controllerInteraction?.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: false, external: false, message: message)) - })) - ) - } - - if !items.isEmpty { - items.append(.separator) - } - } - items.append( - .action(ContextMenuActionItem(text: "Copy Card Number", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Card_Copy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) guard let self else { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenHashtagContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenHashtagContextMenu.swift index 0d9b72f0d5..5a9bfd220a 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenHashtagContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenHashtagContextMenu.swift @@ -44,7 +44,7 @@ extension ChatControllerImpl { var items: [ContextMenuItem] = [] items.append( - .action(ContextMenuActionItem(text: "Search", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Hashtag_Search, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in guard let self else { return } @@ -54,7 +54,7 @@ extension ChatControllerImpl { ) items.append( - .action(ContextMenuActionItem(text: "Copy Hashtag", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Hashtag_Copy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) guard let self else { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkContextMenu.swift index 46a627c0c3..ce4b381e19 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkContextMenu.swift @@ -80,7 +80,7 @@ extension ChatControllerImpl { if canAddToReadingList { items.append( - .action(ContextMenuActionItem(text: "Add to Reading List", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in + .action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_AddToReadingList, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) if let link = URL(string: url) { @@ -88,16 +88,10 @@ extension ChatControllerImpl { } })) ) -// / items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_AddToReadingList, color: .accent, action: { [weak actionSheet] in -// // actionSheet?.dismissAnimated() -// // if let link = URL(string: url) { -// // let _ = try? SSReadingList.default()?.addItem(with: link, title: nil, previewText: nil) -// // } -// // })) } items.append( - .action(ContextMenuActionItem(text: "Copy Link", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + .action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuCopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) guard let self else { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenUsernameContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenUsernameContextMenu.swift index 7d5a25f414..ae629872f8 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenUsernameContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenUsernameContextMenu.swift @@ -68,19 +68,46 @@ extension ChatControllerImpl { var items: [ContextMenuItem] = [] if let peer { - items.append( - .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_SendMessage, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in - f(.default) - guard let self else { - return - } - self.openPeer(peer: peer, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil) - })) - ) + if case .user = peer { + items.append( + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Username_SendMessage, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.default) + guard let self else { + return + } + self.openPeer(peer: peer, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil) + })) + ) + } else { + var isGroup = true + if case let .channel(channel) = peer, case .broadcast = channel.info { + isGroup = false + } + + let openTitle: String + let openIcon: UIImage? + + if isGroup { + openTitle = self.presentationData.strings.Chat_Context_Username_OpenGroup + openIcon = UIImage(bundleImageName: "Chat/Context Menu/Groups") + } else { + openTitle = self.presentationData.strings.Chat_Context_Username_OpenChannel + openIcon = UIImage(bundleImageName: "Chat/Context Menu/Channels") + } + items.append( + .action(ContextMenuActionItem(text: openTitle, icon: { theme in return generateTintedImage(image: openIcon, color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.default) + guard let self else { + return + } + self.openPeer(peer: peer, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil) + })) + ) + } } items.append( - .action(ContextMenuActionItem(text: "Copy Username", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Username_Copy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) guard let self else { @@ -117,7 +144,7 @@ extension ChatControllerImpl { } else { let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil items.append( - .action(ContextMenuActionItem(text: "This user doesn't exist on Telegram.", textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction)) + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Username_NotOnTelegram, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction)) ) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index bdc834dd26..84f8280c52 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -902,40 +902,40 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - #if DEBUG - if message.text == "#", let telegramImage = message.media.first(where: { $0 is TelegramMediaImage }) as? TelegramMediaImage { - let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: 100, startParam: "", extendedMedia: .preview(dimensions: telegramImage.representations.first?.dimensions ?? PixelDimensions(width: 1, height: 1), immediateThumbnailData: telegramImage.immediateThumbnailData, videoDuration: nil), flags: [], version: 0) - - let inputData = Promise() - inputData.set(.single( - BotCheckoutController.InputData( - form: BotPaymentForm(id: 123, canSaveCredentials: false, passwordMissing: false, invoice: BotPaymentInvoice(isTest: false, requestedFields: [], currency: "XTR", prices: [BotPaymentPrice(label: "", amount: 100)], tip: nil, termsInfo: nil), paymentBotId: message.id.peerId, providerId: nil, url: nil, nativeProvider: nil, savedInfo: nil, savedCredentials: [], additionalPaymentMethods: []), - validatedFormInfo: nil, - botPeer: nil - ))) - if invoice.currency == "XTR", let starsContext = strongSelf.context.starsContext { - let starsInputData = combineLatest( - inputData.get(), - starsContext.state - ) - |> map { data, state -> (StarsContext.State, BotPaymentForm, EnginePeer?)? in - if let data, let state { - return (state, data.form, data.botPeer) - } else { - return nil - } - } - let _ = (starsInputData |> filter { $0 != nil } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in - guard let strongSelf = self else { - return - } - let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(context: strongSelf.context, starsContext: starsContext, invoice: invoice, source: .message(message.id), inputData: starsInputData, completion: { _ in }) - strongSelf.push(controller) - }) - } - return true - } - #endif +// #if DEBUG +// if message.text == "#", let telegramImage = message.media.first(where: { $0 is TelegramMediaImage }) as? TelegramMediaImage { +// let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: 100, startParam: "", extendedMedia: .preview(dimensions: telegramImage.representations.first?.dimensions ?? PixelDimensions(width: 1, height: 1), immediateThumbnailData: telegramImage.immediateThumbnailData, videoDuration: nil), flags: [], version: 0) +// +// let inputData = Promise() +// inputData.set(.single( +// BotCheckoutController.InputData( +// form: BotPaymentForm(id: 123, canSaveCredentials: false, passwordMissing: false, invoice: BotPaymentInvoice(isTest: false, requestedFields: [], currency: "XTR", prices: [BotPaymentPrice(label: "", amount: 100)], tip: nil, termsInfo: nil), paymentBotId: message.id.peerId, providerId: nil, url: nil, nativeProvider: nil, savedInfo: nil, savedCredentials: [], additionalPaymentMethods: []), +// validatedFormInfo: nil, +// botPeer: nil +// ))) +// if invoice.currency == "XTR", let starsContext = strongSelf.context.starsContext { +// let starsInputData = combineLatest( +// inputData.get(), +// starsContext.state +// ) +// |> map { data, state -> (StarsContext.State, BotPaymentForm, EnginePeer?)? in +// if let data, let state { +// return (state, data.form, data.botPeer) +// } else { +// return nil +// } +// } +// let _ = (starsInputData |> filter { $0 != nil } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in +// guard let strongSelf = self else { +// return +// } +// let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(context: strongSelf.context, starsContext: starsContext, invoice: invoice, source: .message(message.id), inputData: starsInputData, completion: { _ in }) +// strongSelf.push(controller) +// }) +// } +// return true +// } +// #endif if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia { switch extendedMedia { diff --git a/submodules/UrlEscaping/Sources/UrlEscaping.swift b/submodules/UrlEscaping/Sources/UrlEscaping.swift index 165b60ed44..83a6fed01c 100644 --- a/submodules/UrlEscaping/Sources/UrlEscaping.swift +++ b/submodules/UrlEscaping/Sources/UrlEscaping.swift @@ -26,7 +26,7 @@ public func isValidUrl(_ url: String, validSchemes: [String: Bool] = ["http": tr if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: escapedUrl), let scheme = url.scheme?.lowercased(), let requiresTopLevelDomain = validSchemes[scheme], let host = url.host, (!requiresTopLevelDomain || host.contains(".")) && url.user == nil { if requiresTopLevelDomain { let components = host.components(separatedBy: ".") - let domain = (components.first ?? "") + let domain = (components.last ?? "") if domain.isEmpty { return false }