mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Various Improvements
This commit is contained in:
parent
0cc01c5280
commit
835ce9adae
BIN
Telegram/Telegram-iOS/Resources/PlaneLogoPlain.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/PlaneLogoPlain.tgs
Normal file
Binary file not shown.
@ -7145,3 +7145,7 @@ Sorry for the inconvenience.";
|
||||
"ChatList.Archive" = "Archive";
|
||||
|
||||
"TextFormat.Spoiler" = "Spoiler";
|
||||
|
||||
"Conversation.ContextMenuTranslate" = "Translate";
|
||||
|
||||
"ClearCache.ClearDescription" = "All media will stay in the Telegram cloud and can be re-downloaded if you need it again.";
|
||||
|
||||
@ -61,6 +61,7 @@ swift_library(
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/TelegramCallsUI:TelegramCallsUI",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -17,6 +17,8 @@ import PhotoResources
|
||||
import ChatListSearchItemNode
|
||||
import ContextUI
|
||||
import ChatInterfaceState
|
||||
import TextFormat
|
||||
import InvisibleInkDustNode
|
||||
|
||||
public enum ChatListItemContent {
|
||||
public final class DraftState: Equatable {
|
||||
@ -427,6 +429,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let measureNode: TextNode
|
||||
private var currentItemHeight: CGFloat?
|
||||
let textNode: TextNode
|
||||
var dustNode: InvisibleInkDustNode?
|
||||
let inputActivitiesNode: ChatListInputActivitiesNode
|
||||
let dateNode: TextNode
|
||||
let separatorNode: ASDisplayNode
|
||||
@ -1049,12 +1052,26 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
attributedText = NSAttributedString(string: foldLineBreaks(draftText.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor)
|
||||
} else if let message = messages.last {
|
||||
var composedString: NSMutableAttributedString
|
||||
|
||||
let entities = (message._asMessage().textEntitiesAttribute?.entities ?? []).filter { entity in
|
||||
if case .Spoiler = entity.type {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
let messageString: NSAttributedString
|
||||
if !message.text.isEmpty {
|
||||
messageString = stringWithAppliedEntities(message.text, entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false)
|
||||
} else {
|
||||
messageString = NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
||||
}
|
||||
if let inlineAuthorPrefix = inlineAuthorPrefix {
|
||||
composedString = NSMutableAttributedString()
|
||||
composedString.append(NSAttributedString(string: "\(inlineAuthorPrefix): ", font: textFont, textColor: theme.titleColor))
|
||||
composedString.append(NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor))
|
||||
composedString.append(messageString)
|
||||
} else {
|
||||
composedString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
||||
composedString = NSMutableAttributedString(attributedString: messageString)
|
||||
}
|
||||
|
||||
if let searchQuery = item.interaction.searchTextHighightState {
|
||||
@ -1730,6 +1747,24 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.size.height.isZero ? 0.0 : (authorLayout.size.height - 3.0))), size: textLayout.size)
|
||||
strongSelf.textNode.frame = textNodeFrame
|
||||
|
||||
if !textLayout.spoilers.isEmpty {
|
||||
let dustNode: InvisibleInkDustNode
|
||||
if let current = strongSelf.dustNode {
|
||||
dustNode = current
|
||||
} else {
|
||||
dustNode = InvisibleInkDustNode(textNode: nil)
|
||||
dustNode.isUserInteractionEnabled = false
|
||||
strongSelf.dustNode = dustNode
|
||||
strongSelf.contextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode)
|
||||
}
|
||||
dustNode.update(size: textNodeFrame.size, color: theme.messageTextColor, rects: textLayout.spoilers.map { $0.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
||||
dustNode.frame = textNodeFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||
|
||||
} else if let dustNode = strongSelf.dustNode {
|
||||
strongSelf.dustNode = nil
|
||||
dustNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
var animateInputActivitiesFrame = false
|
||||
let inputActivities = inputActivities?.filter({
|
||||
switch $0.1 {
|
||||
|
||||
@ -81,6 +81,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case enableDebugDataDisplay(Bool)
|
||||
case acceleratedStickers(Bool)
|
||||
case experimentalBackground(Bool)
|
||||
case snow(Bool)
|
||||
case playerEmbedding(Bool)
|
||||
case playlistPlayback(Bool)
|
||||
case voiceConference
|
||||
@ -102,7 +103,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.logging.rawValue
|
||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground:
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .snow:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .preferredVideoCodec:
|
||||
return DebugControllerSection.videoExperiments.rawValue
|
||||
@ -173,14 +174,16 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 29
|
||||
case .experimentalBackground:
|
||||
return 30
|
||||
case .playerEmbedding:
|
||||
case .snow:
|
||||
return 31
|
||||
case .playlistPlayback:
|
||||
case .playerEmbedding:
|
||||
return 32
|
||||
case .voiceConference:
|
||||
case .playlistPlayback:
|
||||
return 33
|
||||
case .voiceConference:
|
||||
return 34
|
||||
case let .preferredVideoCodec(index, _, _, _):
|
||||
return 34 + index
|
||||
return 35 + index
|
||||
case .disableVideoAspectScaling:
|
||||
return 100
|
||||
case .enableVoipTcp:
|
||||
@ -814,6 +817,16 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .snow(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Snow", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.snow = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .playerEmbedding(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Player Embedding", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
@ -927,6 +940,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
||||
entries.append(.snow(experimentalSettings.snow))
|
||||
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
||||
}
|
||||
|
||||
@ -1005,6 +1005,10 @@ public class TextNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let lineRange = CFRange(location: lastLineCharacterIndex, length: stringLength - lastLineCharacterIndex)
|
||||
var brokenLineRange = CFRange(location: lastLineCharacterIndex, length: lineCharacterCount)
|
||||
if brokenLineRange.location + brokenLineRange.length > attributedString.length {
|
||||
brokenLineRange.length = attributedString.length - brokenLineRange.location
|
||||
}
|
||||
if lineRange.length == 0 {
|
||||
break
|
||||
}
|
||||
@ -1033,7 +1037,7 @@ public class TextNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var headIndent: CGFloat = 0.0
|
||||
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
|
||||
attributedString.enumerateAttributes(in: NSMakeRange(brokenLineRange.location, brokenLineRange.length), options: []) { attributes, range, _ in
|
||||
if let _ = attributes[NSAttributedString.Key.init(rawValue: "TelegramSpoiler")] {
|
||||
var ascent: CGFloat = 0.0
|
||||
var descent: CGFloat = 0.0
|
||||
|
||||
@ -39,6 +39,7 @@ swift_library(
|
||||
"//submodules/Speak:Speak",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||
"//submodules/Translate:Translate",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -16,6 +16,7 @@ import PresentationDataUtils
|
||||
import ImageContentAnalysis
|
||||
import TextSelectionNode
|
||||
import Speak
|
||||
import Translate
|
||||
import ShareController
|
||||
import UndoUI
|
||||
|
||||
@ -352,6 +353,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
case .speak:
|
||||
speakText(string)
|
||||
case .translate:
|
||||
translateText(context: strongSelf.context, text: string)
|
||||
}
|
||||
})
|
||||
recognizedContentNode.barcodeAction = { [weak self] payload, rect in
|
||||
|
||||
@ -203,6 +203,7 @@ public enum RecognizedTextSelectionAction {
|
||||
case share
|
||||
case lookup
|
||||
case speak
|
||||
case translate
|
||||
}
|
||||
|
||||
public final class RecognizedTextSelectionNode: ASDisplayNode {
|
||||
@ -509,6 +510,12 @@ public final class RecognizedTextSelectionNode: ASDisplayNode {
|
||||
self?.performAction(selectedText, .lookup)
|
||||
let _ = self?.dismissSelection()
|
||||
}))
|
||||
// if #available(iOS 15.0, *) {
|
||||
// actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuTranslate, accessibilityLabel: self.strings.Conversation_ContextMenuTranslate), action: { [weak self] in
|
||||
// self?.performAction(selectedText, .translate)
|
||||
// let _ = self?.dismissSelection()
|
||||
// }))
|
||||
// }
|
||||
if isSpeakSelectionEnabled() {
|
||||
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuSpeak, accessibilityLabel: self.strings.Conversation_ContextMenuSpeak), action: { [weak self] in
|
||||
self?.performAction(selectedText, .speak)
|
||||
|
||||
@ -62,7 +62,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
|
||||
public var isRevealed = false
|
||||
|
||||
public init(textNode: TextNode) {
|
||||
public init(textNode: TextNode?) {
|
||||
self.textNode = textNode
|
||||
|
||||
self.emitterNode = ASDisplayNode()
|
||||
@ -144,7 +144,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
@objc private func tap(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
guard let (size, _, _) = self.currentParams, !self.isRevealed else {
|
||||
guard let (size, _, _) = self.currentParams, let textNode = self.textNode, !self.isRevealed else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -155,15 +155,15 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
self.emitterLayer?.setValue(position, forKeyPath: "emitterBehaviors.fingerAttractor.position")
|
||||
|
||||
Queue.mainQueue().after(0.1 * UIView.animationDurationFactor()) {
|
||||
self.textNode?.view.mask = self.textMaskNode.view
|
||||
self.textNode?.alpha = 1.0
|
||||
textNode.view.mask = self.textMaskNode.view
|
||||
textNode.alpha = 1.0
|
||||
|
||||
let radius = max(size.width, size.height)
|
||||
self.textSpotNode.frame = CGRect(x: position.x - radius / 2.0, y: position.y - radius / 2.0, width: radius, height: radius)
|
||||
|
||||
self.textSpotNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
self.textSpotNode.layer.animateScale(from: 0.1, to: 3.5, duration: 0.71, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
self?.textNode?.view.mask = nil
|
||||
self.textSpotNode.layer.animateScale(from: 0.1, to: 3.5, duration: 0.71, removeOnCompletion: false, completion: { _ in
|
||||
textNode.view.mask = nil
|
||||
})
|
||||
|
||||
self.emitterNode.view.mask = self.emitterMaskNode.view
|
||||
@ -187,7 +187,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
|
||||
let textLength = CGFloat((self.textNode?.cachedLayout?.attributedString?.string ?? "").count)
|
||||
let textLength = CGFloat((textNode.cachedLayout?.attributedString?.string ?? "").count)
|
||||
let timeToRead = min(45.0, ceil(max(4.0, textLength * 0.04)))
|
||||
Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) {
|
||||
self.isRevealed = false
|
||||
@ -195,11 +195,9 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear)
|
||||
transition.updateAlpha(node: self, alpha: 1.0)
|
||||
if let textNode = self.textNode {
|
||||
transition.updateAlpha(node: textNode, alpha: 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateEmitter() {
|
||||
guard let (size, color, rects) = self.currentParams else {
|
||||
|
||||
@ -268,7 +268,6 @@ public final class QrCodeScreen: ViewController {
|
||||
self.qrImageNode.cornerRadius = 16.0
|
||||
|
||||
self.qrIconNode = AnimatedStickerNode()
|
||||
|
||||
self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, mode: .direct(cachePathPrefix: nil))
|
||||
self.qrIconNode.visibility = true
|
||||
|
||||
@ -434,7 +433,6 @@ public final class QrCodeScreen: ViewController {
|
||||
let imageSide: CGFloat = 240.0
|
||||
let imageSize = CGSize(width: imageSide, height: imageSide)
|
||||
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))
|
||||
|
||||
let _ = imageApply()
|
||||
|
||||
let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0)
|
||||
|
||||
@ -521,6 +521,8 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
|
||||
|
||||
var totalSize: Int64 = 0
|
||||
|
||||
items.append(ActionSheetTextItem(title: presentationData.strings.ClearCache_ClearDescription))
|
||||
|
||||
for categoryId in validCategories {
|
||||
if let (_, size) = sizeIndex[categoryId] {
|
||||
let categorySize: Int64 = size
|
||||
|
||||
@ -150,7 +150,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
|
||||
|
||||
strongSelf.buttonNode.title = item.context.sharedContext.currentPresentationData.with { $0 }.strings.AuthSessions_LinkDesktopDevice
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.buttonNode.icon = generateTintedImage(image: UIImage(bundleImageName: "Settings/QrButtonIcon"), color: .white)
|
||||
strongSelf.buttonNode.icon = UIImage(bundleImageName: "Settings/QrButtonIcon")
|
||||
strongSelf.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: item.theme))
|
||||
}
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
|
||||
public var icon: UIImage? {
|
||||
didSet {
|
||||
self.iconNode.image = generateTintedImage(image: self.iconNode.image, color: self.theme.foregroundColor)
|
||||
self.iconNode.image = generateTintedImage(image: self.icon, color: self.theme.foregroundColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -322,6 +322,15 @@ public extension Message {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var textEntitiesAttribute: TextEntitiesMessageAttribute? {
|
||||
for attribute in self.attributes {
|
||||
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
return attribute
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func _internal_parseMediaAttachment(data: Data) -> Media? {
|
||||
|
||||
@ -229,12 +229,13 @@ public func stringForMediaKind(_ kind: MessageContentKind, strings: Presentation
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionStringForMessage(contentSettings: ContentSettings, message: EngineMessage, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: EnginePeer.Id) -> (String, Bool) {
|
||||
public func descriptionStringForMessage(contentSettings: ContentSettings, message: EngineMessage, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: EnginePeer.Id) -> (String, Bool, Bool) {
|
||||
let contentKind = messageContentKind(contentSettings: contentSettings, message: message, strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId)
|
||||
if !message.text.isEmpty && ![.expiredImage, .expiredVideo].contains(contentKind.key) {
|
||||
return (foldLineBreaks(message.text), false)
|
||||
return (foldLineBreaks(message.text), false, true)
|
||||
}
|
||||
return stringForMediaKind(contentKind, strings: strings)
|
||||
let result = stringForMediaKind(contentKind, strings: strings)
|
||||
return (result.0, result.1, false)
|
||||
}
|
||||
|
||||
public func foldLineBreaks(_ text: String) -> String {
|
||||
|
||||
@ -251,6 +251,7 @@ swift_library(
|
||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||
"//submodules/QrCodeUI:QrCodeUI",
|
||||
"//submodules/Components/ReactionListContextMenuContent:ReactionListContextMenuContent",
|
||||
"//submodules/Translate:Translate",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
|
||||
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Translate.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Translate.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "translate_24.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
127
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Translate.imageset/translate_24.pdf
vendored
Normal file
127
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Translate.imageset/translate_24.pdf
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 8.335022 7.270081 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
5.135226 14.200185 m
|
||||
4.875527 14.459883 4.454473 14.459883 4.194774 14.200185 c
|
||||
3.935075 13.940486 3.935075 13.519431 4.194774 13.259732 c
|
||||
6.194774 11.259732 l
|
||||
6.454473 11.000034 6.875527 11.000034 7.135226 11.259732 c
|
||||
7.394925 11.519431 7.394925 11.940486 7.135226 12.200185 c
|
||||
5.135226 14.200185 l
|
||||
h
|
||||
11.665000 9.064959 m
|
||||
10.317697 9.064959 l
|
||||
10.212673 6.276149 9.432839 4.057345 7.885226 2.509732 c
|
||||
7.853345 2.477852 7.821179 2.446297 7.788730 2.415067 c
|
||||
9.029876 1.748239 10.640882 1.394958 12.665000 1.394958 c
|
||||
13.032269 1.394958 13.330000 1.097227 13.330000 0.729958 c
|
||||
13.330000 0.362688 13.032269 0.064959 12.665000 0.064959 c
|
||||
10.248617 0.064959 8.228645 0.535731 6.665002 1.532337 c
|
||||
5.101360 0.535731 3.081383 0.064959 0.665000 0.064959 c
|
||||
0.297731 0.064959 0.000000 0.362688 0.000000 0.729958 c
|
||||
0.000000 1.097227 0.297731 1.394958 0.665000 1.394958 c
|
||||
2.689117 1.394958 4.300128 1.748238 5.541274 2.415066 c
|
||||
5.508824 2.446296 5.476655 2.477852 5.444774 2.509732 c
|
||||
3.897161 4.057345 3.117327 6.276149 3.012303 9.064959 c
|
||||
1.665000 9.064959 l
|
||||
1.297731 9.064959 1.000000 9.362689 1.000000 9.729959 c
|
||||
1.000000 10.097228 1.297731 10.394958 1.665000 10.394958 c
|
||||
3.665000 10.394958 l
|
||||
9.665000 10.394958 l
|
||||
11.665000 10.394958 l
|
||||
12.032269 10.394958 12.330000 10.097228 12.330000 9.729959 c
|
||||
12.330000 9.362689 12.032269 9.064959 11.665000 9.064959 c
|
||||
h
|
||||
4.343254 9.064959 m
|
||||
8.986746 9.064959 l
|
||||
8.882931 6.515748 8.171854 4.677263 6.944774 3.450184 c
|
||||
6.854816 3.360226 6.761571 3.273041 6.665002 3.188664 c
|
||||
6.568432 3.273041 6.475184 3.360226 6.385226 3.450184 c
|
||||
5.158146 4.677263 4.447069 6.515748 4.343254 9.064959 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.334839 3.245422 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
5.780663 12.006347 m
|
||||
5.678422 12.256270 5.435204 12.419556 5.165175 12.419556 c
|
||||
4.895147 12.419556 4.651928 12.256270 4.549686 12.006347 c
|
||||
0.049686 1.006347 l
|
||||
-0.089374 0.666422 0.073459 0.278128 0.413384 0.139067 c
|
||||
0.753309 0.000007 1.141604 0.162840 1.280664 0.502765 c
|
||||
2.747988 4.089556 l
|
||||
7.582363 4.089556 l
|
||||
9.049686 0.502765 l
|
||||
9.188747 0.162840 9.577042 0.000007 9.916966 0.139067 c
|
||||
10.256891 0.278128 10.419724 0.666422 10.280664 1.006347 c
|
||||
5.780663 12.006347 l
|
||||
h
|
||||
7.038272 5.419556 m
|
||||
5.165175 9.998237 l
|
||||
3.292078 5.419556 l
|
||||
7.038272 5.419556 l
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
2388
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000002478 00000 n
|
||||
0000002501 00000 n
|
||||
0000002674 00000 n
|
||||
0000002748 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2807
|
||||
%%EOF
|
||||
@ -60,6 +60,7 @@ import InviteLinksUI
|
||||
import Markdown
|
||||
import TelegramPermissionsUI
|
||||
import Speak
|
||||
import Translate
|
||||
import UniversalMediaPlayer
|
||||
import WallpaperBackgroundNode
|
||||
import ChatListUI
|
||||
@ -2759,6 +2760,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
case .speak:
|
||||
speakText(text.string)
|
||||
case .translate:
|
||||
translateText(context: context, text: text.string)
|
||||
}
|
||||
}, displayImportedMessageTooltip: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
|
||||
@ -760,6 +760,15 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
f(.default)
|
||||
})))
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuTranslate, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
controllerInteraction.performTextSelectionAction(0, NSAttributedString(string: message.text), .translate)
|
||||
f(.default)
|
||||
})))
|
||||
}
|
||||
|
||||
if isSpeakSelectionEnabled() && !message.text.isEmpty {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSpeak, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
|
||||
|
||||
@ -893,7 +893,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
if item.associatedData.isCopyProtectionEnabled {
|
||||
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
|
||||
needsShareButton = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1161,7 +1161,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
if item.associatedData.isCopyProtectionEnabled {
|
||||
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
|
||||
needsShareButton = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,7 +351,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
}
|
||||
}
|
||||
|
||||
if item.associatedData.isCopyProtectionEnabled {
|
||||
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
|
||||
needsShareButton = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,8 @@ import AccountContext
|
||||
import LocalizedPeerData
|
||||
import PhotoResources
|
||||
import TelegramStringFormatting
|
||||
import TextFormat
|
||||
import InvisibleInkDustNode
|
||||
|
||||
enum ChatMessageReplyInfoType {
|
||||
case bubble(incoming: Bool)
|
||||
@ -21,6 +23,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
private let lineNode: ASImageNode
|
||||
private var titleNode: TextNode?
|
||||
private var textNode: TextNode?
|
||||
private var dustNode: InvisibleInkDustNode?
|
||||
private var imageNode: TransformImageNode?
|
||||
private var previousMediaReference: AnyMediaReference?
|
||||
|
||||
@ -64,7 +67,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
let (textString, isMedia) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat, accountPeerId: context.account.peerId)
|
||||
let (textString, isMedia, isText) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat, accountPeerId: context.account.peerId)
|
||||
|
||||
let placeholderColor: UIColor = message.effectivelyIncoming(context.account.peerId) ? presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
|
||||
let titleColor: UIColor
|
||||
@ -89,6 +92,21 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
textColor = titleColor
|
||||
}
|
||||
|
||||
|
||||
let messageText: NSAttributedString
|
||||
if isText {
|
||||
let entities = (message.textEntitiesAttribute?.entities ?? []).filter { entity in
|
||||
if case .Spoiler = entity.type {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
messageText = stringWithAppliedEntities(message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false)
|
||||
} else {
|
||||
messageText = NSAttributedString(string: textString, font: textFont, textColor: textColor)
|
||||
}
|
||||
|
||||
var leftInset: CGFloat = 11.0
|
||||
let spacing: CGFloat = 2.0
|
||||
|
||||
@ -131,7 +149,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
let textInsets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0)
|
||||
|
||||
let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: textString, font: textFont, textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||
|
||||
let imageSide = titleLayout.size.height + textLayout.size.height - 16.0
|
||||
|
||||
@ -218,7 +236,26 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
node.imageNode?.captureProtected = message.isCopyProtected()
|
||||
|
||||
titleNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left, y: spacing - textInsets.top), size: titleLayout.size)
|
||||
textNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top), size: textLayout.size)
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: leftInset - textInsets.left, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top), size: textLayout.size)
|
||||
textNode.frame = textFrame
|
||||
|
||||
if !textLayout.spoilers.isEmpty {
|
||||
let dustNode: InvisibleInkDustNode
|
||||
if let current = node.dustNode {
|
||||
dustNode = current
|
||||
} else {
|
||||
dustNode = InvisibleInkDustNode(textNode: nil)
|
||||
dustNode.isUserInteractionEnabled = false
|
||||
node.dustNode = dustNode
|
||||
node.contentNode.insertSubnode(dustNode, aboveSubnode: textNode)
|
||||
}
|
||||
dustNode.update(size: textFrame.size, color: titleColor, rects: textLayout.spoilers.map { $0.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||
} else if let dustNode = node.dustNode {
|
||||
dustNode.removeFromSupernode()
|
||||
node.dustNode = nil
|
||||
}
|
||||
|
||||
node.lineNode.image = lineImage
|
||||
node.lineNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 3.0), size: CGSize(width: 2.0, height: max(0.0, size.height - 5.0)))
|
||||
|
||||
@ -436,7 +436,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
if item.associatedData.isCopyProtectionEnabled {
|
||||
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
|
||||
needsShareButton = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,6 +374,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
if let (_, spoilerTextApply) = spoilerTextLayoutAndApply {
|
||||
let spoilerTextNode = spoilerTextApply()
|
||||
if strongSelf.spoilerTextNode == nil {
|
||||
spoilerTextNode.alpha = 0.0
|
||||
spoilerTextNode.isUserInteractionEnabled = false
|
||||
spoilerTextNode.contentMode = .topLeft
|
||||
spoilerTextNode.contentsScale = UIScreenScale
|
||||
@ -384,8 +385,6 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
strongSelf.spoilerTextNode?.frame = textFrame
|
||||
strongSelf.spoilerTextNode?.isHidden = false
|
||||
strongSelf.spoilerTextNode?.alpha = 0.0
|
||||
|
||||
let dustNode: InvisibleInkDustNode
|
||||
if let current = strongSelf.dustNode {
|
||||
@ -395,11 +394,16 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.dustNode = dustNode
|
||||
strongSelf.insertSubnode(dustNode, aboveSubnode: spoilerTextNode)
|
||||
}
|
||||
dustNode.update(size: textFrame.size, color: messageTheme.primaryTextColor, rects: textLayout.spoilers.map { $0.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
||||
dustNode.update(size: textFrame.size, color: messageTheme.secondaryTextColor, rects: textLayout.spoilers.map { $0.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||
} else if let spoilerTextNode = strongSelf.spoilerTextNode {
|
||||
strongSelf.spoilerTextNode = nil
|
||||
spoilerTextNode.removeFromSupernode()
|
||||
|
||||
if let dustNode = strongSelf.dustNode {
|
||||
strongSelf.dustNode = nil
|
||||
dustNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
if let textSelectionNode = strongSelf.textSelectionNode {
|
||||
@ -632,7 +636,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
override func updateIsExtractedToContextPreview(_ value: Bool) {
|
||||
if value {
|
||||
if self.textSelectionNode == nil, let item = self.item, !item.associatedData.isCopyProtectionEnabled, let rootNode = item.controllerInteraction.chatControllerNode() {
|
||||
if self.textSelectionNode == nil, let item = self.item, !item.associatedData.isCopyProtectionEnabled && !item.message.isCopyProtected(), let rootNode = item.controllerInteraction.chatControllerNode() {
|
||||
let selectionColor: UIColor
|
||||
let knobColor: UIColor
|
||||
if item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
|
||||
1322
submodules/TelegramUI/Sources/ChatQrCodeScreen.swift
Normal file
1322
submodules/TelegramUI/Sources/ChatQrCodeScreen.swift
Normal file
File diff suppressed because it is too large
Load Diff
@ -394,6 +394,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
var updatedTheme = false
|
||||
var updatedWallpaper = false
|
||||
var updatedSelected = false
|
||||
var updatedNightMode = false
|
||||
|
||||
if currentItem?.emoticon != item.emoticon {
|
||||
updatedEmoticon = true
|
||||
@ -410,6 +411,9 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
if currentItem?.selected != item.selected {
|
||||
updatedSelected = true
|
||||
}
|
||||
if currentItem?.nightMode != item.nightMode {
|
||||
updatedNightMode = true
|
||||
}
|
||||
|
||||
let text = NSAttributedString(string: item.strings.Conversation_Theme_NoTheme, font: Font.semibold(15.0), textColor: item.theme.actionSheet.controlAccentColor)
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: text, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
@ -423,7 +427,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
if updatedThemeReference || updatedWallpaper {
|
||||
if updatedThemeReference || updatedWallpaper || updatedNightMode {
|
||||
if let themeReference = item.themeReference {
|
||||
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: nil, wallpaper: item.wallpaper, nightMode: item.nightMode, emoticon: true))
|
||||
strongSelf.imageNode.backgroundColor = nil
|
||||
|
||||
@ -169,7 +169,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
||||
if let currentEditMediaReference = self.currentEditMediaReference {
|
||||
effectiveMessage = effectiveMessage.withUpdatedMedia([currentEditMediaReference.media])
|
||||
}
|
||||
(text, _) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(effectiveMessage), strings: self.strings, nameDisplayOrder: self.nameDisplayOrder, dateTimeFormat: self.dateTimeFormat, accountPeerId: self.context.account.peerId)
|
||||
(text, _, _) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(effectiveMessage), strings: self.strings, nameDisplayOrder: self.nameDisplayOrder, dateTimeFormat: self.dateTimeFormat, accountPeerId: self.context.account.peerId)
|
||||
}
|
||||
|
||||
var updatedMediaReference: AnyMediaReference?
|
||||
|
||||
@ -5394,7 +5394,35 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
guard let data = self.data, let peer = data.peer, let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
controller.present(QrCodeScreen(context: self.context, updatedPresentationData: controller.updatedPresentationData, subject: .peer(peer: EnginePeer(peer))), in: .window(.root))
|
||||
|
||||
let animatedEmojiStickers = context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false)
|
||||
|> map { animatedEmoji -> [String: [StickerPackItem]] in
|
||||
var animatedEmojiStickers: [String: [StickerPackItem]] = [:]
|
||||
switch animatedEmoji {
|
||||
case let .result(_, items, _):
|
||||
for item in items {
|
||||
if let emoji = item.getStringRepresentationsOfIndexKeys().first {
|
||||
animatedEmojiStickers[emoji.basicEmoji.0] = [item]
|
||||
let strippedEmoji = emoji.basicEmoji.0.strippedEmoji
|
||||
if animatedEmojiStickers[strippedEmoji] == nil {
|
||||
animatedEmojiStickers[strippedEmoji] = [item]
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return animatedEmojiStickers
|
||||
}
|
||||
|
||||
let _ = (animatedEmojiStickers
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak controller] animatedEmojiStickers in
|
||||
if let strongSelf = self, let controller = controller {
|
||||
controller.present(ChatQrCodeScreen(context: strongSelf.context, animatedEmojiStickers: animatedEmojiStickers, peer: peer), in: .window(.root))
|
||||
}
|
||||
})
|
||||
|
||||
// controller.present(QrCodeScreen(context: self.context, updatedPresentationData: controller.updatedPresentationData, subject: .peer(peer: EnginePeer(peer))), in: .window(.root))
|
||||
}
|
||||
|
||||
fileprivate func openSettings(section: PeerInfoSettingsSection) {
|
||||
@ -6639,7 +6667,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .done, isForExpandedView: false))
|
||||
} else {
|
||||
if self.isSettings {
|
||||
if let addressName = self.data?.peer?.addressName, !addressName.isEmpty {
|
||||
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
||||
}
|
||||
|
||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
||||
|
||||
@ -106,7 +106,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
||||
authorName = EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
}
|
||||
if let message = message {
|
||||
(text, _) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: context.account.peerId)
|
||||
(text, _, _) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: context.account.peerId)
|
||||
}
|
||||
|
||||
var updatedMediaReference: AnyMediaReference?
|
||||
|
||||
@ -19,6 +19,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var enableDebugDataDisplay: Bool
|
||||
public var acceleratedStickers: Bool
|
||||
public var experimentalBackground: Bool
|
||||
public var snow: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(
|
||||
@ -36,7 +37,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
experimentalCompatibility: false,
|
||||
enableDebugDataDisplay: false,
|
||||
acceleratedStickers: false,
|
||||
experimentalBackground: false
|
||||
experimentalBackground: false,
|
||||
snow: false
|
||||
)
|
||||
}
|
||||
|
||||
@ -55,7 +57,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
experimentalCompatibility: Bool,
|
||||
enableDebugDataDisplay: Bool,
|
||||
acceleratedStickers: Bool,
|
||||
experimentalBackground: Bool
|
||||
experimentalBackground: Bool,
|
||||
snow: Bool
|
||||
) {
|
||||
self.keepChatNavigationStack = keepChatNavigationStack
|
||||
self.skipReadHistory = skipReadHistory
|
||||
@ -72,6 +75,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.enableDebugDataDisplay = enableDebugDataDisplay
|
||||
self.acceleratedStickers = acceleratedStickers
|
||||
self.experimentalBackground = experimentalBackground
|
||||
self.snow = snow
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -92,6 +96,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
|
||||
self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0
|
||||
self.experimentalBackground = (try container.decodeIfPresent(Int32.self, forKey: "experimentalBackground") ?? 0) != 0
|
||||
self.snow = (try container.decodeIfPresent(Int32.self, forKey: "snow") ?? 0) != 0
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -112,6 +117,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
|
||||
try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers")
|
||||
try container.encode((self.experimentalBackground ? 1 : 0) as Int32, forKey: "experimentalBackground")
|
||||
try container.encode((self.snow ? 1 : 0) as Int32, forKey: "snow")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -698,7 +698,7 @@ public func breakChatInputText(_ text: NSAttributedString) -> [NSAttributedStrin
|
||||
}
|
||||
}
|
||||
|
||||
private let markdownRegexFormat = "(^|\\s|\\n)(````?)([\\s\\S]+?)(````?)([\\s\\n\\.,:?!;]|$)|(^|\\s)(`|\\*\\*|__|~~)([^\\n]+?)\\7([\\s\\.,:?!;]|$)|@(\\d+)\\s*\\((.+?)\\)"
|
||||
private let markdownRegexFormat = "(^|\\s|\\n)(````?)([\\s\\S]+?)(````?)([\\s\\n\\.,:?!;]|$)|(^|\\s)(`|\\*\\*|__|~~|\\|\\|)([^\\n]+?)\\7([\\s\\.,:?!;]|$)|@(\\d+)\\s*\\((.+?)\\)"
|
||||
private let markdownRegex = try? NSRegularExpression(pattern: markdownRegexFormat, options: [.caseInsensitive, .anchorsMatchLines])
|
||||
|
||||
public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttributedString {
|
||||
|
||||
@ -188,6 +188,7 @@ public enum TextSelectionAction {
|
||||
case share
|
||||
case lookup
|
||||
case speak
|
||||
case translate
|
||||
}
|
||||
|
||||
public final class TextSelectionNode: ASDisplayNode {
|
||||
@ -501,6 +502,12 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
self?.performAction(attributedText, .lookup)
|
||||
self?.dismissSelection()
|
||||
}))
|
||||
// if #available(iOS 15.0, *) {
|
||||
// actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuTranslate, accessibilityLabel: self.strings.Conversation_ContextMenuTranslate), action: { [weak self] in
|
||||
// self?.performAction(attributedText, .translate)
|
||||
// self?.dismissSelection()
|
||||
// }))
|
||||
// }
|
||||
if isSpeakSelectionEnabled() {
|
||||
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuSpeak, accessibilityLabel: self.strings.Conversation_ContextMenuSpeak), action: { [weak self] in
|
||||
self?.performAction(attributedText, .speak)
|
||||
|
||||
19
submodules/Translate/BUILD
Normal file
19
submodules/Translate/BUILD
Normal file
@ -0,0 +1,19 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "Translate",
|
||||
module_name = "Translate",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
27
submodules/Translate/Sources/Translate.swift
Normal file
27
submodules/Translate/Sources/Translate.swift
Normal file
@ -0,0 +1,27 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AccountContext
|
||||
|
||||
// Incuding at least one Objective-C class in a swift file ensures that it doesn't get stripped by the linker
|
||||
private final class LinkHelperClass: NSObject {
|
||||
}
|
||||
|
||||
public func translateText(context: AccountContext, text: String) {
|
||||
guard !text.isEmpty else {
|
||||
return
|
||||
}
|
||||
if #available(iOS 15.0, *) {
|
||||
let textField = UITextField()
|
||||
textField.text = text
|
||||
if let navigationController = context.sharedContext.mainWindow?.viewController as? NavigationController, let topController = navigationController.topViewController as? ViewController {
|
||||
topController.view.addSubview(textField)
|
||||
textField.selectAll(nil)
|
||||
textField.perform(NSSelectorFromString(["_", "trans", "late:"].joined(separator: "")), with: nil)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
textField.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,6 +56,8 @@ public protocol WallpaperBackgroundNode: ASDisplayNode {
|
||||
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners)
|
||||
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool
|
||||
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode?
|
||||
|
||||
func makeDimmedNode() -> ASDisplayNode?
|
||||
}
|
||||
|
||||
final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode {
|
||||
@ -799,7 +801,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
if isFirstLayout && !self.frame.isEmpty {
|
||||
self.updateScale()
|
||||
|
||||
if false, self.newYearNode == nil {
|
||||
if self.context.sharedContext.immediateExperimentalUISettings.snow, self.newYearNode == nil {
|
||||
let newYearNode = WallpaperNewYearNode()
|
||||
self.addSubnode(newYearNode)
|
||||
self.newYearNode = newYearNode
|
||||
@ -897,6 +899,14 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
node.updateContents()
|
||||
return node
|
||||
}
|
||||
|
||||
func makeDimmedNode() -> ASDisplayNode? {
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
return GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private protocol WallpaperComponentView: AnyObject {
|
||||
@ -1695,6 +1705,10 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
|
||||
node.updateContents()
|
||||
return node
|
||||
}
|
||||
|
||||
func makeDimmedNode() -> ASDisplayNode? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private let sharedStorage = WallpaperBackgroundNodeMergedImpl.SharedStorage()
|
||||
@ -1738,7 +1752,7 @@ private class WallpaperNewYearNode: ASDisplayNode {
|
||||
cell1.scale = 0.04
|
||||
cell1.scaleRange = 0.15
|
||||
cell1.color = UIColor.white.withAlphaComponent(0.88).cgColor
|
||||
cell1.alphaRange = -0.2
|
||||
// cell1.alphaRange = -0.2
|
||||
|
||||
particlesLayer.emitterCells = [cell1]
|
||||
}
|
||||
|
||||
@ -1300,7 +1300,23 @@ public func themeImage(account: Account, accountManager: AccountManager<Telegram
|
||||
}
|
||||
}
|
||||
|
||||
public func themeIconImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil, nightMode: Bool? = nil, emoticon: Bool = false, large: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
private let qrIconImage: UIImage = {
|
||||
return generateImage(CGSize(width: 36.0, height: 36.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 9.0).cgPath)
|
||||
context.fillPath()
|
||||
|
||||
if let image = UIImage(bundleImageName: "Settings/QrButtonIcon")?.cgImage {
|
||||
context.clip(to: CGRect(x: 6.0, y: 6.0, width: 24.0, height: 24.0), mask: image)
|
||||
context.clear(bounds)
|
||||
}
|
||||
})!
|
||||
}()
|
||||
|
||||
public func themeIconImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil, nightMode: Bool? = nil, emoticon: Bool = false, large: Bool = false, qr: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError>
|
||||
|
||||
var reference: MediaResourceReference?
|
||||
@ -1552,6 +1568,11 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
||||
}
|
||||
}
|
||||
|
||||
if qr {
|
||||
if let image = qrIconImage.cgImage {
|
||||
c.draw(image, in: CGRect(x: floor((drawingRect.width - 36.0) / 2.0), y: floor((drawingRect.height - 36.0) / 2.0), width: 36.0, height: 36.0))
|
||||
}
|
||||
} else {
|
||||
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
|
||||
c.scaleBy(x: 1.0, y: -1.0)
|
||||
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
|
||||
@ -1682,6 +1703,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
||||
c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
addCorners(context, arguments: arguments)
|
||||
return context
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user