mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-09 02:03:43 +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";
|
"ChatList.Archive" = "Archive";
|
||||||
|
|
||||||
"TextFormat.Spoiler" = "Spoiler";
|
"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/TelegramStringFormatting:TelegramStringFormatting",
|
||||||
"//submodules/TelegramCallsUI:TelegramCallsUI",
|
"//submodules/TelegramCallsUI:TelegramCallsUI",
|
||||||
"//submodules/StickerResources:StickerResources",
|
"//submodules/StickerResources:StickerResources",
|
||||||
|
"//submodules/TextFormat:TextFormat",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import PhotoResources
|
|||||||
import ChatListSearchItemNode
|
import ChatListSearchItemNode
|
||||||
import ContextUI
|
import ContextUI
|
||||||
import ChatInterfaceState
|
import ChatInterfaceState
|
||||||
|
import TextFormat
|
||||||
|
import InvisibleInkDustNode
|
||||||
|
|
||||||
public enum ChatListItemContent {
|
public enum ChatListItemContent {
|
||||||
public final class DraftState: Equatable {
|
public final class DraftState: Equatable {
|
||||||
@ -427,6 +429,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let measureNode: TextNode
|
let measureNode: TextNode
|
||||||
private var currentItemHeight: CGFloat?
|
private var currentItemHeight: CGFloat?
|
||||||
let textNode: TextNode
|
let textNode: TextNode
|
||||||
|
var dustNode: InvisibleInkDustNode?
|
||||||
let inputActivitiesNode: ChatListInputActivitiesNode
|
let inputActivitiesNode: ChatListInputActivitiesNode
|
||||||
let dateNode: TextNode
|
let dateNode: TextNode
|
||||||
let separatorNode: ASDisplayNode
|
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)
|
attributedText = NSAttributedString(string: foldLineBreaks(draftText.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor)
|
||||||
} else if let message = messages.last {
|
} else if let message = messages.last {
|
||||||
var composedString: NSMutableAttributedString
|
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 {
|
if let inlineAuthorPrefix = inlineAuthorPrefix {
|
||||||
composedString = NSMutableAttributedString()
|
composedString = NSMutableAttributedString()
|
||||||
composedString.append(NSAttributedString(string: "\(inlineAuthorPrefix): ", font: textFont, textColor: theme.titleColor))
|
composedString.append(NSAttributedString(string: "\(inlineAuthorPrefix): ", font: textFont, textColor: theme.titleColor))
|
||||||
composedString.append(NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor))
|
composedString.append(messageString)
|
||||||
} else {
|
} else {
|
||||||
composedString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
composedString = NSMutableAttributedString(attributedString: messageString)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let searchQuery = item.interaction.searchTextHighightState {
|
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)
|
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
|
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
|
var animateInputActivitiesFrame = false
|
||||||
let inputActivities = inputActivities?.filter({
|
let inputActivities = inputActivities?.filter({
|
||||||
switch $0.1 {
|
switch $0.1 {
|
||||||
|
|||||||
@ -81,6 +81,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case enableDebugDataDisplay(Bool)
|
case enableDebugDataDisplay(Bool)
|
||||||
case acceleratedStickers(Bool)
|
case acceleratedStickers(Bool)
|
||||||
case experimentalBackground(Bool)
|
case experimentalBackground(Bool)
|
||||||
|
case snow(Bool)
|
||||||
case playerEmbedding(Bool)
|
case playerEmbedding(Bool)
|
||||||
case playlistPlayback(Bool)
|
case playlistPlayback(Bool)
|
||||||
case voiceConference
|
case voiceConference
|
||||||
@ -102,7 +103,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.logging.rawValue
|
return DebugControllerSection.logging.rawValue
|
||||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||||
return DebugControllerSection.experiments.rawValue
|
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
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .preferredVideoCodec:
|
case .preferredVideoCodec:
|
||||||
return DebugControllerSection.videoExperiments.rawValue
|
return DebugControllerSection.videoExperiments.rawValue
|
||||||
@ -173,14 +174,16 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 29
|
return 29
|
||||||
case .experimentalBackground:
|
case .experimentalBackground:
|
||||||
return 30
|
return 30
|
||||||
case .playerEmbedding:
|
case .snow:
|
||||||
return 31
|
return 31
|
||||||
case .playlistPlayback:
|
case .playerEmbedding:
|
||||||
return 32
|
return 32
|
||||||
case .voiceConference:
|
case .playlistPlayback:
|
||||||
return 33
|
return 33
|
||||||
|
case .voiceConference:
|
||||||
|
return 34
|
||||||
case let .preferredVideoCodec(index, _, _, _):
|
case let .preferredVideoCodec(index, _, _, _):
|
||||||
return 34 + index
|
return 35 + index
|
||||||
case .disableVideoAspectScaling:
|
case .disableVideoAspectScaling:
|
||||||
return 100
|
return 100
|
||||||
case .enableVoipTcp:
|
case .enableVoipTcp:
|
||||||
@ -814,6 +817,16 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).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):
|
case let .playerEmbedding(value):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: "Player Embedding", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: "Player Embedding", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction 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(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||||
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
||||||
|
entries.append(.snow(experimentalSettings.snow))
|
||||||
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
||||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1005,6 +1005,10 @@ public class TextNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let lineRange = CFRange(location: lastLineCharacterIndex, length: stringLength - lastLineCharacterIndex)
|
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 {
|
if lineRange.length == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1033,7 +1037,7 @@ public class TextNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var headIndent: CGFloat = 0.0
|
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")] {
|
if let _ = attributes[NSAttributedString.Key.init(rawValue: "TelegramSpoiler")] {
|
||||||
var ascent: CGFloat = 0.0
|
var ascent: CGFloat = 0.0
|
||||||
var descent: CGFloat = 0.0
|
var descent: CGFloat = 0.0
|
||||||
|
|||||||
@ -39,6 +39,7 @@ swift_library(
|
|||||||
"//submodules/Speak:Speak",
|
"//submodules/Speak:Speak",
|
||||||
"//submodules/UndoUI:UndoUI",
|
"//submodules/UndoUI:UndoUI",
|
||||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||||
|
"//submodules/Translate:Translate",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import PresentationDataUtils
|
|||||||
import ImageContentAnalysis
|
import ImageContentAnalysis
|
||||||
import TextSelectionNode
|
import TextSelectionNode
|
||||||
import Speak
|
import Speak
|
||||||
|
import Translate
|
||||||
import ShareController
|
import ShareController
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
|
||||||
@ -352,6 +353,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
case .speak:
|
case .speak:
|
||||||
speakText(string)
|
speakText(string)
|
||||||
|
case .translate:
|
||||||
|
translateText(context: strongSelf.context, text: string)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
recognizedContentNode.barcodeAction = { [weak self] payload, rect in
|
recognizedContentNode.barcodeAction = { [weak self] payload, rect in
|
||||||
|
|||||||
@ -203,6 +203,7 @@ public enum RecognizedTextSelectionAction {
|
|||||||
case share
|
case share
|
||||||
case lookup
|
case lookup
|
||||||
case speak
|
case speak
|
||||||
|
case translate
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class RecognizedTextSelectionNode: ASDisplayNode {
|
public final class RecognizedTextSelectionNode: ASDisplayNode {
|
||||||
@ -509,6 +510,12 @@ public final class RecognizedTextSelectionNode: ASDisplayNode {
|
|||||||
self?.performAction(selectedText, .lookup)
|
self?.performAction(selectedText, .lookup)
|
||||||
let _ = self?.dismissSelection()
|
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() {
|
if isSpeakSelectionEnabled() {
|
||||||
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuSpeak, accessibilityLabel: self.strings.Conversation_ContextMenuSpeak), action: { [weak self] in
|
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuSpeak, accessibilityLabel: self.strings.Conversation_ContextMenuSpeak), action: { [weak self] in
|
||||||
self?.performAction(selectedText, .speak)
|
self?.performAction(selectedText, .speak)
|
||||||
|
|||||||
@ -62,7 +62,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
|
|
||||||
public var isRevealed = false
|
public var isRevealed = false
|
||||||
|
|
||||||
public init(textNode: TextNode) {
|
public init(textNode: TextNode?) {
|
||||||
self.textNode = textNode
|
self.textNode = textNode
|
||||||
|
|
||||||
self.emitterNode = ASDisplayNode()
|
self.emitterNode = ASDisplayNode()
|
||||||
@ -144,7 +144,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func tap(_ gestureRecognizer: UITapGestureRecognizer) {
|
@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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,15 +155,15 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
self.emitterLayer?.setValue(position, forKeyPath: "emitterBehaviors.fingerAttractor.position")
|
self.emitterLayer?.setValue(position, forKeyPath: "emitterBehaviors.fingerAttractor.position")
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1 * UIView.animationDurationFactor()) {
|
Queue.mainQueue().after(0.1 * UIView.animationDurationFactor()) {
|
||||||
self.textNode?.view.mask = self.textMaskNode.view
|
textNode.view.mask = self.textMaskNode.view
|
||||||
self.textNode?.alpha = 1.0
|
textNode.alpha = 1.0
|
||||||
|
|
||||||
let radius = max(size.width, size.height)
|
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.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.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.textSpotNode.layer.animateScale(from: 0.1, to: 3.5, duration: 0.71, removeOnCompletion: false, completion: { _ in
|
||||||
self?.textNode?.view.mask = nil
|
textNode.view.mask = nil
|
||||||
})
|
})
|
||||||
|
|
||||||
self.emitterNode.view.mask = self.emitterMaskNode.view
|
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)))
|
let timeToRead = min(45.0, ceil(max(4.0, textLength * 0.04)))
|
||||||
Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) {
|
Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) {
|
||||||
self.isRevealed = false
|
self.isRevealed = false
|
||||||
@ -195,11 +195,9 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
|
|
||||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear)
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear)
|
||||||
transition.updateAlpha(node: self, alpha: 1.0)
|
transition.updateAlpha(node: self, alpha: 1.0)
|
||||||
if let textNode = self.textNode {
|
|
||||||
transition.updateAlpha(node: textNode, alpha: 0.0)
|
transition.updateAlpha(node: textNode, alpha: 0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private func updateEmitter() {
|
private func updateEmitter() {
|
||||||
guard let (size, color, rects) = self.currentParams else {
|
guard let (size, color, rects) = self.currentParams else {
|
||||||
|
|||||||
@ -268,7 +268,6 @@ public final class QrCodeScreen: ViewController {
|
|||||||
self.qrImageNode.cornerRadius = 16.0
|
self.qrImageNode.cornerRadius = 16.0
|
||||||
|
|
||||||
self.qrIconNode = AnimatedStickerNode()
|
self.qrIconNode = AnimatedStickerNode()
|
||||||
|
|
||||||
self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, mode: .direct(cachePathPrefix: nil))
|
self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, mode: .direct(cachePathPrefix: nil))
|
||||||
self.qrIconNode.visibility = true
|
self.qrIconNode.visibility = true
|
||||||
|
|
||||||
@ -434,7 +433,6 @@ public final class QrCodeScreen: ViewController {
|
|||||||
let imageSide: CGFloat = 240.0
|
let imageSide: CGFloat = 240.0
|
||||||
let imageSize = CGSize(width: imageSide, height: imageSide)
|
let imageSize = CGSize(width: imageSide, height: imageSide)
|
||||||
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))
|
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))
|
||||||
|
|
||||||
let _ = imageApply()
|
let _ = imageApply()
|
||||||
|
|
||||||
let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0)
|
let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0)
|
||||||
|
|||||||
@ -521,6 +521,8 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
|
|||||||
|
|
||||||
var totalSize: Int64 = 0
|
var totalSize: Int64 = 0
|
||||||
|
|
||||||
|
items.append(ActionSheetTextItem(title: presentationData.strings.ClearCache_ClearDescription))
|
||||||
|
|
||||||
for categoryId in validCategories {
|
for categoryId in validCategories {
|
||||||
if let (_, size) = sizeIndex[categoryId] {
|
if let (_, size) = sizeIndex[categoryId] {
|
||||||
let categorySize: Int64 = size
|
let categorySize: Int64 = size
|
||||||
|
|||||||
@ -150,7 +150,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
strongSelf.buttonNode.title = item.context.sharedContext.currentPresentationData.with { $0 }.strings.AuthSessions_LinkDesktopDevice
|
strongSelf.buttonNode.title = item.context.sharedContext.currentPresentationData.with { $0 }.strings.AuthSessions_LinkDesktopDevice
|
||||||
if let _ = updatedTheme {
|
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))
|
strongSelf.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: item.theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -69,7 +69,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
|
|
||||||
public var icon: UIImage? {
|
public var icon: UIImage? {
|
||||||
didSet {
|
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
|
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? {
|
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)
|
let contentKind = messageContentKind(contentSettings: contentSettings, message: message, strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId)
|
||||||
if !message.text.isEmpty && ![.expiredImage, .expiredVideo].contains(contentKind.key) {
|
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 {
|
public func foldLineBreaks(_ text: String) -> String {
|
||||||
|
|||||||
@ -251,6 +251,7 @@ swift_library(
|
|||||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||||
"//submodules/QrCodeUI:QrCodeUI",
|
"//submodules/QrCodeUI:QrCodeUI",
|
||||||
"//submodules/Components/ReactionListContextMenuContent:ReactionListContextMenuContent",
|
"//submodules/Components/ReactionListContextMenuContent:ReactionListContextMenuContent",
|
||||||
|
"//submodules/Translate:Translate",
|
||||||
] + select({
|
] + select({
|
||||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
"@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 Markdown
|
||||||
import TelegramPermissionsUI
|
import TelegramPermissionsUI
|
||||||
import Speak
|
import Speak
|
||||||
|
import Translate
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
import WallpaperBackgroundNode
|
import WallpaperBackgroundNode
|
||||||
import ChatListUI
|
import ChatListUI
|
||||||
@ -2759,6 +2760,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
case .speak:
|
case .speak:
|
||||||
speakText(text.string)
|
speakText(text.string)
|
||||||
|
case .translate:
|
||||||
|
translateText(context: context, text: text.string)
|
||||||
}
|
}
|
||||||
}, displayImportedMessageTooltip: { [weak self] _ in
|
}, displayImportedMessageTooltip: { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
|
|||||||
@ -760,6 +760,15 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
f(.default)
|
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 {
|
if isSpeakSelectionEnabled() && !message.text.isEmpty {
|
||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSpeak, icon: { theme in
|
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)
|
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
|
needsShareButton = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1161,7 +1161,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.associatedData.isCopyProtectionEnabled {
|
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
|
||||||
needsShareButton = false
|
needsShareButton = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -351,7 +351,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.associatedData.isCopyProtectionEnabled {
|
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
|
||||||
needsShareButton = false
|
needsShareButton = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import AccountContext
|
|||||||
import LocalizedPeerData
|
import LocalizedPeerData
|
||||||
import PhotoResources
|
import PhotoResources
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
|
import TextFormat
|
||||||
|
import InvisibleInkDustNode
|
||||||
|
|
||||||
enum ChatMessageReplyInfoType {
|
enum ChatMessageReplyInfoType {
|
||||||
case bubble(incoming: Bool)
|
case bubble(incoming: Bool)
|
||||||
@ -21,6 +23,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
private let lineNode: ASImageNode
|
private let lineNode: ASImageNode
|
||||||
private var titleNode: TextNode?
|
private var titleNode: TextNode?
|
||||||
private var textNode: TextNode?
|
private var textNode: TextNode?
|
||||||
|
private var dustNode: InvisibleInkDustNode?
|
||||||
private var imageNode: TransformImageNode?
|
private var imageNode: TransformImageNode?
|
||||||
private var previousMediaReference: AnyMediaReference?
|
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 placeholderColor: UIColor = message.effectivelyIncoming(context.account.peerId) ? presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
|
||||||
let titleColor: UIColor
|
let titleColor: UIColor
|
||||||
@ -89,6 +92,21 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
textColor = titleColor
|
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
|
var leftInset: CGFloat = 11.0
|
||||||
let spacing: CGFloat = 2.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 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 (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
|
let imageSide = titleLayout.size.height + textLayout.size.height - 16.0
|
||||||
|
|
||||||
@ -218,7 +236,26 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
node.imageNode?.captureProtected = message.isCopyProtected()
|
node.imageNode?.captureProtected = message.isCopyProtected()
|
||||||
|
|
||||||
titleNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left, y: spacing - textInsets.top), size: titleLayout.size)
|
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.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)))
|
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
|
needsShareButton = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -374,6 +374,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let (_, spoilerTextApply) = spoilerTextLayoutAndApply {
|
if let (_, spoilerTextApply) = spoilerTextLayoutAndApply {
|
||||||
let spoilerTextNode = spoilerTextApply()
|
let spoilerTextNode = spoilerTextApply()
|
||||||
if strongSelf.spoilerTextNode == nil {
|
if strongSelf.spoilerTextNode == nil {
|
||||||
|
spoilerTextNode.alpha = 0.0
|
||||||
spoilerTextNode.isUserInteractionEnabled = false
|
spoilerTextNode.isUserInteractionEnabled = false
|
||||||
spoilerTextNode.contentMode = .topLeft
|
spoilerTextNode.contentMode = .topLeft
|
||||||
spoilerTextNode.contentsScale = UIScreenScale
|
spoilerTextNode.contentsScale = UIScreenScale
|
||||||
@ -384,8 +385,6 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.spoilerTextNode?.frame = textFrame
|
strongSelf.spoilerTextNode?.frame = textFrame
|
||||||
strongSelf.spoilerTextNode?.isHidden = false
|
|
||||||
strongSelf.spoilerTextNode?.alpha = 0.0
|
|
||||||
|
|
||||||
let dustNode: InvisibleInkDustNode
|
let dustNode: InvisibleInkDustNode
|
||||||
if let current = strongSelf.dustNode {
|
if let current = strongSelf.dustNode {
|
||||||
@ -395,11 +394,16 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
strongSelf.dustNode = dustNode
|
strongSelf.dustNode = dustNode
|
||||||
strongSelf.insertSubnode(dustNode, aboveSubnode: spoilerTextNode)
|
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)
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
} else if let spoilerTextNode = strongSelf.spoilerTextNode {
|
} else if let spoilerTextNode = strongSelf.spoilerTextNode {
|
||||||
strongSelf.spoilerTextNode = nil
|
strongSelf.spoilerTextNode = nil
|
||||||
spoilerTextNode.removeFromSupernode()
|
spoilerTextNode.removeFromSupernode()
|
||||||
|
|
||||||
|
if let dustNode = strongSelf.dustNode {
|
||||||
|
strongSelf.dustNode = nil
|
||||||
|
dustNode.removeFromSupernode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let textSelectionNode = strongSelf.textSelectionNode {
|
if let textSelectionNode = strongSelf.textSelectionNode {
|
||||||
@ -632,7 +636,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
override func updateIsExtractedToContextPreview(_ value: Bool) {
|
override func updateIsExtractedToContextPreview(_ value: Bool) {
|
||||||
if value {
|
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 selectionColor: UIColor
|
||||||
let knobColor: UIColor
|
let knobColor: UIColor
|
||||||
if item.message.effectivelyIncoming(item.context.account.peerId) {
|
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 updatedTheme = false
|
||||||
var updatedWallpaper = false
|
var updatedWallpaper = false
|
||||||
var updatedSelected = false
|
var updatedSelected = false
|
||||||
|
var updatedNightMode = false
|
||||||
|
|
||||||
if currentItem?.emoticon != item.emoticon {
|
if currentItem?.emoticon != item.emoticon {
|
||||||
updatedEmoticon = true
|
updatedEmoticon = true
|
||||||
@ -410,6 +411,9 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
|||||||
if currentItem?.selected != item.selected {
|
if currentItem?.selected != item.selected {
|
||||||
updatedSelected = true
|
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 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()))
|
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 {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
|
||||||
if updatedThemeReference || updatedWallpaper {
|
if updatedThemeReference || updatedWallpaper || updatedNightMode {
|
||||||
if let themeReference = item.themeReference {
|
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.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
|
strongSelf.imageNode.backgroundColor = nil
|
||||||
|
|||||||
@ -169,7 +169,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
if let currentEditMediaReference = self.currentEditMediaReference {
|
if let currentEditMediaReference = self.currentEditMediaReference {
|
||||||
effectiveMessage = effectiveMessage.withUpdatedMedia([currentEditMediaReference.media])
|
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?
|
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 {
|
guard let data = self.data, let peer = data.peer, let controller = self.controller else {
|
||||||
return
|
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) {
|
fileprivate func openSettings(section: PeerInfoSettingsSection) {
|
||||||
@ -6639,7 +6667,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .done, isForExpandedView: false))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .done, isForExpandedView: false))
|
||||||
} else {
|
} else {
|
||||||
if self.isSettings {
|
if self.isSettings {
|
||||||
|
if let addressName = self.data?.peer?.addressName, !addressName.isEmpty {
|
||||||
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
||||||
|
}
|
||||||
|
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
||||||
|
|||||||
@ -106,7 +106,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
authorName = EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
authorName = EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||||
}
|
}
|
||||||
if let message = message {
|
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?
|
var updatedMediaReference: AnyMediaReference?
|
||||||
|
|||||||
@ -19,6 +19,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
public var enableDebugDataDisplay: Bool
|
public var enableDebugDataDisplay: Bool
|
||||||
public var acceleratedStickers: Bool
|
public var acceleratedStickers: Bool
|
||||||
public var experimentalBackground: Bool
|
public var experimentalBackground: Bool
|
||||||
|
public var snow: Bool
|
||||||
|
|
||||||
public static var defaultSettings: ExperimentalUISettings {
|
public static var defaultSettings: ExperimentalUISettings {
|
||||||
return ExperimentalUISettings(
|
return ExperimentalUISettings(
|
||||||
@ -36,7 +37,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
experimentalCompatibility: false,
|
experimentalCompatibility: false,
|
||||||
enableDebugDataDisplay: false,
|
enableDebugDataDisplay: false,
|
||||||
acceleratedStickers: false,
|
acceleratedStickers: false,
|
||||||
experimentalBackground: false
|
experimentalBackground: false,
|
||||||
|
snow: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +57,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
experimentalCompatibility: Bool,
|
experimentalCompatibility: Bool,
|
||||||
enableDebugDataDisplay: Bool,
|
enableDebugDataDisplay: Bool,
|
||||||
acceleratedStickers: Bool,
|
acceleratedStickers: Bool,
|
||||||
experimentalBackground: Bool
|
experimentalBackground: Bool,
|
||||||
|
snow: Bool
|
||||||
) {
|
) {
|
||||||
self.keepChatNavigationStack = keepChatNavigationStack
|
self.keepChatNavigationStack = keepChatNavigationStack
|
||||||
self.skipReadHistory = skipReadHistory
|
self.skipReadHistory = skipReadHistory
|
||||||
@ -72,6 +75,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.enableDebugDataDisplay = enableDebugDataDisplay
|
self.enableDebugDataDisplay = enableDebugDataDisplay
|
||||||
self.acceleratedStickers = acceleratedStickers
|
self.acceleratedStickers = acceleratedStickers
|
||||||
self.experimentalBackground = experimentalBackground
|
self.experimentalBackground = experimentalBackground
|
||||||
|
self.snow = snow
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
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.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
|
||||||
self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 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.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 {
|
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.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
|
||||||
try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers")
|
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.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])
|
private let markdownRegex = try? NSRegularExpression(pattern: markdownRegexFormat, options: [.caseInsensitive, .anchorsMatchLines])
|
||||||
|
|
||||||
public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttributedString {
|
public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttributedString {
|
||||||
|
|||||||
@ -188,6 +188,7 @@ public enum TextSelectionAction {
|
|||||||
case share
|
case share
|
||||||
case lookup
|
case lookup
|
||||||
case speak
|
case speak
|
||||||
|
case translate
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class TextSelectionNode: ASDisplayNode {
|
public final class TextSelectionNode: ASDisplayNode {
|
||||||
@ -501,6 +502,12 @@ public final class TextSelectionNode: ASDisplayNode {
|
|||||||
self?.performAction(attributedText, .lookup)
|
self?.performAction(attributedText, .lookup)
|
||||||
self?.dismissSelection()
|
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() {
|
if isSpeakSelectionEnabled() {
|
||||||
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuSpeak, accessibilityLabel: self.strings.Conversation_ContextMenuSpeak), action: { [weak self] in
|
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuSpeak, accessibilityLabel: self.strings.Conversation_ContextMenuSpeak), action: { [weak self] in
|
||||||
self?.performAction(attributedText, .speak)
|
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 updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners)
|
||||||
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool
|
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool
|
||||||
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode?
|
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode?
|
||||||
|
|
||||||
|
func makeDimmedNode() -> ASDisplayNode?
|
||||||
}
|
}
|
||||||
|
|
||||||
final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode {
|
final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode {
|
||||||
@ -799,7 +801,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
if isFirstLayout && !self.frame.isEmpty {
|
if isFirstLayout && !self.frame.isEmpty {
|
||||||
self.updateScale()
|
self.updateScale()
|
||||||
|
|
||||||
if false, self.newYearNode == nil {
|
if self.context.sharedContext.immediateExperimentalUISettings.snow, self.newYearNode == nil {
|
||||||
let newYearNode = WallpaperNewYearNode()
|
let newYearNode = WallpaperNewYearNode()
|
||||||
self.addSubnode(newYearNode)
|
self.addSubnode(newYearNode)
|
||||||
self.newYearNode = newYearNode
|
self.newYearNode = newYearNode
|
||||||
@ -897,6 +899,14 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
node.updateContents()
|
node.updateContents()
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeDimmedNode() -> ASDisplayNode? {
|
||||||
|
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||||
|
return GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private protocol WallpaperComponentView: AnyObject {
|
private protocol WallpaperComponentView: AnyObject {
|
||||||
@ -1695,6 +1705,10 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
|
|||||||
node.updateContents()
|
node.updateContents()
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeDimmedNode() -> ASDisplayNode? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let sharedStorage = WallpaperBackgroundNodeMergedImpl.SharedStorage()
|
private let sharedStorage = WallpaperBackgroundNodeMergedImpl.SharedStorage()
|
||||||
@ -1738,7 +1752,7 @@ private class WallpaperNewYearNode: ASDisplayNode {
|
|||||||
cell1.scale = 0.04
|
cell1.scale = 0.04
|
||||||
cell1.scaleRange = 0.15
|
cell1.scaleRange = 0.15
|
||||||
cell1.color = UIColor.white.withAlphaComponent(0.88).cgColor
|
cell1.color = UIColor.white.withAlphaComponent(0.88).cgColor
|
||||||
cell1.alphaRange = -0.2
|
// cell1.alphaRange = -0.2
|
||||||
|
|
||||||
particlesLayer.emitterCells = [cell1]
|
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>
|
let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError>
|
||||||
|
|
||||||
var reference: MediaResourceReference?
|
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.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
|
||||||
c.scaleBy(x: 1.0, y: -1.0)
|
c.scaleBy(x: 1.0, y: -1.0)
|
||||||
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.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))
|
c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
addCorners(context, arguments: arguments)
|
addCorners(context, arguments: arguments)
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user