mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-24 17:12:56 +00:00
Added caption scroll in media viewer
This commit is contained in:
parent
6b34f748d7
commit
58eb595460
@ -2490,6 +2490,13 @@
|
||||
path = TelegramUI/Resources/Animations;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
093857B422464B2700EB6A54 /* Stats */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Stats;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0941A99E210B053300EBE194 /* Open In */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -4325,6 +4332,7 @@
|
||||
D0EE97131D88BB1A006C18E1 /* Peer Info */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
093857B422464B2700EB6A54 /* Stats */,
|
||||
D0B843CC1DA903BB005F29E1 /* PeerInfoController.swift */,
|
||||
D0486F091E523C8500091F0C /* GroupInfoController.swift */,
|
||||
D03E5E0E1E55F8B90029569A /* ChannelVisibilityController.swift */,
|
||||
|
||||
@ -99,7 +99,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let width = size.width
|
||||
var panelHeight: CGFloat = 44.0 + bottomInset
|
||||
panelHeight += contentInset
|
||||
|
||||
@ -296,7 +296,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
return false
|
||||
}
|
||||
strongSelf.commitPurposefulAction()
|
||||
strongSelf.videoUnmuteTooltipController?.dismiss()
|
||||
strongSelf.dismissAllTooltips()
|
||||
|
||||
var openMessageByAction: Bool = false
|
||||
|
||||
@ -860,7 +860,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
})
|
||||
}
|
||||
}, longTap: { [weak self] action, messageId in
|
||||
}, longTap: { [weak self] action, message in
|
||||
if let strongSelf = self {
|
||||
switch action {
|
||||
case let .url(url):
|
||||
@ -1027,7 +1027,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
case let .timecode(timecode, text):
|
||||
guard let messageId = messageId else {
|
||||
guard let message = message else {
|
||||
return
|
||||
}
|
||||
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
|
||||
@ -1036,7 +1036,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.seekToTimecode(messageId, timecode)
|
||||
strongSelf.controllerInteraction?.seekToTimecode(message, timecode, true)
|
||||
}
|
||||
}),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogCopy, color: .accent, action: { [weak actionSheet] in
|
||||
@ -1216,21 +1216,22 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}))
|
||||
}
|
||||
}
|
||||
}, seekToTimecode: { [weak self] messageId, timestamp in
|
||||
}, seekToTimecode: { [weak self] message, timestamp, forceOpen in
|
||||
if let strongSelf = self {
|
||||
let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId)
|
||||
var completed = false
|
||||
strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in
|
||||
if !completed, let itemNode = itemNode as? ChatMessageItemView, itemNode.item?.message.id == messageId, let (action, _, _, _, _) = itemNode.playMediaWithSound() {
|
||||
if case let .visible(fraction) = itemNode.visibility, fraction > 0.7 {
|
||||
action(Double(timestamp))
|
||||
} else if let message = message {
|
||||
let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp)))
|
||||
var found = false
|
||||
if !forceOpen {
|
||||
strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in
|
||||
if !found, let itemNode = itemNode as? ChatMessageItemView, itemNode.item?.message.id == message.id, let (action, _, _, _, _) = itemNode.playMediaWithSound() {
|
||||
if case let .visible(fraction) = itemNode.visibility, fraction > 0.7 {
|
||||
action(Double(timestamp))
|
||||
} else {
|
||||
let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp)))
|
||||
}
|
||||
found = true
|
||||
}
|
||||
completed = true
|
||||
}
|
||||
}
|
||||
if !completed, let message = message {
|
||||
if !found {
|
||||
let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp)))
|
||||
}
|
||||
}
|
||||
@ -2762,15 +2763,15 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
guard let strongSelf = self, let layout = strongSelf.validLayout, strongSelf.traceVisibility() && isTopmostChatController(strongSelf) else {
|
||||
return
|
||||
}
|
||||
let deviceMetrics = DeviceMetrics.forScreenSize(layout.size)
|
||||
let icon: UIImage?
|
||||
if deviceMetrics == .iPhoneX || deviceMetrics == .iPhoneXSMax {
|
||||
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIconX")
|
||||
} else {
|
||||
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIcon")
|
||||
switch DeviceMetrics.forScreenSize(layout.size) {
|
||||
case .iPhoneX?, .iPhoneXSMax?:
|
||||
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIconX")
|
||||
default:
|
||||
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIcon")
|
||||
}
|
||||
if let location = location, let icon = icon {
|
||||
strongSelf.mediaRestrictedTooltipController?.dismiss()
|
||||
strongSelf.videoUnmuteTooltipController?.dismiss()
|
||||
let tooltipController = TooltipController(content: .iconAndText(icon, strongSelf.presentationInterfaceState.strings.Conversation_PressVolumeButtonForSound), timeout: 3.5, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true)
|
||||
strongSelf.videoUnmuteTooltipController = tooltipController
|
||||
tooltipController.dismissed = { [weak tooltipController] in
|
||||
@ -2816,7 +2817,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
let _ = ApplicationSpecificNotice.incrementChatMediaMediaRecordingTips(accountManager: strongSelf.context.sharedContext.accountManager, count: 3).start()
|
||||
}
|
||||
|
||||
strongSelf.displayMediaRecordingTip()
|
||||
strongSelf.displayMediaRecordingTooltip()
|
||||
}
|
||||
}, setupMessageAutoremoveTimeout: { [weak self] in
|
||||
if let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
@ -3491,7 +3492,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
if displayTip {
|
||||
let _ = ApplicationSpecificNotice.incrementChatMediaMediaRecordingTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||
strongSelf.displayMediaRecordingTip()
|
||||
strongSelf.displayMediaRecordingTooltip()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -3505,11 +3506,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
self.chatDisplayNode.historyNode.canReadHistory.set(.single(false))
|
||||
self.saveInterfaceState()
|
||||
|
||||
self.messageTooltipController?.dismiss()
|
||||
self.videoUnmuteTooltipController?.dismiss()
|
||||
self.silentPostTooltipController?.dismiss()
|
||||
self.mediaRecordingModeTooltipController?.dismiss()
|
||||
self.mediaRestrictedTooltipController?.dismiss()
|
||||
self.dismissAllTooltips()
|
||||
|
||||
self.window?.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
@ -4143,24 +4140,36 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
legacyController.bind(controller: navigationController)
|
||||
|
||||
legacyController.enableSizeClassSignal = true
|
||||
let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, editMediaOptions: editMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, openGallery: {
|
||||
|
||||
let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
|
||||
let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, editMediaOptions: editMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText.string, openGallery: {
|
||||
self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals in
|
||||
if !inputText.string.isEmpty {
|
||||
strongSelf.clearInputText()
|
||||
}
|
||||
if editMediaOptions != nil {
|
||||
self?.editMessageMediaWithLegacySignals(signals)
|
||||
} else {
|
||||
self?.enqueueMediaMessages(signals: signals)
|
||||
}
|
||||
})
|
||||
}, openCamera: { cameraView, menuController in
|
||||
}, openCamera: { [weak self] cameraView, menuController in
|
||||
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, sendMessagesWithSignals: { signals in
|
||||
if editMediaOptions != nil {
|
||||
self?.editMessageMediaWithLegacySignals(signals!)
|
||||
} else {
|
||||
self?.enqueueMediaMessages(signals: signals)
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, sendMessagesWithSignals: { [weak self] signals in
|
||||
if let strongSelf = self {
|
||||
if editMediaOptions != nil {
|
||||
strongSelf.editMessageMediaWithLegacySignals(signals!)
|
||||
} else {
|
||||
strongSelf.enqueueMediaMessages(signals: signals)
|
||||
}
|
||||
if !inputText.string.isEmpty {
|
||||
strongSelf.clearInputText()
|
||||
}
|
||||
}
|
||||
}, recognizedQRCode: { [weak self] code in
|
||||
if let strongSelf = self, let (host, port, username, password, secret) = parseProxyUrl(code) {
|
||||
strongSelf.openResolved(ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret))
|
||||
}
|
||||
}, recognizedQRCode: { code in
|
||||
self?.processQRCode(code)
|
||||
})
|
||||
}
|
||||
}, openFileGallery: {
|
||||
@ -4174,6 +4183,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}, openPoll: {
|
||||
self?.presentPollCreation()
|
||||
}, sendMessagesWithSignals: { [weak self] signals in
|
||||
if !inputText.string.isEmpty {
|
||||
strongSelf.clearInputText()
|
||||
}
|
||||
if editMediaOptions != nil {
|
||||
self?.editMessageMediaWithLegacySignals(signals!)
|
||||
} else {
|
||||
@ -4290,6 +4302,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return
|
||||
}
|
||||
let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
|
||||
let _ = legacyAssetPicker(context: strongSelf.context, presentationData: strongSelf.presentationData, editingMedia: editingMedia, fileMode: fileMode, peer: peer, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true).start(next: { generator in
|
||||
if let strongSelf = self {
|
||||
let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
|
||||
@ -4301,7 +4314,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
legacyController.bind(controller: controller)
|
||||
legacyController.deferScreenEdgeGestures = [.top]
|
||||
|
||||
configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, presentWebSearch: { [weak self, weak legacyController] in
|
||||
configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, initialCaption: inputText.string, presentWebSearch: { [weak self, weak legacyController] in
|
||||
if let strongSelf = self {
|
||||
let controller = WebSearchController(context: strongSelf.context, peer: peer, configuration: searchBotsConfiguration, mode: .media(completion: { results, selectionState, editingState in
|
||||
if let legacyController = legacyController {
|
||||
@ -4321,7 +4334,13 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
})
|
||||
controller.descriptionGenerator = legacyAssetPickerItemGenerator()
|
||||
controller.completionBlock = { [weak legacyController] signals in
|
||||
controller.completionBlock = { [weak legacyController, weak self] signals in
|
||||
if let strongSelf = self {
|
||||
if !inputText.string.isEmpty {
|
||||
strongSelf.clearInputText()
|
||||
}
|
||||
}
|
||||
|
||||
if let legacyController = legacyController {
|
||||
legacyController.dismiss()
|
||||
completion(signals!)
|
||||
@ -4811,7 +4830,6 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
} else if let videoRecorderValue = self.videoRecorderValue {
|
||||
if case .send = action {
|
||||
videoRecorderValue.completeVideo()
|
||||
//self.tempVideoRecorderValue = videoRecorderValue
|
||||
self.videoRecorder.set(.single(nil))
|
||||
} else {
|
||||
self.videoRecorder.set(.single(nil))
|
||||
@ -6143,7 +6161,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
self.interfaceInteraction?.beginMessageSearch(.everything, query)
|
||||
}
|
||||
|
||||
private func displayMediaRecordingTip() {
|
||||
private func displayMediaRecordingTooltip() {
|
||||
let rect: CGRect? = self.chatDisplayNode.frameForInputActionButton()
|
||||
|
||||
let updatedMode: ChatTextInputMediaRecordingButtonMode = self.presentationInterfaceState.interfaceState.mediaRecordingMode
|
||||
@ -6174,8 +6192,19 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
}
|
||||
|
||||
private func dismissAllTooltips() {
|
||||
self.messageTooltipController?.dismiss()
|
||||
self.videoUnmuteTooltipController?.dismiss()
|
||||
self.silentPostTooltipController?.dismiss()
|
||||
self.mediaRecordingModeTooltipController?.dismiss()
|
||||
self.mediaRestrictedTooltipController?.dismiss()
|
||||
}
|
||||
|
||||
private func commitPurposefulAction() {
|
||||
self.purposefulAction?()
|
||||
if let purposefulAction = self.purposefulAction {
|
||||
self.purposefulAction = nil
|
||||
purposefulAction()
|
||||
}
|
||||
}
|
||||
|
||||
public var keyShortcuts: [KeyShortcut] {
|
||||
@ -6287,12 +6316,6 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
return inputShortcuts + otherShortcuts
|
||||
}
|
||||
|
||||
private func processQRCode(_ code: String) {
|
||||
if let (host, port, username, password, secret) = parseProxyUrl(code) {
|
||||
self.openResolved(ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret))
|
||||
}
|
||||
}
|
||||
|
||||
func getTransitionInfo(messageId: MessageId, media: Media) -> ((UIView) -> Void, ASDisplayNode, () -> (UIView?, UIView?))? {
|
||||
var selectedNode: (ASDisplayNode, () -> (UIView?, UIView?))?
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
@ -6313,4 +6336,17 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func clearInputText() {
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
if !state.interfaceState.effectiveInputState.inputText.string.isEmpty {
|
||||
return state.updatedInterfaceState { interfaceState in
|
||||
let effectiveInputState = ChatTextInputState(inputText: NSAttributedString(string: ""))
|
||||
return interfaceState.withUpdatedEffectiveInputState(effectiveInputState)
|
||||
}
|
||||
} else {
|
||||
return state
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ public final class ChatControllerInteraction {
|
||||
let navigationController: () -> NavigationController?
|
||||
let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
||||
let callPeer: (PeerId) -> Void
|
||||
let longTap: (ChatControllerInteractionLongTapAction, MessageId?) -> Void
|
||||
let longTap: (ChatControllerInteractionLongTapAction, Message?) -> Void
|
||||
let openCheckoutOrReceipt: (MessageId) -> Void
|
||||
let openSearch: () -> Void
|
||||
let setupReply: (MessageId) -> Void
|
||||
@ -89,7 +89,7 @@ public final class ChatControllerInteraction {
|
||||
let requestSelectMessagePollOption: (MessageId, Data) -> Void
|
||||
let openAppStorePage: () -> Void
|
||||
let displayMessageTooltip: (MessageId, String, ASDisplayNode?, CGRect?) -> Void
|
||||
let seekToTimecode: (MessageId, Double) -> Void
|
||||
let seekToTimecode: (Message, Double, Bool) -> Void
|
||||
|
||||
let requestMessageUpdate: (MessageId) -> Void
|
||||
let cancelInteractiveKeyboardGestures: () -> Void
|
||||
@ -102,7 +102,7 @@ public final class ChatControllerInteraction {
|
||||
var pollActionState: ChatInterfacePollActionState
|
||||
var searchTextHighightState: String?
|
||||
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, MessageId?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (MessageId, Double) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState) {
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState) {
|
||||
self.openMessage = openMessage
|
||||
self.openPeer = openPeer
|
||||
self.openPeerMention = openPeerMention
|
||||
@ -166,7 +166,7 @@ public final class ChatControllerInteraction {
|
||||
}, requestSelectMessagePollOption: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
}, displayMessageTooltip: { _, _, _, _ in
|
||||
}, seekToTimecode: { _, _ in
|
||||
}, seekToTimecode: { _, _, _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
||||
@ -57,6 +57,19 @@ private let playImage = generateImage(CGSize(width: 15.0, height: 18.0), rotated
|
||||
|
||||
private let cloudFetchIcon = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/FileCloudFetch"), color: UIColor.white)
|
||||
|
||||
private let captionMaskImage = generateImage(CGSize(width: 1.0, height: 17.0), opaque: false, rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let gradientColors = [UIColor.white.withAlphaComponent(1.0).cgColor, UIColor.white.withAlphaComponent(0.0).cgColor] as CFArray
|
||||
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: 17.0), options: CGGradientDrawingOptions())
|
||||
})
|
||||
|
||||
private let titleFont = Font.medium(15.0)
|
||||
private let dateFont = Font.regular(14.0)
|
||||
|
||||
@ -102,7 +115,7 @@ enum ChatItemGalleryFooterContentTapAction {
|
||||
case ignore
|
||||
}
|
||||
|
||||
final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private var theme: PresentationTheme
|
||||
private var strings: PresentationStrings
|
||||
@ -110,6 +123,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
|
||||
private let deleteButton: UIButton
|
||||
private let actionButton: UIButton
|
||||
private let maskNode: ASDisplayNode
|
||||
private let scrollWrapperNode: ASDisplayNode
|
||||
private let scrollNode: ASScrollNode
|
||||
|
||||
private let textNode: ImmediateTextNode
|
||||
private let authorNameNode: ASTextNode
|
||||
private let dateNode: ASTextNode
|
||||
@ -213,10 +230,18 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
self.deleteButton.setImage(deleteImage, for: [.normal])
|
||||
self.actionButton.setImage(actionImage, for: [.normal])
|
||||
|
||||
self.scrollWrapperNode = ASDisplayNode()
|
||||
self.scrollWrapperNode.clipsToBounds = true
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.scrollNode.clipsToBounds = false
|
||||
|
||||
self.maskNode = ASDisplayNode()
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.maximumNumberOfLines = 10
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
self.textNode.linkHighlightColor = UIColor(rgb: 0x5ac8fa, alpha: 0.2)
|
||||
|
||||
|
||||
self.authorNameNode = ASTextNode()
|
||||
self.authorNameNode.maximumNumberOfLines = 1
|
||||
self.authorNameNode.isUserInteractionEnabled = false
|
||||
@ -271,7 +296,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
|
||||
self.view.addSubview(self.deleteButton)
|
||||
self.view.addSubview(self.actionButton)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.scrollWrapperNode)
|
||||
self.scrollWrapperNode.addSubnode(self.scrollNode)
|
||||
self.scrollNode.addSubnode(self.textNode)
|
||||
|
||||
self.addSubnode(self.authorNameNode)
|
||||
self.addSubnode(self.dateNode)
|
||||
|
||||
@ -307,6 +335,21 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
self.messageContextDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
self.scrollNode.view.delegate = self
|
||||
|
||||
if let maskImage = captionMaskImage {
|
||||
let mask = CALayer()
|
||||
mask.contents = maskImage.cgImage
|
||||
mask.contentsScale = maskImage.scale
|
||||
//mask.contentsCenter = CGRect(x: max(corners.topLeft.radius, corners.bottomLeft.radius) / maskImage.size.width, y: max(corners.topLeft.radius, corners.topRight.radius) / maskImage.size.height, width: (maskImage.size.width - max(corners.topLeft.radius, corners.bottomLeft.radius) - max(corners.topRight.radius, corners.bottomRight.radius)) / maskImage.size.width, height: (maskImage.size.height - max(corners.topLeft.radius, corners.topRight.radius) - max(corners.bottomLeft.radius, corners.bottomRight.radius)) / maskImage.size.height)
|
||||
|
||||
//self.scrollWrapperNode.layer.mask = mask
|
||||
//self.scrollWrapperNode.layer.mask?.frame = self.scrollWrapperNode.bounds
|
||||
}
|
||||
}
|
||||
|
||||
private func actionForAttributes(_ attributes: [NSAttributedStringKey: Any]) -> GalleryControllerInteractionTapAction? {
|
||||
if let url = attributes[NSAttributedStringKey(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
return .url(url: url, concealed: false)
|
||||
@ -441,7 +484,20 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
self.currentWebPageAndMedia = (webPage, media)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.requestLayout?(.immediate)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
if self.scrollWrapperNode.frame.contains(point) {
|
||||
return self.scrollNode.view
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let width = size.width
|
||||
var bottomInset = bottomInset
|
||||
if bottomInset < 30.0 {
|
||||
@ -451,7 +507,12 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
panelHeight += contentInset
|
||||
|
||||
let isLandscape = size.width > size.height
|
||||
let displayCaption = !self.textNode.isHidden && !isLandscape
|
||||
let displayCaption: Bool
|
||||
if case .compact = metrics.widthClass {
|
||||
displayCaption = !self.textNode.isHidden && !isLandscape
|
||||
} else {
|
||||
displayCaption = !self.textNode.isHidden
|
||||
}
|
||||
|
||||
var textFrame = CGRect()
|
||||
if !self.textNode.isHidden {
|
||||
@ -459,10 +520,55 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
let topInset: CGFloat = 8.0
|
||||
let textBottomInset: CGFloat = 8.0
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
var textOffset: CGFloat = 0.0
|
||||
if displayCaption {
|
||||
panelHeight += textSize.height + topInset + textBottomInset
|
||||
var visibleTextHeight = textSize.height
|
||||
if visibleTextHeight > 100.0 {
|
||||
visibleTextHeight = 80.0
|
||||
self.scrollNode.view.isScrollEnabled = true
|
||||
} else {
|
||||
self.scrollNode.view.isScrollEnabled = false
|
||||
}
|
||||
|
||||
let visibleTextPanelHeight = visibleTextHeight + topInset + textBottomInset
|
||||
let scrollViewContentSize = CGSize(width: width, height: textSize.height + topInset + textBottomInset)
|
||||
if self.scrollNode.view.contentSize != scrollViewContentSize {
|
||||
self.scrollNode.view.contentSize = scrollViewContentSize
|
||||
}
|
||||
let scrollNodeFrame = CGRect(x: 0.0, y: 0.0, width: width, height: visibleTextPanelHeight)
|
||||
if self.scrollNode.frame != scrollNodeFrame {
|
||||
self.scrollNode.frame = scrollNodeFrame
|
||||
}
|
||||
|
||||
textOffset = min(400.0, self.scrollNode.view.contentOffset.y)
|
||||
panelHeight = max(0.0, panelHeight + visibleTextPanelHeight + textOffset)
|
||||
|
||||
if self.scrollNode.view.isScrollEnabled {
|
||||
if self.scrollWrapperNode.layer.mask == nil {
|
||||
let maskImage = captionMaskImage!
|
||||
let maskLayer = CALayer()
|
||||
maskLayer.contents = maskImage.cgImage
|
||||
maskLayer.contentsScale = maskImage.scale
|
||||
maskLayer.contentsCenter = CGRect(x: 0.0, y: 0.0, width: 1.0, height: (maskImage.size.height - 16.0) / maskImage.size.height)
|
||||
self.scrollWrapperNode.layer.mask = maskLayer
|
||||
|
||||
}
|
||||
} else {
|
||||
self.scrollWrapperNode.layer.mask = nil
|
||||
}
|
||||
|
||||
let scrollWrapperNodeFrame = CGRect(x: 0.0, y: 0.0, width: width, height: max(0.0, visibleTextPanelHeight + textOffset))
|
||||
if self.scrollWrapperNode.frame != scrollWrapperNodeFrame {
|
||||
self.scrollWrapperNode.frame = scrollWrapperNodeFrame
|
||||
self.scrollWrapperNode.layer.mask?.frame = self.scrollWrapperNode.bounds //.offsetBy(dx: 0.0, dy: textOffset)
|
||||
self.scrollWrapperNode.layer.mask?.removeAllAnimations()
|
||||
}
|
||||
}
|
||||
textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + textOffset), size: textSize)
|
||||
if self.textNode.frame != textFrame {
|
||||
self.textNode.frame = textFrame
|
||||
}
|
||||
textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: textSize)
|
||||
}
|
||||
|
||||
if let scrubberView = self.scrubberView, scrubberView.superview == self.view {
|
||||
@ -484,8 +590,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
scrubberView.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset)
|
||||
transition.updateFrame(layer: scrubberView.layer, frame: scrubberFrame)
|
||||
}
|
||||
|
||||
self.textNode.frame = textFrame
|
||||
transition.updateAlpha(node: self.textNode, alpha: displayCaption ? 1.0 : 0.0)
|
||||
|
||||
self.actionButton.frame = CGRect(origin: CGPoint(x: leftInset, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0))
|
||||
@ -524,8 +628,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
scrubberView.alpha = 1.0
|
||||
scrubberView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
transition.animatePositionAdditive(node: self.textNode, offset: CGPoint(x: 0.0, y: self.bounds.height - fromHeight))
|
||||
self.textNode.alpha = 1.0
|
||||
transition.animatePositionAdditive(node: self.scrollWrapperNode, offset: CGPoint(x: 0.0, y: self.bounds.height - fromHeight))
|
||||
self.scrollWrapperNode.alpha = 1.0
|
||||
self.dateNode.alpha = 1.0
|
||||
self.authorNameNode.alpha = 1.0
|
||||
self.deleteButton.alpha = 1.0
|
||||
@ -534,7 +638,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
self.forwardButton.alpha = 1.0
|
||||
self.statusNode.alpha = 1.0
|
||||
self.playbackControlButton.alpha = 1.0
|
||||
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
self.scrollWrapperNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
|
||||
override func animateOut(toHeight: CGFloat, nextContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
||||
@ -546,8 +650,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
scrubberView.alpha = 0.0
|
||||
scrubberView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||
}
|
||||
transition.updateFrame(node: self.textNode, frame: self.textNode.frame.offsetBy(dx: 0.0, dy: self.bounds.height - toHeight))
|
||||
self.textNode.alpha = 0.0
|
||||
transition.updateFrame(node: self.scrollWrapperNode, frame: self.scrollWrapperNode.frame.offsetBy(dx: 0.0, dy: self.bounds.height - toHeight))
|
||||
self.scrollWrapperNode.alpha = 0.0
|
||||
self.dateNode.alpha = 0.0
|
||||
self.authorNameNode.alpha = 0.0
|
||||
self.deleteButton.alpha = 0.0
|
||||
@ -556,7 +660,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
self.forwardButton.alpha = 0.0
|
||||
self.statusNode.alpha = 0.0
|
||||
self.playbackControlButton.alpha = 0.0
|
||||
self.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { _ in
|
||||
self.scrollWrapperNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
@ -1612,6 +1612,35 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
var mediaMessage: Message?
|
||||
var forceOpen = false
|
||||
if let item = self.item {
|
||||
for media in item.message.media {
|
||||
if let file = media as? TelegramMediaFile, file.duration != nil {
|
||||
mediaMessage = item.message
|
||||
}
|
||||
}
|
||||
var forceOpen = false
|
||||
if mediaMessage == nil {
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? ReplyMessageAttribute {
|
||||
if let replyMessage = item.message.associatedMessages[attribute.messageId] {
|
||||
for media in replyMessage.media {
|
||||
if let file = media as? TelegramMediaFile, file.duration != nil {
|
||||
mediaMessage = replyMessage
|
||||
forceOpen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if mediaMessage == nil {
|
||||
mediaMessage = item.message
|
||||
}
|
||||
}
|
||||
|
||||
switch gesture {
|
||||
case .tap:
|
||||
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
|
||||
@ -1729,33 +1758,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
break loop
|
||||
case let .timecode(timecode, _):
|
||||
foundTapAction = true
|
||||
if let item = self.item {
|
||||
var messageId: MessageId?
|
||||
for media in item.message.media {
|
||||
if let file = media as? TelegramMediaFile, file.duration != nil {
|
||||
messageId = item.message.id
|
||||
}
|
||||
}
|
||||
if messageId == nil {
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? ReplyMessageAttribute {
|
||||
if let replyMessage = item.message.associatedMessages[attribute.messageId] {
|
||||
for media in replyMessage.media {
|
||||
if let file = media as? TelegramMediaFile, file.duration != nil {
|
||||
messageId = replyMessage.id
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if messageId == nil {
|
||||
messageId = item.message.id
|
||||
}
|
||||
if let messageId = messageId {
|
||||
item.controllerInteraction.seekToTimecode(messageId, timecode)
|
||||
}
|
||||
if let item = self.item, let mediaMessage = mediaMessage {
|
||||
item.controllerInteraction.seekToTimecode(mediaMessage, timecode, forceOpen)
|
||||
}
|
||||
break loop
|
||||
}
|
||||
@ -1765,7 +1769,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
}
|
||||
case .longTap, .doubleTap:
|
||||
if let item = self.item, self.backgroundNode.frame.contains(location) {
|
||||
let messageId = item.message.id
|
||||
let message = item.message
|
||||
|
||||
var foundTapAction = false
|
||||
var tapMessage: Message? = item.content.firstMessage
|
||||
@ -1783,23 +1787,23 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
break
|
||||
case let .url(url, _):
|
||||
foundTapAction = true
|
||||
item.controllerInteraction.longTap(.url(url), messageId)
|
||||
item.controllerInteraction.longTap(.url(url), message)
|
||||
break loop
|
||||
case let .peerMention(peerId, mention):
|
||||
foundTapAction = true
|
||||
item.controllerInteraction.longTap(.peerMention(peerId, mention), messageId)
|
||||
item.controllerInteraction.longTap(.peerMention(peerId, mention), message)
|
||||
break loop
|
||||
case let .textMention(name):
|
||||
foundTapAction = true
|
||||
item.controllerInteraction.longTap(.mention(name), messageId)
|
||||
item.controllerInteraction.longTap(.mention(name), message)
|
||||
break loop
|
||||
case let .botCommand(command):
|
||||
foundTapAction = true
|
||||
item.controllerInteraction.longTap(.command(command), messageId)
|
||||
item.controllerInteraction.longTap(.command(command), message)
|
||||
break loop
|
||||
case let .hashtag(_, hashtag):
|
||||
foundTapAction = true
|
||||
item.controllerInteraction.longTap(.hashtag(hashtag), messageId)
|
||||
item.controllerInteraction.longTap(.hashtag(hashtag), message)
|
||||
break loop
|
||||
case .instantPage:
|
||||
break
|
||||
@ -1812,7 +1816,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
break
|
||||
case let .timecode(timecode, text):
|
||||
foundTapAction = true
|
||||
item.controllerInteraction.longTap(.timecode(timecode, text), messageId)
|
||||
if let mediaMessage = mediaMessage {
|
||||
item.controllerInteraction.longTap(.timecode(timecode, text), mediaMessage)
|
||||
}
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,7 +757,7 @@ public class ChatMessageItemView: ListViewItemNode {
|
||||
if let item = self.item {
|
||||
switch button.action {
|
||||
case let .url(url):
|
||||
item.controllerInteraction.longTap(.url(url), item.message.id)
|
||||
item.controllerInteraction.longTap(.url(url), item.message)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, presentController: { _, _ in
|
||||
}, navigationController: { [weak self] in
|
||||
return self?.getNavigationController()
|
||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { [weak self] action, messageId in
|
||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { [weak self] action, message in
|
||||
if let strongSelf = self {
|
||||
switch action {
|
||||
case let .url(url):
|
||||
@ -348,7 +348,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
])])
|
||||
strongSelf.presentController(actionSheet, nil)
|
||||
case let .timecode(timecode, text):
|
||||
guard let messageId = messageId else {
|
||||
guard let message = message else {
|
||||
return
|
||||
}
|
||||
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
|
||||
@ -357,7 +357,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.seekToTimecode(messageId, timecode)
|
||||
strongSelf.controllerInteraction?.seekToTimecode(message, timecode, true)
|
||||
}
|
||||
}),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogCopy, color: .accent, action: { [weak actionSheet] in
|
||||
@ -387,7 +387,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
strongSelf.context.sharedContext.applicationBindings.openAppStorePage()
|
||||
}
|
||||
}, displayMessageTooltip: { _, _, _, _ in
|
||||
}, seekToTimecode: { _, _ in
|
||||
}, seekToTimecode: { _, _, _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||
|
||||
@ -337,6 +337,9 @@ public extension DeviceContactExtendedData {
|
||||
value.postalCode = address.postcode
|
||||
return CNLabeledValue<CNPostalAddress>(label: address.label, value: value)
|
||||
})
|
||||
if let birthdayDate = self.birthdayDate {
|
||||
contact.birthday = Calendar(identifier: .gregorian).dateComponents([.day, .month, .year], from: birthdayDate)
|
||||
}
|
||||
return contact
|
||||
}
|
||||
|
||||
|
||||
@ -614,7 +614,8 @@ private func deviceContactInfoEntries(account: Account, presentationData: Presen
|
||||
if let birthday = contactData.birthdayDate {
|
||||
let dateText: String
|
||||
let calendar = Calendar(identifier: .gregorian)
|
||||
let components = calendar.dateComponents(Set([.era, .year, .month, .day]), from: birthday)
|
||||
var components = calendar.dateComponents(Set([.era, .year, .month, .day]), from: birthday)
|
||||
components.hour = 12
|
||||
if let year = components.year, year > 1 {
|
||||
dateText = stringForDate(timestamp: Int32(birthday.timeIntervalSince1970), strings: presentationData.strings)
|
||||
} else {
|
||||
|
||||
@ -19,7 +19,7 @@ open class GalleryFooterContentNode: ASDisplayNode {
|
||||
var requestLayout: ((ContainedViewLayoutTransition) -> Void)?
|
||||
var controllerInteraction: GalleryControllerInteraction?
|
||||
|
||||
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ final class GalleryFooterNode: ASDisplayNode {
|
||||
|
||||
var backgroundHeight: CGFloat = 0.0
|
||||
if let footerContentNode = self.currentFooterContentNode {
|
||||
backgroundHeight = footerContentNode.updateLayout(size: layout.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, contentInset: thumbnailPanelHeight, transition: transition)
|
||||
backgroundHeight = footerContentNode.updateLayout(size: layout.size, metrics: layout.metrics, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, contentInset: thumbnailPanelHeight, transition: transition)
|
||||
transition.updateFrame(node: footerContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundHeight), size: CGSize(width: layout.size.width, height: backgroundHeight)))
|
||||
if let removeCurrentFooterContentNode = removeCurrentFooterContentNode {
|
||||
let contentTransition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
||||
|
||||
@ -111,8 +111,8 @@ private func commitEntity(_ utf16: String.UTF16View, _ type: CurrentEntityType,
|
||||
entityType = .Custom(type: ApplicationSpecificEntityType.Timecode)
|
||||
}
|
||||
|
||||
if case .timecode = type, let mediaDuration = mediaDuration, let timecode = parseTimecodeString(String(utf16[range])) {
|
||||
if timecode <= mediaDuration {
|
||||
if case .timecode = type {
|
||||
if let mediaDuration = mediaDuration, let timecode = parseTimecodeString(String(utf16[range])), timecode <= mediaDuration {
|
||||
entities.append(MessageTextEntity(range: indexRange, type: entityType))
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -99,7 +99,7 @@ final class InstantPageGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
self.actionButton.isHidden = shareMedia == nil
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let width = size.width
|
||||
var panelHeight: CGFloat = 44.0 + bottomInset + contentInset
|
||||
if !self.textNode.isHidden {
|
||||
|
||||
@ -432,7 +432,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
|
||||
} else if case .generic = item.mode, !servicePeer {
|
||||
let presence = (item.presence as? TelegramUserPresence) ?? TelegramUserPresence(status: .none, lastActivity: 0)
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
let (string, activity) = stringAndActivityForUserPresence(strings: item.strings, dateTimeFormat: item.dateTimeFormat, presence: presence, relativeTo: Int32(timestamp))
|
||||
let (string, activity) = stringAndActivityForUserPresence(strings: item.strings, dateTimeFormat: item.dateTimeFormat, presence: presence, relativeTo: Int32(timestamp), expanded: true)
|
||||
statusText = string
|
||||
if activity {
|
||||
statusColor = item.theme.list.itemAccentColor
|
||||
|
||||
@ -6,7 +6,7 @@ import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: MessageMediaEditingOptions?, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, sendMessagesWithSignals: @escaping ([Any]?) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController {
|
||||
func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: MessageMediaEditingOptions?, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, sendMessagesWithSignals: @escaping ([Any]?) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController {
|
||||
let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat
|
||||
|
||||
let controller = TGMenuSheetController(context: parentController.context, dark: false)!
|
||||
@ -58,6 +58,7 @@ func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions:
|
||||
}
|
||||
};
|
||||
carouselItem.allowCaptions = true
|
||||
carouselItem.editingContext.setInitialCaption(initialCaption, entities: [])
|
||||
itemViews.append(carouselItem)
|
||||
|
||||
let galleryItem = TGMenuSheetButtonItemView(title: editing ? strings.Conversation_EditingMessageMediaChange : strings.AttachmentMenu_PhotoOrVideo, type: TGMenuSheetButtonTypeDefault, action: { [weak controller] in
|
||||
|
||||
@ -6,7 +6,7 @@ import TelegramCore
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
|
||||
func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }) {
|
||||
func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
||||
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||
|
||||
@ -13,7 +13,7 @@ func guessMimeTypeByFileExtension(_ ext: String) -> String {
|
||||
return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary"
|
||||
}
|
||||
|
||||
func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, presentWebSearch: (() -> Void)?) {
|
||||
func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, presentWebSearch: (() -> Void)?) {
|
||||
controller.captionsEnabled = captionsEnabled
|
||||
controller.inhibitDocumentCaptions = false
|
||||
controller.suggestionContext = legacySuggestionContext(account: context.account, peerId: peer.id)
|
||||
@ -26,6 +26,8 @@ func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context:
|
||||
controller.shouldStoreAssets = storeCreatedAssets
|
||||
controller.shouldShowFileTipIfNeeded = showFileTooltip
|
||||
controller.requestSearchController = presentWebSearch
|
||||
|
||||
controller.editingContext.setInitialCaption(initialCaption, entities: [])
|
||||
}
|
||||
|
||||
func legacyAssetPicker(context: AccountContext, presentationData: PresentationData, editingMedia: Bool, fileMode: Bool, peer: Peer?, saveEditedPhotos: Bool, allowGrouping: Bool) -> Signal<(LegacyComponentsContext) -> TGMediaAssetsController, Void> {
|
||||
|
||||
@ -543,7 +543,7 @@ final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
case .tap, .longTap:
|
||||
if let item = self.item, let url = self.urlAtPoint(location) {
|
||||
if case .longTap = gesture {
|
||||
item.controllerInteraction.longTap(ChatControllerInteractionLongTapAction.url(url), item.message.id)
|
||||
item.controllerInteraction.longTap(ChatControllerInteractionLongTapAction.url(url), item.message)
|
||||
} else if url == self.currentPrimaryUrl {
|
||||
if !item.controllerInteraction.openMessage(item.message, .default) {
|
||||
item.controllerInteraction.openUrl(url, false, false)
|
||||
|
||||
@ -73,7 +73,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
|
||||
}, requestSelectMessagePollOption: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
}, displayMessageTooltip: { _, _, _, _ in
|
||||
}, seekToTimecode: { _, _ in
|
||||
}, seekToTimecode: { _, _, _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
||||
@ -254,7 +254,7 @@ public class PeerMediaCollectionController: TelegramController {
|
||||
}, requestSelectMessagePollOption: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
}, displayMessageTooltip: { _, _, _, _ in
|
||||
}, seekToTimecode: { _, _ in
|
||||
}, seekToTimecode: { _, _, _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
||||
@ -276,7 +276,7 @@ func stringForRelativeLiveLocationUpdateTimestamp(strings: PresentationStrings,
|
||||
}
|
||||
}
|
||||
|
||||
func stringAndActivityForUserPresence(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, presence: TelegramUserPresence, relativeTo timestamp: Int32) -> (String, Bool) {
|
||||
func stringAndActivityForUserPresence(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, presence: TelegramUserPresence, relativeTo timestamp: Int32, expanded: Bool = false) -> (String, Bool) {
|
||||
switch presence.status {
|
||||
case .none:
|
||||
return (strings.LastSeen_Offline, false)
|
||||
@ -287,7 +287,7 @@ func stringAndActivityForUserPresence(strings: PresentationStrings, dateTimeForm
|
||||
let difference = timestamp - statusTimestamp
|
||||
if difference < 60 {
|
||||
return (strings.LastSeen_JustNow, false)
|
||||
} else if difference < 60 * 60 {
|
||||
} else if difference < 60 * 60 && !expanded {
|
||||
let minutes = difference / 60
|
||||
return (strings.LastSeen_MinutesAgo(minutes), false)
|
||||
} else {
|
||||
@ -307,8 +307,12 @@ func stringAndActivityForUserPresence(strings: PresentationStrings, dateTimeForm
|
||||
if dayDifference == 0 || dayDifference == -1 {
|
||||
let day: RelativeTimestampFormatDay
|
||||
if dayDifference == 0 {
|
||||
let minutes = difference / (60 * 60)
|
||||
return (strings.LastSeen_HoursAgo(minutes), false)
|
||||
if expanded {
|
||||
day = .today
|
||||
} else {
|
||||
let minutes = difference / (60 * 60)
|
||||
return (strings.LastSeen_HoursAgo(minutes), false)
|
||||
}
|
||||
} else {
|
||||
day = .yesterday
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ final class SecretMediaPreviewFooterContentNode: GalleryFooterContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let width = size.width
|
||||
let panelHeight: CGFloat = 44.0 + bottomInset
|
||||
|
||||
|
||||
@ -86,7 +86,7 @@ final class SecureIdDocumentGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let width = size.width
|
||||
var panelHeight: CGFloat = 44.0 + bottomInset
|
||||
panelHeight += contentInset
|
||||
|
||||
@ -160,7 +160,6 @@ final class SettingsSearchInteraction {
|
||||
|
||||
private enum SettingsSearchEntryStableId: Hashable {
|
||||
case result(SettingsSearchableItemId)
|
||||
case faq(String)
|
||||
}
|
||||
|
||||
private enum SettingsSearchEntry: Comparable, Identifiable {
|
||||
@ -302,7 +301,6 @@ private func preparedSettingsSearchContainerRecentTransition(from fromEntries: [
|
||||
|
||||
|
||||
private final class SettingsSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
private let dimNode: ASDisplayNode
|
||||
private let listNode: ListView
|
||||
private let recentListNode: ListView
|
||||
|
||||
@ -323,9 +321,6 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
|
||||
self.listNode = ListView()
|
||||
self.listNode.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
self.listNode.isHidden = true
|
||||
@ -338,7 +333,6 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
|
||||
//self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.recentListNode)
|
||||
self.addSubnode(self.listNode)
|
||||
|
||||
@ -407,7 +401,10 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
var result: [SettingsSearchableItem] = []
|
||||
for itemId in recentItems {
|
||||
if let searchItem = searchableItemsMap[itemId] {
|
||||
result.append(searchItem)
|
||||
if case let .language(id) = searchItem.id, id > 0 {
|
||||
} else {
|
||||
result.append(searchItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
@ -489,12 +486,6 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
}
|
||||
|
||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.listNode.backgroundColor = theme.chatList.backgroundColor
|
||||
self.recentListNode.backgroundColor = theme.chatList.backgroundColor
|
||||
@ -529,7 +520,6 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
let isSearching = transition.isSearching
|
||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||
self?.listNode.isHidden = !isSearching
|
||||
self?.dimNode.isHidden = isSearching
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -563,10 +553,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
|
||||
let topInset = navigationBarHeight
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
||||
|
||||
|
||||
var duration: Double = 0.0
|
||||
var curve: UInt = 0
|
||||
switch transition {
|
||||
@ -723,7 +710,6 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
|
||||
}
|
||||
|
||||
override func queryUpdated(_ query: String) {
|
||||
//self.containerNode.searchTextUpdated(text: query)
|
||||
}
|
||||
|
||||
override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
|
||||
@ -747,17 +747,22 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
|
||||
}
|
||||
|
||||
let localizationPreferencesKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.localizationListState]))
|
||||
let localizations = context.account.postbox.combinedView(keys: [localizationPreferencesKey])
|
||||
|> map { view -> [LocalizationInfo] in
|
||||
let localizations = combineLatest(context.account.postbox.combinedView(keys: [localizationPreferencesKey]), context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.localizationSettings]))
|
||||
|> map { view, sharedData -> [LocalizationInfo] in
|
||||
if let localizationListState = (view.views[localizationPreferencesKey] as? PreferencesView)?.values[PreferencesKeys.localizationListState] as? LocalizationListState, !localizationListState.availableOfficialLocalizations.isEmpty {
|
||||
|
||||
var existingIds = Set<String>()
|
||||
let availableSavedLocalizations = localizationListState.availableSavedLocalizations.filter({ info in !localizationListState.availableOfficialLocalizations.contains(where: { $0.languageCode == info.languageCode }) })
|
||||
|
||||
var activeLanguageCode: String?
|
||||
if let localizationSettings = sharedData.entries[SharedDataKeys.localizationSettings] as? LocalizationSettings {
|
||||
activeLanguageCode = localizationSettings.primaryComponent.languageCode
|
||||
}
|
||||
|
||||
var localizationItems: [LocalizationInfo] = []
|
||||
if !availableSavedLocalizations.isEmpty {
|
||||
for info in availableSavedLocalizations {
|
||||
if existingIds.contains(info.languageCode) {
|
||||
if existingIds.contains(info.languageCode) || info.languageCode == activeLanguageCode {
|
||||
continue
|
||||
}
|
||||
existingIds.insert(info.languageCode)
|
||||
@ -765,7 +770,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
|
||||
}
|
||||
}
|
||||
for info in localizationListState.availableOfficialLocalizations {
|
||||
if existingIds.contains(info.languageCode) {
|
||||
if existingIds.contains(info.languageCode) || info.languageCode == activeLanguageCode {
|
||||
continue
|
||||
}
|
||||
existingIds.insert(info.languageCode)
|
||||
|
||||
@ -4,6 +4,8 @@ import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
import TelegramUIPrivateModule
|
||||
|
||||
public final class TelegramRootController: NavigationController {
|
||||
private let context: AccountContext
|
||||
|
||||
@ -81,8 +83,52 @@ public final class TelegramRootController: NavigationController {
|
||||
self.accountSettingsController = accountSettingsController
|
||||
self.rootTabController = tabBarController
|
||||
self.pushViewController(tabBarController, animated: false)
|
||||
|
||||
|
||||
|
||||
|
||||
// guard let controller = self.viewControllers.last as? ViewController else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0) {
|
||||
// let wrapperNode = ASDisplayNode()
|
||||
// let bounds = controller.displayNode.bounds
|
||||
// wrapperNode.frame = bounds
|
||||
// wrapperNode.backgroundColor = .gray
|
||||
// //controller.displayNode.addSubnode(wrapperNode)
|
||||
//
|
||||
// let label = TGMarqLabel(frame: CGRect())
|
||||
// label.textColor = .white
|
||||
// label.font = Font.regular(28.0)
|
||||
// label.scrollDuration = 15.0
|
||||
// label.fadeLength = 25.0
|
||||
// label.trailingBuffer = 60.0
|
||||
// label.animationDelay = 2.0
|
||||
// label.text = "Lorem ipsum dolor sir amet, consecteur"
|
||||
// label.sizeToFit()
|
||||
// label.frame = CGRect(x: bounds.width / 2.0 - 100.0, y: 100.0, width: 200.0, height: label.frame.height)
|
||||
// //wrapperNode.view.addSubview(label)
|
||||
//
|
||||
// let data = testLineChartData()
|
||||
// let node = LineChartContainerNode(data: data)
|
||||
// node.frame = CGRect(x: 0.0, y: 100.0, width: bounds.width, height: 280.0)
|
||||
// node.updateLayout(size: node.frame.size)
|
||||
// wrapperNode.addSubnode(node)
|
||||
//
|
||||
// self.wNode = wrapperNode
|
||||
//
|
||||
// let gesture = UITapGestureRecognizer(target: self, action: #selector(self.closeIt))
|
||||
// wrapperNode.view.addGestureRecognizer(gesture)
|
||||
// }
|
||||
}
|
||||
|
||||
@objc func closeIt() {
|
||||
self.wNode?.removeFromSupernode()
|
||||
}
|
||||
|
||||
private var wNode: ASDisplayNode?
|
||||
|
||||
public func updateRootControllers(showCallsTab: Bool) {
|
||||
guard let rootTabController = self.rootTabController else {
|
||||
return
|
||||
|
||||
@ -40,22 +40,11 @@ final class WebSearchGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let width = size.width
|
||||
let panelSize: CGFloat = 49.0
|
||||
var panelHeight: CGFloat = panelSize + bottomInset
|
||||
panelHeight += contentInset
|
||||
var textFrame = CGRect()
|
||||
// if !self.textNode.isHidden {
|
||||
// let sideInset: CGFloat = 8.0 + leftInset
|
||||
// let topInset: CGFloat = 8.0
|
||||
// let textBottomInset: CGFloat = 8.0
|
||||
// let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
// panelHeight += textSize.height + topInset + textBottomInset
|
||||
// textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: textSize)
|
||||
// }
|
||||
|
||||
//self.textNode.frame = textFrame
|
||||
|
||||
self.cancelButton.frame = CGRect(origin: CGPoint(x: leftInset, y: panelHeight - bottomInset - panelSize), size: CGSize(width: panelSize, height: panelSize))
|
||||
self.sendButton.frame = CGRect(origin: CGPoint(x: width - panelSize - rightInset, y: panelHeight - bottomInset - panelSize), size: CGSize(width: panelSize, height: panelSize))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user