mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-02 12:48:45 +00:00
Various improvements and bug fixes
This commit is contained in:
parent
826e1291d6
commit
756e7b836a
@ -130,6 +130,8 @@
|
||||
09DD88FA21BFD70B000766BC /* ThemedTextAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88F921BFD70B000766BC /* ThemedTextAlertController.swift */; };
|
||||
09E4A801223AE1B30038140F /* PeerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09E4A800223AE1B30038140F /* PeerType.swift */; };
|
||||
09E4A803223B833B0038140F /* ForwardPrivacyChatPreviewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09E4A802223B833B0038140F /* ForwardPrivacyChatPreviewItem.swift */; };
|
||||
09E4A805223D4A5A0038140F /* OpenSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09E4A804223D4A5A0038140F /* OpenSettings.swift */; };
|
||||
09E4A807223D4B860038140F /* AccountUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09E4A806223D4B860038140F /* AccountUtils.swift */; };
|
||||
09EDAD26220D30980012A50B /* AutodownloadConnectionTypeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD25220D30980012A50B /* AutodownloadConnectionTypeController.swift */; };
|
||||
09EDAD2A220DA6A40012A50B /* VolumeButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD29220DA6A40012A50B /* VolumeButtons.swift */; };
|
||||
09EDAD2C2211552F0012A50B /* AutodownloadMediaCategoryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD2B2211552F0012A50B /* AutodownloadMediaCategoryController.swift */; };
|
||||
@ -1289,6 +1291,8 @@
|
||||
09DD88F921BFD70B000766BC /* ThemedTextAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedTextAlertController.swift; sourceTree = "<group>"; };
|
||||
09E4A800223AE1B30038140F /* PeerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerType.swift; sourceTree = "<group>"; };
|
||||
09E4A802223B833B0038140F /* ForwardPrivacyChatPreviewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardPrivacyChatPreviewItem.swift; sourceTree = "<group>"; };
|
||||
09E4A804223D4A5A0038140F /* OpenSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettings.swift; sourceTree = "<group>"; };
|
||||
09E4A806223D4B860038140F /* AccountUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountUtils.swift; sourceTree = "<group>"; };
|
||||
09EDAD25220D30980012A50B /* AutodownloadConnectionTypeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutodownloadConnectionTypeController.swift; sourceTree = "<group>"; };
|
||||
09EDAD29220DA6A40012A50B /* VolumeButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeButtons.swift; sourceTree = "<group>"; };
|
||||
09EDAD2B2211552F0012A50B /* AutodownloadMediaCategoryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutodownloadMediaCategoryController.swift; sourceTree = "<group>"; };
|
||||
@ -2640,6 +2644,7 @@
|
||||
D04ECD711FFBF22B00DE9029 /* OpenUrl.swift */,
|
||||
D0FC194C201F82A000FEDBB2 /* OpenResolvedUrl.swift */,
|
||||
D023836F1DDF0462004018B6 /* UrlHandling.swift */,
|
||||
09E4A804223D4A5A0038140F /* OpenSettings.swift */,
|
||||
);
|
||||
name = Routing;
|
||||
sourceTree = "<group>";
|
||||
@ -4861,6 +4866,7 @@
|
||||
090B48C72200BCA8005083FA /* WallpaperUploadManager.swift */,
|
||||
09D96898221DE92600B1458A /* ID3ArtworkReader.swift */,
|
||||
09E4A800223AE1B30038140F /* PeerType.swift */,
|
||||
09E4A806223D4B860038140F /* AccountUtils.swift */,
|
||||
);
|
||||
name = Utils;
|
||||
sourceTree = "<group>";
|
||||
@ -5415,6 +5421,7 @@
|
||||
D093D7DF2062F3F000BC3599 /* SecureIdDocumentFormController.swift in Sources */,
|
||||
D0E9BA371F05585000F079A4 /* STPPhoneNumberValidator.m in Sources */,
|
||||
0910B0EF21FA532D00F8F87D /* WallpaperResources.swift in Sources */,
|
||||
09E4A807223D4B860038140F /* AccountUtils.swift in Sources */,
|
||||
D069F5D0212700B90000565A /* StickerPanePeerSpecificSetupGridItem.swift in Sources */,
|
||||
D0EC6D041EB9F58800EBF1C3 /* opusenc.m in Sources */,
|
||||
D0A8998D217A294100759EE6 /* SaveIncomingMediaController.swift in Sources */,
|
||||
@ -5634,6 +5641,7 @@
|
||||
D0EC6D681EB9F58800EBF1C3 /* AuthorizationSequenceController.swift in Sources */,
|
||||
09F664C021EAAFAF00AB7E26 /* ThemeColorsGridController.swift in Sources */,
|
||||
D0EC6D691EB9F58800EBF1C3 /* AuthorizationSequenceSplashController.swift in Sources */,
|
||||
09E4A805223D4A5A0038140F /* OpenSettings.swift in Sources */,
|
||||
D0EC6D6A1EB9F58800EBF1C3 /* AuthorizationSequenceSplashControllerNode.swift in Sources */,
|
||||
D0C683FC21AD797F00A6CAD5 /* ChatListSelection.swift in Sources */,
|
||||
D0EC6D6B1EB9F58800EBF1C3 /* AuthorizationSequenceCountrySelectionController.swift in Sources */,
|
||||
|
||||
40
TelegramUI/AccountUtils.swift
Normal file
40
TelegramUI/AccountUtils.swift
Normal file
@ -0,0 +1,40 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
func activeAccountsAndPeers(context: AccountContext) -> Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> {
|
||||
let sharedContext = context.sharedContext
|
||||
return context.sharedContext.activeAccounts
|
||||
|> mapToSignal { primary, activeAccounts, _ -> Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> in
|
||||
var accounts: [Signal<(Account, Peer, Int32)?, NoError>] = []
|
||||
func accountWithPeer(_ account: Account) -> Signal<(Account, Peer, Int32)?, NoError> {
|
||||
return combineLatest(account.postbox.peerView(id: account.peerId), renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: account.postbox))
|
||||
|> map { view, totalUnreadCount -> (Peer?, Int32) in
|
||||
return (view.peers[view.peerId], totalUnreadCount.0)
|
||||
}
|
||||
|> distinctUntilChanged { lhs, rhs in
|
||||
return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1
|
||||
}
|
||||
|> map { peer, totalUnreadCount -> (Account, Peer, Int32)? in
|
||||
if let peer = peer {
|
||||
return (account, peer, totalUnreadCount)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
for (_, account, _) in activeAccounts {
|
||||
accounts.append(accountWithPeer(account))
|
||||
}
|
||||
|
||||
return combineLatest(accounts)
|
||||
|> map { accounts -> ((Account, Peer)?, [(Account, Peer, Int32)]) in
|
||||
var primaryRecord: (Account, Peer)?
|
||||
if let first = accounts.filter({ $0?.0.id == primary?.id }).first, let (account, peer, _) = first {
|
||||
primaryRecord = (account, peer)
|
||||
}
|
||||
return (primaryRecord, accounts.filter({ $0?.0.id != primary?.id }).compactMap({ $0 }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,8 @@ private func extractAnchor(string: String) -> (String, String?) {
|
||||
return (trimmedUrl, anchorValue)
|
||||
}
|
||||
|
||||
private let refreshTimeout: Int32 = 60 * 60 * 12
|
||||
|
||||
func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
|
||||
var faqUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_FAQ_URL
|
||||
if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
|
||||
@ -31,27 +33,36 @@ func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoErro
|
||||
|
||||
return cachedInstantPage(postbox: context.account.postbox, url: cachedUrl)
|
||||
|> mapToSignal { cachedInstantPage -> Signal<ResolvedUrl, NoError> in
|
||||
if let webPage = cachedInstantPage?.webPage, case let .Loaded(content) = webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
|
||||
return .single(.instantView(webPage, anchor))
|
||||
} else {
|
||||
return resolveInstantViewUrl(account: context.account, url: faqUrl)
|
||||
|> afterNext { result in
|
||||
if case let .instantView(webPage, _) = result, case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
|
||||
if instantPage.isComplete {
|
||||
let _ = updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage).start()
|
||||
} else {
|
||||
let _ = (actualizedWebpage(postbox: context.account.postbox, network: context.account.network, webpage: webPage)
|
||||
|> mapToSignal { webPage -> Signal<Void, NoError> in
|
||||
if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
|
||||
return updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
let updated = resolveInstantViewUrl(account: context.account, url: faqUrl)
|
||||
|> afterNext { result in
|
||||
if case let .instantView(webPage, _) = result, case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
|
||||
if instantPage.isComplete {
|
||||
let _ = updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage).start()
|
||||
} else {
|
||||
let _ = (actualizedWebpage(postbox: context.account.postbox, network: context.account.network, webpage: webPage)
|
||||
|> mapToSignal { webPage -> Signal<Void, NoError> in
|
||||
if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
|
||||
return updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let now = Int32(CFAbsoluteTimeGetCurrent())
|
||||
if let cachedInstantPage = cachedInstantPage, case let .Loaded(content) = cachedInstantPage.webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
|
||||
let current: Signal<ResolvedUrl, NoError> = .single(.instantView(cachedInstantPage.webPage, anchor))
|
||||
if now > cachedInstantPage.timestamp + refreshTimeout {
|
||||
return current
|
||||
|> then(updated)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
} else {
|
||||
return updated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +87,7 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
|
||||
currentAnchor = anchor
|
||||
case let .header(text):
|
||||
if let anchor = currentAnchor {
|
||||
results.append(SettingsSearchableItem(id: .faq(results.count + 1), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ], present: { context, present in
|
||||
results.append(SettingsSearchableItem(id: .faq(results.count + 1), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ], present: { context, _, present in
|
||||
present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor))
|
||||
}))
|
||||
}
|
||||
@ -96,7 +107,7 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
|
||||
for item in items {
|
||||
if case let .text(itemText, _) = item, case let .url(text, url, _) = itemText {
|
||||
let (_, anchor) = extractAnchor(string: url)
|
||||
results.append(SettingsSearchableItem(id: .faq(results.count + 1), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ, currentSection], present: { context, present in
|
||||
results.append(SettingsSearchableItem(id: .faq(results.count + 1), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ, currentSection], present: { context, _, present in
|
||||
present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor))
|
||||
}))
|
||||
}
|
||||
|
||||
@ -5,17 +5,21 @@ import TelegramCore
|
||||
|
||||
final class CachedInstantPage: PostboxCoding {
|
||||
let webPage: TelegramMediaWebpage
|
||||
let timestamp: Int32
|
||||
|
||||
init(webPage: TelegramMediaWebpage) {
|
||||
init(webPage: TelegramMediaWebpage, timestamp: Int32) {
|
||||
self.webPage = webPage
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
|
||||
init(decoder: PostboxDecoder) {
|
||||
self.webPage = decoder.decodeObjectForKey("webpage", decoder: { TelegramMediaWebpage(decoder: $0) }) as! TelegramMediaWebpage
|
||||
self.timestamp = decoder.decodeInt32ForKey("timestamp", orElse: 0)
|
||||
}
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeObject(self.webPage, forKey: "webpage")
|
||||
encoder.encodeInt32(self.timestamp, forKey: "timestamp")
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +43,7 @@ func updateCachedInstantPage(postbox: Postbox, url: String, webPage: TelegramMed
|
||||
key.setInt64(0, value: Int64(url.hashValue))
|
||||
let id = ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedInstantPages, key: key)
|
||||
if let webPage = webPage {
|
||||
transaction.putItemCacheEntry(id: id, entry: CachedInstantPage(webPage: webPage), collectionSpec: collectionSpec)
|
||||
transaction.putItemCacheEntry(id: id, entry: CachedInstantPage(webPage: webPage, timestamp: Int32(CFAbsoluteTimeGetCurrent())), collectionSpec: collectionSpec)
|
||||
} else {
|
||||
transaction.removeItemCacheEntry(id: id)
|
||||
}
|
||||
|
||||
@ -296,6 +296,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
return false
|
||||
}
|
||||
strongSelf.commitPurposefulAction()
|
||||
strongSelf.videoUnmuteTooltipController?.dismiss()
|
||||
|
||||
var openMessageByAction: Bool = false
|
||||
|
||||
@ -1101,6 +1102,10 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
if let strongSelf = self {
|
||||
openAddContact(context: strongSelf.context, phoneNumber: phoneNumber, present: { [weak self] controller, arguments in
|
||||
self?.present(controller, in: .window(.root), with: arguments)
|
||||
}, pushController: { [weak self] controller in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, rateCall: { [weak self] message, callId in
|
||||
@ -2232,6 +2237,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
if isAction && (actions.options == .deleteGlobally || actions.options == .deleteLocally) {
|
||||
let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: actions.options == .deleteLocally ? .forLocalPeer : .forEveryone).start()
|
||||
} else if actions.options == .cancelSending {
|
||||
let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: .forEveryone).start()
|
||||
} else {
|
||||
var options = actions.options
|
||||
if messages.first?.flags.isSending ?? false {
|
||||
@ -2699,7 +2706,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
if let location = location, let icon = icon {
|
||||
strongSelf.mediaRestrictedTooltipController?.dismiss()
|
||||
let tooltipController = TooltipController(content: .iconAndText(icon, strongSelf.presentationInterfaceState.strings.Conversation_PressVolumeButtonForSound), timeout: 3.5, dismissImmediatelyOnLayoutUpdate: true)
|
||||
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
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.videoUnmuteTooltipController === tooltipController {
|
||||
@ -3240,8 +3247,6 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
guard let strongSelf = self, strongSelf.traceVisibility() && isTopmostChatController(strongSelf) else {
|
||||
return
|
||||
}
|
||||
ApplicationSpecificNotice.setVolumeButtonToUnmute(accountManager: strongSelf.context.sharedContext.accountManager)
|
||||
strongSelf.videoUnmuteTooltipController?.dismiss()
|
||||
strongSelf.chatDisplayNode.playFirstMediaWithSound()
|
||||
})
|
||||
|
||||
@ -6104,9 +6109,6 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
})
|
||||
}
|
||||
}),
|
||||
KeyShortcut(input: "W", modifiers: [.command], action: { [weak self] in
|
||||
|
||||
})
|
||||
]
|
||||
|
||||
return inputShortcuts + otherShortcuts
|
||||
|
||||
@ -597,6 +597,8 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
strongSelf.view.endEditing(true)
|
||||
openAddContact(context: strongSelf.context, phoneNumber: phoneNumber, present: { [weak self] controller, arguments in
|
||||
self?.present(controller, in: .window(.root), with: arguments)
|
||||
}, pushController: { [weak self] controller in
|
||||
(self?.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}, completed: {
|
||||
self?.deactivateSearch(animated: false)
|
||||
})
|
||||
|
||||
@ -116,6 +116,10 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override var isEmpty: Bool {
|
||||
return self.multiplexedNode?.files.isEmpty ?? true
|
||||
}
|
||||
|
||||
override func willEnterHierarchy() {
|
||||
super.willEnterHierarchy()
|
||||
|
||||
|
||||
@ -871,9 +871,9 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
strongSelf.controllerInteraction.sendGif(file)
|
||||
}
|
||||
}),
|
||||
PeekControllerMenuItem(title: strongSelf.strings.Common_Delete, color: .destructive, action: {
|
||||
PeekControllerMenuItem(title: strongSelf.strings.Preview_SaveGif, color: .accent, action: {
|
||||
if let strongSelf = self {
|
||||
let _ = removeSavedGif(postbox: strongSelf.context.account.postbox, mediaId: file.media.fileId).start()
|
||||
let _ = addSavedGif(postbox: strongSelf.context.account.postbox, fileReference: file).start()
|
||||
}
|
||||
})
|
||||
])))
|
||||
@ -1375,11 +1375,13 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
self.searchContainerNode = nil
|
||||
self.searchContainerNodeLoadedDisposable.set(nil)
|
||||
|
||||
var paneIsEmpty = false
|
||||
var placeholderNode: PaneSearchBarPlaceholderNode?
|
||||
if let searchMode = searchMode {
|
||||
switch searchMode {
|
||||
case .gif:
|
||||
placeholderNode = self.gifPane.searchPlaceholderNode
|
||||
paneIsEmpty = self.gifPane.isEmpty
|
||||
case .sticker:
|
||||
self.stickerPane.gridNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? PaneSearchBarPlaceholderNode {
|
||||
@ -1389,7 +1391,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
}
|
||||
}
|
||||
if let placeholderNode = placeholderNode {
|
||||
searchContainerNode.animateOut(to: placeholderNode, transition: transition, completion: { [weak searchContainerNode] in
|
||||
searchContainerNode.animateOut(to: placeholderNode, animateOutSearchBar: !paneIsEmpty, transition: transition, completion: { [weak searchContainerNode] in
|
||||
searchContainerNode?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
|
||||
@ -10,6 +10,9 @@ struct ChatMediaInputPaneScrollState {
|
||||
class ChatMediaInputPane: ASDisplayNode {
|
||||
var inputNodeInteraction: ChatMediaInputNodeInteraction?
|
||||
var collectionListPanelOffset: CGFloat = 0.0
|
||||
var isEmpty: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, topInset: CGFloat, bottomInset: CGFloat, isExpanded: Bool, isVisible: Bool, transition: ContainedViewLayoutTransition) {
|
||||
}
|
||||
|
||||
@ -150,6 +150,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
private var currentSwipeToReplyTranslation: CGFloat = 0.0
|
||||
|
||||
private var appliedItem: ChatMessageItem?
|
||||
private var appliedForwardInfo: (Peer?, String?)?
|
||||
|
||||
override var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
@ -314,6 +315,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
let layoutConstants = self.layoutConstants
|
||||
|
||||
let currentItem = self.appliedItem
|
||||
let currentForwardInfo = self.appliedForwardInfo
|
||||
|
||||
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
||||
let accessibilityData = ChatMessageAccessibilityData(item: item)
|
||||
@ -746,6 +748,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
var forwardInfoOriginY: CGFloat = 0.0
|
||||
var forwardInfoSizeApply: (CGSize, () -> ChatMessageForwardInfoNode?) = (CGSize(), { nil })
|
||||
|
||||
var forwardSource: Peer?
|
||||
var forwardAuthorSignature: String?
|
||||
|
||||
if displayHeader {
|
||||
if authorNameString != nil || inlineBotNameString != nil {
|
||||
if headerSize.height.isZero {
|
||||
@ -790,13 +795,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
headerSize.width = max(headerSize.width, nameNodeSizeApply.0.width + adminBadgeSizeAndApply.0.size.width + layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right)
|
||||
headerSize.height += nameNodeSizeApply.0.height
|
||||
}
|
||||
|
||||
|
||||
if !ignoreForward, let forwardInfo = firstMessage.forwardInfo {
|
||||
if headerSize.height.isZero {
|
||||
headerSize.height += 5.0
|
||||
}
|
||||
let forwardSource: Peer?
|
||||
let forwardAuthorSignature: String?
|
||||
|
||||
if let source = forwardInfo.source {
|
||||
forwardSource = source
|
||||
@ -808,8 +811,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
forwardAuthorSignature = nil
|
||||
}
|
||||
} else {
|
||||
forwardSource = forwardInfo.author
|
||||
forwardAuthorSignature = forwardInfo.authorSignature
|
||||
if let currentForwardInfo = currentForwardInfo, forwardInfo.author == nil && currentForwardInfo.0 != nil {
|
||||
forwardSource = currentForwardInfo.0
|
||||
forwardAuthorSignature = currentForwardInfo.1
|
||||
} else {
|
||||
forwardSource = forwardInfo.author
|
||||
forwardAuthorSignature = forwardInfo.authorSignature
|
||||
}
|
||||
}
|
||||
let sizeAndApply = forwardInfoLayout(item.presentationData, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, forwardAuthorSignature, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude))
|
||||
forwardInfoSizeApply = (sizeAndApply.0, { sizeAndApply.1() })
|
||||
@ -1148,6 +1156,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
return (layout, { [weak self] animation, synchronousLoads in
|
||||
if let strongSelf = self {
|
||||
strongSelf.appliedItem = item
|
||||
strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature)
|
||||
strongSelf.accessibilityLabel = accessibilityData.label
|
||||
strongSelf.accessibilityValue = accessibilityData.value
|
||||
|
||||
@ -1597,7 +1606,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id {
|
||||
item.controllerInteraction.openPeer(id, .info, nil)
|
||||
} else if let authorSignature = forwardInfo.authorSignature {
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode)
|
||||
}
|
||||
return
|
||||
|
||||
@ -20,6 +20,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
private var swipeToReplyFeedback: HapticFeedback?
|
||||
|
||||
private var appliedItem: ChatMessageItem?
|
||||
private var appliedForwardInfo: (Peer?, String?)?
|
||||
|
||||
private var forwardInfoNode: ChatMessageForwardInfoNode?
|
||||
private var forwardBackgroundNode: ASImageNode?
|
||||
@ -95,9 +96,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
let actionButtonsLayout = ChatMessageActionButtonsNode.asyncLayout(self.actionButtonsNode)
|
||||
|
||||
let currentItem = self.appliedItem
|
||||
let currentForwardInfo = self.appliedForwardInfo
|
||||
|
||||
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
||||
let baseWidth = params.width - params.leftInset - params.rightInset
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
|
||||
let avatarInset: CGFloat
|
||||
@ -268,13 +269,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
|
||||
let availableContentWidth = params.width - params.leftInset - params.rightInset - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left
|
||||
|
||||
var forwardSource: Peer?
|
||||
var forwardAuthorSignature: String?
|
||||
|
||||
var forwardInfoSizeApply: (CGSize, () -> ChatMessageForwardInfoNode)?
|
||||
var updatedForwardBackgroundNode: ASImageNode?
|
||||
var forwardBackgroundImage: UIImage?
|
||||
if let forwardInfo = item.message.forwardInfo {
|
||||
let forwardSource: Peer?
|
||||
let forwardAuthorSignature: String?
|
||||
|
||||
if let source = forwardInfo.source {
|
||||
forwardSource = source
|
||||
if let authorSignature = forwardInfo.authorSignature {
|
||||
@ -285,8 +286,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
forwardAuthorSignature = nil
|
||||
}
|
||||
} else {
|
||||
forwardSource = forwardInfo.author
|
||||
forwardAuthorSignature = forwardInfo.authorSignature
|
||||
if let currentForwardInfo = currentForwardInfo, forwardInfo.author == nil && currentForwardInfo.0 != nil {
|
||||
forwardSource = currentForwardInfo.0
|
||||
forwardAuthorSignature = currentForwardInfo.1
|
||||
} else {
|
||||
forwardSource = forwardInfo.author
|
||||
forwardAuthorSignature = forwardInfo.authorSignature
|
||||
}
|
||||
}
|
||||
let availableWidth = max(60.0, availableContentWidth - videoLayout.contentSize.width + 6.0)
|
||||
forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
@ -322,7 +328,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.appliedItem = item
|
||||
|
||||
strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature)
|
||||
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if animation.isAnimated {
|
||||
transition = .animated(duration: 0.2, curve: .spring)
|
||||
@ -508,7 +515,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id {
|
||||
item.controllerInteraction.openPeer(id, .chat(textInputState: nil, messageId: nil), nil)
|
||||
} else if let authorSignature = forwardInfo.authorSignature {
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode)
|
||||
}
|
||||
return
|
||||
|
||||
@ -747,7 +747,10 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
statusForegroundColor = presentationData.wallpaper.isEmpty ? bubbleTheme.outgoing.withoutWallpaper.fill : bubbleTheme.outgoing.withWallpaper.fill
|
||||
}
|
||||
switch resourceStatus.mediaStatus {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
case var .fetchStatus(fetchStatus):
|
||||
if self.message?.forwardInfo != nil {
|
||||
fetchStatus = resourceStatus.fetchStatus
|
||||
}
|
||||
self.waveformScrubbingNode?.enableScrubbing = false
|
||||
switch fetchStatus {
|
||||
case let .Fetching(_, progress):
|
||||
@ -822,8 +825,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
streamingStatusNode.transitionToState(streamingState, completion: {
|
||||
})
|
||||
streamingStatusNode.transitionToState(streamingState)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -45,8 +45,16 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
private let infoBackgroundNode: ASImageNode
|
||||
private let muteIconNode: ASImageNode
|
||||
|
||||
private var status: FileMediaResourceMediaStatus?
|
||||
private var status: FileMediaResourceStatus?
|
||||
private var playerStatus: MediaPlayerStatus? {
|
||||
didSet {
|
||||
if self.playerStatus != oldValue {
|
||||
self.updateStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
private let playbackStatusDisposable = MetaDisposable()
|
||||
private let playerStatusDisposable = MetaDisposable()
|
||||
private let fetchedThumbnailDisposable = MetaDisposable()
|
||||
|
||||
private var shouldAcquireVideoContext: Bool {
|
||||
@ -98,6 +106,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
deinit {
|
||||
self.fetchDisposable.dispose()
|
||||
self.playbackStatusDisposable.dispose()
|
||||
self.playerStatusDisposable.dispose()
|
||||
self.fetchedThumbnailDisposable.dispose()
|
||||
}
|
||||
|
||||
@ -293,17 +302,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
transition.updateFrame(node: strongSelf.muteIconNode, frame: muteIconFrame)
|
||||
}
|
||||
|
||||
if let updatedPlaybackStatus = updatedPlaybackStatus {
|
||||
strongSelf.playbackStatusDisposable.set((updatedPlaybackStatus
|
||||
|> deliverOnMainQueue).start(next: { status in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.status = status.mediaStatus
|
||||
strongSelf.updateStatus()
|
||||
}))
|
||||
}
|
||||
|
||||
if let updatedFile = updatedFile, updatedMedia {
|
||||
if let resource = updatedFile.previewRepresentations.first?.resource {
|
||||
strongSelf.fetchedThumbnailDisposable.set(fetchedMediaResource(postbox: item.context.account.postbox, reference: FileMediaReference.message(message: MessageReference(item.message), media: updatedFile).resourceReference(resource)).start())
|
||||
@ -326,6 +324,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: min(floor(videoFrame.midX) + 55.0, videoFrame.maxX + right - dateAndStatusSize.width - 4.0), y: videoFrame.maxY - dateAndStatusSize.height), size: dateAndStatusSize)
|
||||
}
|
||||
|
||||
var updatedPlayerStatusSignal: Signal<MediaPlayerStatus?, NoError>?
|
||||
if let telegramFile = updatedFile, updatedMedia {
|
||||
let durationTextColor: UIColor
|
||||
let durationFillColor: UIColor
|
||||
@ -353,7 +352,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
durationNode.defaultDuration = telegramFile.duration.flatMap(Double.init)
|
||||
|
||||
let streamVideo = isMediaStreamable(message: item.message, media: telegramFile)
|
||||
let streamVideo = automaticDownload && isMediaStreamable(message: item.message, media: telegramFile)
|
||||
if let videoNode = strongSelf.videoNode {
|
||||
videoNode.layer.allowsGroupOpacity = true
|
||||
videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.5, delay: 0.2, removeOnCompletion: false, completion: { [weak videoNode] _ in
|
||||
@ -371,7 +370,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: streamVideo, enableSound: false, fetchAutomatically: false), priority: .embedded, autoplay: true)
|
||||
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: streamVideo ? .earlierStart : .none, enableSound: false, fetchAutomatically: false), priority: .embedded, autoplay: true)
|
||||
let previousVideoNode = strongSelf.videoNode
|
||||
strongSelf.videoNode = videoNode
|
||||
strongSelf.insertSubnode(videoNode, belowSubnode: previousVideoNode ?? strongSelf.dateAndStatusNode)
|
||||
@ -385,6 +384,36 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
strongSelf.insertSubnode(strongSelf.secretVideoPlaceholder, belowSubnode: videoNode)
|
||||
}
|
||||
}
|
||||
|
||||
updatedPlayerStatusSignal = videoNode.status
|
||||
|> mapToSignal { status -> Signal<MediaPlayerStatus?, NoError> in
|
||||
if let status = status, case .buffering = status.status {
|
||||
return .single(status) |> delay(1.0, queue: Queue.mainQueue())
|
||||
} else {
|
||||
return .single(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let updatedPlaybackStatus = updatedPlaybackStatus {
|
||||
strongSelf.playbackStatusDisposable.set((updatedPlaybackStatus
|
||||
|> deliverOnMainQueue).start(next: { status in
|
||||
if let strongSelf = self {
|
||||
strongSelf.status = status
|
||||
strongSelf.updateStatus()
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if let updatedPlayerStatusSignal = updatedPlayerStatusSignal {
|
||||
strongSelf.playerStatusDisposable.set((updatedPlayerStatusSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
displayLinkDispatcher.dispatch {
|
||||
if let strongSelf = self {
|
||||
strongSelf.playerStatus = status
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if let durationNode = strongSelf.durationNode {
|
||||
@ -448,7 +477,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let displayMute: Bool
|
||||
switch status {
|
||||
switch status.mediaStatus {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
switch fetchStatus {
|
||||
case .Local:
|
||||
@ -472,7 +501,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var progressRequired = false
|
||||
if case let .fetchStatus(fetchStatus) = status {
|
||||
if case let .fetchStatus(fetchStatus) = status.mediaStatus {
|
||||
if case .Local = fetchStatus {
|
||||
if file.isVideo {
|
||||
progressRequired = true
|
||||
@ -484,10 +513,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
if item.message.flags.isSending && item.message.forwardInfo != nil {
|
||||
progressRequired = false
|
||||
}
|
||||
|
||||
if progressRequired {
|
||||
if self.statusNode == nil {
|
||||
let statusNode = RadialStatusNode(backgroundNodeColor: item.presentationData.theme.theme.chat.bubble.mediaOverlayControlBackgroundColor)
|
||||
@ -505,13 +530,34 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var isBuffering: Bool?
|
||||
if let message = self.item?.message, let media = self.media, let size = media.size, (isMediaStreamable(message: message, media: media) || size <= 256 * 1024) && (self.automaticDownload ?? false) {
|
||||
if let playerStatus = self.playerStatus, case .buffering = playerStatus.status {
|
||||
isBuffering = true
|
||||
} else {
|
||||
isBuffering = false
|
||||
}
|
||||
}
|
||||
|
||||
var state: RadialStatusNodeState
|
||||
switch status {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
switch status.mediaStatus {
|
||||
case var .fetchStatus(fetchStatus):
|
||||
if item.message.forwardInfo != nil {
|
||||
fetchStatus = status.fetchStatus
|
||||
}
|
||||
|
||||
switch fetchStatus {
|
||||
case let .Fetching(_, progress):
|
||||
let adjustedProgress = max(progress, 0.027)
|
||||
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||
if let isBuffering = isBuffering {
|
||||
if isBuffering {
|
||||
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: nil, cancelEnabled: false)
|
||||
} else {
|
||||
state = .none
|
||||
}
|
||||
} else {
|
||||
let adjustedProgress = max(progress, 0.027)
|
||||
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||
}
|
||||
case .Local:
|
||||
if isSecretMedia && self.secretProgressIcon != nil {
|
||||
if let (beginTime, timeout) = secretBeginTimeAndTimeout {
|
||||
@ -526,7 +572,11 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
state = .download(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
}
|
||||
default:
|
||||
state = .none
|
||||
if isBuffering ?? false {
|
||||
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: nil, cancelEnabled: false)
|
||||
} else {
|
||||
state = .none
|
||||
}
|
||||
}
|
||||
if let statusNode = self.statusNode {
|
||||
if state == .none {
|
||||
@ -539,7 +589,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
|
||||
if case .playbackStatus = status {
|
||||
if case .playbackStatus = status.mediaStatus {
|
||||
let playbackStatusNode: InstantVideoRadialStatusNode
|
||||
if let current = self.playbackStatusNode {
|
||||
playbackStatusNode = current
|
||||
@ -608,7 +658,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
if self.infoBackgroundNode.alpha.isZero {
|
||||
if let status = self.status, case let .fetchStatus(fetchStatus) = status, case .Remote = fetchStatus {
|
||||
if let status = self.status, case let .fetchStatus(fetchStatus) = status.mediaStatus, case .Remote = fetchStatus {
|
||||
item.context.sharedContext.mediaManager.playlistControl(.playback(.pause), type: .voice)
|
||||
self.videoNode?.fetchControl(.fetch)
|
||||
} else {
|
||||
@ -638,7 +688,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
if let status = self.status {
|
||||
switch status {
|
||||
switch status.mediaStatus {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
switch fetchStatus {
|
||||
case .Fetching:
|
||||
|
||||
@ -82,7 +82,7 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode {
|
||||
return self.measureNode.measure(CGSize(width: 240.0, height: 160.0)).width
|
||||
}
|
||||
|
||||
func update(theme: PresentationTheme, content: ChatMessageInteractiveMediaBadgeContent?, mediaDownloadState: ChatMessageInteractiveMediaDownloadState?, alignment: NSTextAlignment = .left, animated: Bool) {
|
||||
func update(theme: PresentationTheme, content: ChatMessageInteractiveMediaBadgeContent?, mediaDownloadState: ChatMessageInteractiveMediaDownloadState?, alignment: NSTextAlignment = .left, animated: Bool, badgeAnimated: Bool = true) {
|
||||
var transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
||||
|
||||
let previousContentSize = self.previousContentSize
|
||||
@ -285,9 +285,9 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode {
|
||||
mediaStatusFrame = CGRect(origin: CGPoint(x: 7.0 + originX, y: originY), size: CGSize(width: 28.0, height: 28.0))
|
||||
}
|
||||
mediaDownloadStatusNode.frame = mediaStatusFrame
|
||||
mediaDownloadStatusNode.transitionToState(state, animated: true, completion: {})
|
||||
mediaDownloadStatusNode.transitionToState(state, animated: badgeAnimated, completion: {})
|
||||
} else if let mediaDownloadStatusNode = self.mediaDownloadStatusNode {
|
||||
mediaDownloadStatusNode.transitionToState(.none, animated: true, completion: {})
|
||||
mediaDownloadStatusNode.transitionToState(.none, animated: badgeAnimated, completion: {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,6 +41,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
private let imageNode: TransformImageNode
|
||||
private var currentImageArguments: TransformImageArguments?
|
||||
private var videoNode: UniversalVideoNode?
|
||||
private var videoContent: NativeVideoContent?
|
||||
private var statusNode: RadialStatusNode?
|
||||
var videoNodeDecoration: ChatBubbleVideoDecoration?
|
||||
private var badgeNode: ChatMessageInteractiveMediaBadge?
|
||||
@ -73,7 +74,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
} else {
|
||||
self.stopTimer()
|
||||
}
|
||||
self.updateFetchStatus()
|
||||
self.updateStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,7 +177,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
if case .ended = recognizer.state {
|
||||
let point = recognizer.location(in: self.imageNode.view)
|
||||
if let fetchStatus = self.fetchStatus, case .Local = fetchStatus {
|
||||
self.activateLocalContent((self.automaticPlayback ?? false) ? .automaticPlayback : .default)
|
||||
var videoContentMatch = true
|
||||
if let content = self.videoContent, case let .message(id, _, _) = content.nativeId {
|
||||
videoContentMatch = self.message?.id == id
|
||||
}
|
||||
self.activateLocalContent((self.automaticPlayback ?? false) && videoContentMatch ? .automaticPlayback : .default)
|
||||
} else {
|
||||
if let message = self.message, message.flags.isSending {
|
||||
if let statusNode = self.statusNode, statusNode.frame.contains(point) {
|
||||
@ -594,13 +599,15 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
let mediaManager = context.sharedContext.mediaManager
|
||||
|
||||
let streamVideo = isMediaStreamable(message: message, media: updatedVideoFile)
|
||||
let videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: decoration, content: NativeVideoContent(id: .message(message.id, message.stableId, updatedVideoFile.fileId), fileReference: .message(message: MessageReference(message), media: updatedVideoFile), streamVideo: streamVideo, enableSound: false, fetchAutomatically: false, onlyFullSizeThumbnail: (onlyFullSizeVideoThumbnail ?? false), continuePlayingWithoutSoundOnLostAudioSession: isInlinePlayableVideo, placeholderColor: emptyColor), priority: .embedded)
|
||||
let videoContent = NativeVideoContent(id: .message(message.id, message.stableId, updatedVideoFile.fileId), fileReference: .message(message: MessageReference(message), media: updatedVideoFile), streamVideo: streamVideo ? .earlierStart : .none, enableSound: false, fetchAutomatically: false, onlyFullSizeThumbnail: (onlyFullSizeVideoThumbnail ?? false), continuePlayingWithoutSoundOnLostAudioSession: isInlinePlayableVideo, placeholderColor: emptyColor)
|
||||
let videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: decoration, content: videoContent, priority: .embedded)
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
videoNode.ownsContentNodeUpdated = { [weak self] owns in
|
||||
if let strongSelf = self {
|
||||
strongSelf.videoNode?.isHidden = !owns
|
||||
}
|
||||
}
|
||||
strongSelf.videoContent = videoContent
|
||||
strongSelf.videoNode = videoNode
|
||||
|
||||
updatedVideoNodeReadySignal = videoNode.ready
|
||||
@ -649,19 +656,21 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let updatedStatusSignal = updatedStatusSignal {
|
||||
strongSelf.statusDisposable.set((updatedStatusSignal |> deliverOnMainQueue).start(next: { [weak strongSelf] status, actualFetchStatus in
|
||||
strongSelf.statusDisposable.set((updatedStatusSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] status, actualFetchStatus in
|
||||
displayLinkDispatcher.dispatch {
|
||||
if let strongSelf = strongSelf {
|
||||
strongSelf.fetchStatus = status
|
||||
strongSelf.actualFetchStatus = actualFetchStatus
|
||||
strongSelf.updateFetchStatus()
|
||||
strongSelf.updateStatus()
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if let updatedVideoNodeReadySignal = updatedVideoNodeReadySignal {
|
||||
strongSelf.videoNodeReadyDisposable.set((updatedVideoNodeReadySignal |> deliverOnMainQueue).start(next: { [weak strongSelf] status in
|
||||
strongSelf.videoNodeReadyDisposable.set((updatedVideoNodeReadySignal
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] status in
|
||||
displayLinkDispatcher.dispatch {
|
||||
if let strongSelf = strongSelf, let videoNode = strongSelf.videoNode {
|
||||
strongSelf.insertSubnode(videoNode, aboveSubnode: strongSelf.imageNode)
|
||||
@ -671,7 +680,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let updatedPlayerStatusSignal = updatedPlayerStatusSignal {
|
||||
strongSelf.playerStatusDisposable.set((updatedPlayerStatusSignal |> deliverOnMainQueue).start(next: { [weak strongSelf] status in
|
||||
strongSelf.playerStatusDisposable.set((updatedPlayerStatusSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] status in
|
||||
displayLinkDispatcher.dispatch {
|
||||
if let strongSelf = strongSelf {
|
||||
strongSelf.playerStatus = status
|
||||
@ -731,7 +741,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
strongSelf.fetchControls.with({ $0 })?.fetch(false)
|
||||
}
|
||||
|
||||
strongSelf.updateFetchStatus()
|
||||
strongSelf.updateStatus()
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -742,7 +752,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
private func ensureHasTimer() {
|
||||
if self.playerUpdateTimer == nil {
|
||||
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
||||
self?.updateFetchStatus()
|
||||
self?.updateStatus()
|
||||
}, queue: Queue.mainQueue())
|
||||
self.playerUpdateTimer = timer
|
||||
timer.start()
|
||||
@ -754,11 +764,18 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
self.playerUpdateTimer = nil
|
||||
}
|
||||
|
||||
private func updateFetchStatus() {
|
||||
private func updateStatus() {
|
||||
guard let (theme, strings, decimalSeparator) = self.themeAndStrings, let sizeCalculation = self.sizeCalculation, let message = self.message, var automaticPlayback = self.automaticPlayback, let wideLayout = self.wideLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let automaticDownload: Bool
|
||||
if let autoDownload = self.automaticDownload, case .full = autoDownload {
|
||||
automaticDownload = true
|
||||
} else {
|
||||
automaticDownload = false
|
||||
}
|
||||
|
||||
var secretBeginTimeAndTimeout: (Double?, Double)?
|
||||
let isSecretMedia = message.containsSecretMedia
|
||||
if isSecretMedia {
|
||||
@ -814,10 +831,6 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
progressRequired = true
|
||||
}
|
||||
}
|
||||
|
||||
if message.flags.isSending && message.forwardInfo != nil {
|
||||
progressRequired = false
|
||||
}
|
||||
}
|
||||
|
||||
let radialStatusSize: CGFloat = wideLayout ? 50.0 : 32.0
|
||||
@ -893,7 +906,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
automaticPlayback = false
|
||||
}
|
||||
|
||||
if let actualFetchStatus = self.actualFetchStatus, automaticPlayback {
|
||||
if let actualFetchStatus = self.actualFetchStatus, automaticPlayback || message.forwardInfo != nil {
|
||||
fetchStatus = actualFetchStatus
|
||||
}
|
||||
|
||||
@ -913,9 +926,12 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
if let file = self.media as? TelegramMediaFile {
|
||||
if wideLayout {
|
||||
if let size = file.size {
|
||||
if let duration = file.duration, !message.flags.contains(.Unsent) {
|
||||
let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, decimalSeparator: decimalSeparator)) / \(dataSizeString(size, forceDecimal: true, decimalSeparator: decimalSeparator))"
|
||||
if file.isAnimated && !automaticDownload {
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: "GIF " + sizeString, size: nil, muted: false, active: false)
|
||||
}
|
||||
else if let duration = file.duration, !message.flags.contains(.Unsent) {
|
||||
let durationString = file.isAnimated ? "GIF" : stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
|
||||
let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, decimalSeparator: decimalSeparator)) / \(dataSizeString(size, forceDecimal: true, decimalSeparator: decimalSeparator))"
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active)
|
||||
mediaDownloadState = .fetching(progress: automaticPlayback ? nil : adjustedProgress)
|
||||
@ -1015,24 +1031,29 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
case .Remote:
|
||||
state = .download(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
if let file = self.media as? TelegramMediaFile {
|
||||
let durationString = file.isAnimated ? "GIF" : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition)
|
||||
if wideLayout {
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
state = automaticPlayback ? .none : .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(file.size ?? 0, decimalSeparator: decimalSeparator), muted: muted, active: true)
|
||||
mediaDownloadState = .remote
|
||||
} else {
|
||||
state = automaticPlayback ? .none : state
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: nil, muted: muted, active: false)
|
||||
}
|
||||
if file.isAnimated && !automaticDownload {
|
||||
let string = "GIF " + dataSizeString(file.size ?? 0, decimalSeparator: decimalSeparator)
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: string, size: nil, muted: false, active: false)
|
||||
} else {
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
state = automaticPlayback ? .none : .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
badgeContent = .text(inset: 12.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString))
|
||||
mediaDownloadState = .compactRemote
|
||||
let durationString = file.isAnimated ? "GIF" : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition)
|
||||
if wideLayout {
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
state = automaticPlayback ? .none : .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(file.size ?? 0, decimalSeparator: decimalSeparator), muted: muted, active: true)
|
||||
mediaDownloadState = .remote
|
||||
} else {
|
||||
state = automaticPlayback ? .none : state
|
||||
badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: nil, muted: muted, active: false)
|
||||
}
|
||||
} else {
|
||||
state = automaticPlayback ? .none : state
|
||||
badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString))
|
||||
if isMediaStreamable(message: message, media: file) {
|
||||
state = automaticPlayback ? .none : .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||
badgeContent = .text(inset: 12.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString))
|
||||
mediaDownloadState = .compactRemote
|
||||
} else {
|
||||
state = automaticPlayback ? .none : state
|
||||
badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let webpage = webpage, let automaticDownload = self.automaticDownload, case .full = automaticDownload, case let .Loaded(content) = webpage.content, content.type != "telegram_background" {
|
||||
@ -1092,7 +1113,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
if isSecretMedia, secretBeginTimeAndTimeout?.0 != nil {
|
||||
if self.secretTimer == nil {
|
||||
self.secretTimer = SwiftSignalKit.Timer(timeout: 0.3, repeat: true, completion: { [weak self] in
|
||||
self?.updateFetchStatus()
|
||||
self?.updateStatus()
|
||||
}, queue: Queue.mainQueue())
|
||||
self.secretTimer?.start()
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
if let context = self.context {
|
||||
let mediaManager = context.sharedContext.mediaManager
|
||||
let mediaPlayer = MediaPlayer(audioSessionManager: mediaManager.audioSession, postbox: context.account.postbox, resourceReference: .standalone(resource: recordedMediaPreview.resource), streamable: false, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true)
|
||||
let mediaPlayer = MediaPlayer(audioSessionManager: mediaManager.audioSession, postbox: context.account.postbox, resourceReference: .standalone(resource: recordedMediaPreview.resource), streamable: .none, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true)
|
||||
self.mediaPlayer = mediaPlayer
|
||||
self.durationLabel.defaultDuration = Double(recordedMediaPreview.duration)
|
||||
self.durationLabel.status = mediaPlayer.status
|
||||
|
||||
@ -27,7 +27,7 @@ private let rootNavigationBar = PresentationThemeRootNavigationBar(
|
||||
disabledButtonColor: UIColor(rgb: 0x525252),
|
||||
primaryTextColor: accentColor,
|
||||
secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
controlColor: accentColor,
|
||||
controlColor: UIColor(rgb: 0x767677),
|
||||
accentTextColor: accentColor,
|
||||
backgroundColor: UIColor(rgb: 0x1c1c1d),
|
||||
separatorColor: UIColor(rgb: 0x000000),
|
||||
|
||||
@ -29,6 +29,19 @@ private enum SettingsSection: Int32 {
|
||||
case logOut
|
||||
}
|
||||
|
||||
public enum EditSettingsEntryTag: ItemListItemTag {
|
||||
case bio
|
||||
|
||||
func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
if let other = other as? EditSettingsEntryTag, self == other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum SettingsEntry: ItemListNodeEntry {
|
||||
case userInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, CachedPeerData?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?)
|
||||
case userInfoNotice(PresentationTheme, String)
|
||||
@ -177,8 +190,7 @@ private enum SettingsEntry: ItemListNodeEntry {
|
||||
case let .bioText(theme, currentText, placeholder):
|
||||
return ItemListMultilineInputItem(theme: theme, text: currentText, placeholder: placeholder, maxLength: ItemListMultilineInputItemTextLimit(value: 70, display: true), sectionId: self.section, style: .blocks, textUpdated: { updatedText in
|
||||
arguments.updateBioText(currentText, updatedText)
|
||||
}, action: {
|
||||
|
||||
}, tag: EditSettingsEntryTag.bio, action: {
|
||||
})
|
||||
case let .bioInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
@ -282,7 +294,7 @@ private func editSettingsEntries(presentationData: PresentationData, state: Edit
|
||||
return entries
|
||||
}
|
||||
|
||||
func editSettingsController(context: AccountContext, currentName: ItemListAvatarAndNameInfoItemName, currentBioText: String, accountManager: AccountManager, canAddAccounts: Bool) -> ViewController {
|
||||
func editSettingsController(context: AccountContext, currentName: ItemListAvatarAndNameInfoItemName, currentBioText: String, accountManager: AccountManager, canAddAccounts: Bool, focusOnItemTag: EditSettingsEntryTag? = nil) -> ViewController {
|
||||
let initialState = EditSettingsState(editingName: currentName, editingBioText: currentBioText)
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
@ -399,7 +411,7 @@ func editSettingsController(context: AccountContext, currentName: ItemListAvatar
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.EditProfile_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: editSettingsEntries(presentationData: presentationData, state: state, view: view, canAddAccounts: canAddAccounts), style: .blocks)
|
||||
let listState = ItemListNodeState(entries: editSettingsEntries(presentationData: presentationData, state: state, view: view, canAddAccounts: canAddAccounts), style: .blocks, ensureVisibleItemTag: focusOnItemTag)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
|
||||
@ -71,6 +71,9 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
private let resourceReference: MediaResourceReference
|
||||
private let tempFilePath: String?
|
||||
private let streamable: Bool
|
||||
private let stallDuration: Double
|
||||
private let lowWaterDuration: Double
|
||||
private let highWaterDuration: Double
|
||||
private let video: Bool
|
||||
private let preferSoftwareDecoding: Bool
|
||||
private let fetchAutomatically: Bool
|
||||
@ -95,7 +98,7 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
}
|
||||
}
|
||||
|
||||
init(queue: Queue, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool, maximumFetchSize: Int? = nil) {
|
||||
init(queue: Queue, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool, maximumFetchSize: Int? = nil, stallDuration: Double = 1.0, lowWaterDuration: Double = 2.0, highWaterDuration: Double = 3.0) {
|
||||
self.queue = queue
|
||||
self.postbox = postbox
|
||||
self.resourceReference = resourceReference
|
||||
@ -105,6 +108,9 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
self.preferSoftwareDecoding = preferSoftwareDecoding
|
||||
self.fetchAutomatically = fetchAutomatically
|
||||
self.maximumFetchSize = maximumFetchSize
|
||||
self.stallDuration = stallDuration
|
||||
self.lowWaterDuration = lowWaterDuration
|
||||
self.highWaterDuration = highWaterDuration
|
||||
|
||||
self.taskQueue = ThreadTaskQueue()
|
||||
|
||||
@ -252,12 +258,12 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
var videoBuffer: MediaTrackFrameBuffer?
|
||||
|
||||
if let audio = streamDescriptions.audio {
|
||||
audioBuffer = MediaTrackFrameBuffer(frameSource: strongSelf, decoder: audio.decoder, type: .audio, duration: audio.duration, rotationAngle: 0.0, aspect: 1.0)
|
||||
audioBuffer = MediaTrackFrameBuffer(frameSource: strongSelf, decoder: audio.decoder, type: .audio, duration: audio.duration, rotationAngle: 0.0, aspect: 1.0, stallDuration: strongSelf.stallDuration, lowWaterDuration: strongSelf.lowWaterDuration, highWaterDuration: strongSelf.highWaterDuration)
|
||||
}
|
||||
|
||||
var extraDecodedVideoFrames: [MediaTrackFrame] = []
|
||||
if let video = streamDescriptions.video {
|
||||
videoBuffer = MediaTrackFrameBuffer(frameSource: strongSelf, decoder: video.decoder, type: .video, duration: video.duration, rotationAngle: video.rotationAngle, aspect: video.aspect)
|
||||
videoBuffer = MediaTrackFrameBuffer(frameSource: strongSelf, decoder: video.decoder, type: .video, duration: video.duration, rotationAngle: video.rotationAngle, aspect: video.aspect, stallDuration: strongSelf.stallDuration, lowWaterDuration: strongSelf.lowWaterDuration, highWaterDuration: strongSelf.highWaterDuration)
|
||||
for videoFrame in streamDescriptions.extraVideoFrames {
|
||||
if let decodedFrame = video.decoder.decode(frame: videoFrame) {
|
||||
extraDecodedVideoFrames.append(decodedFrame)
|
||||
|
||||
@ -5,7 +5,6 @@ import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
|
||||
|
||||
class ForwardPrivacyChatPreviewItem: ListViewItem, ItemListItem {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
@ -15,10 +14,11 @@ class ForwardPrivacyChatPreviewItem: ListViewItem, ItemListItem {
|
||||
let wallpaper: TelegramWallpaper
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let peerName: String
|
||||
let linkEnabled: Bool
|
||||
let tooltipText: String
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, linkEnabled: Bool, tooltipText: String) {
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, peerName: String, linkEnabled: Bool, tooltipText: String) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
@ -27,6 +27,7 @@ class ForwardPrivacyChatPreviewItem: ListViewItem, ItemListItem {
|
||||
self.wallpaper = wallpaper
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.peerName = peerName
|
||||
self.linkEnabled = linkEnabled
|
||||
self.tooltipText = tooltipText
|
||||
}
|
||||
@ -140,9 +141,9 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
let messages = SimpleDictionary<MessageId, Message>()
|
||||
|
||||
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: item.strings.Privacy_Forwards_PreviewForwardAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: item.peerName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
|
||||
let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.strings.Privacy_Forwards_PreviewForwardAuthor)
|
||||
let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName)
|
||||
|
||||
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.theme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat, nameDisplayOrder: item.nameDisplayOrder, disableAnimations: false)
|
||||
|
||||
@ -186,7 +187,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
fromString = String(from)
|
||||
}
|
||||
}
|
||||
let authorString = item.strings.Privacy_Forwards_PreviewForwardAuthor
|
||||
let authorString = item.peerName
|
||||
|
||||
if let fromString = fromString {
|
||||
var attributedMeasureText = NSAttributedString(string: fromString, font: Font.regular(13.0), textColor: .black)
|
||||
|
||||
@ -141,10 +141,10 @@ func galleryItemForEntry(context: AccountContext, presentationData: Presentation
|
||||
if file.isVideo {
|
||||
let content: UniversalVideoContent
|
||||
if file.isAnimated {
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: false, loopVideo: true, enableSound: false, tempFilePath: tempFilePath)
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), loopVideo: true, enableSound: false, tempFilePath: tempFilePath)
|
||||
} else {
|
||||
if true || (file.mimeType == "video/mpeg4" || file.mimeType == "video/mov" || file.mimeType == "video/mp4") {
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, loopVideo: loopVideos, tempFilePath: tempFilePath)
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath)
|
||||
} else {
|
||||
content = PlatformVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), streamVideo: streamVideos, loopVideo: loopVideos)
|
||||
}
|
||||
@ -180,11 +180,11 @@ func galleryItemForEntry(context: AccountContext, presentationData: Presentation
|
||||
var content: UniversalVideoContent?
|
||||
switch websiteType(of: webpageContent) {
|
||||
case .instagram where webpageContent.file != nil && webpageContent.image != nil && webpageContent.file!.isVideo:
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, webpageContent.file?.id ?? webpage.webpageId), fileReference: .message(message: MessageReference(message), media: webpageContent.file!), imageReference: webpageContent.image.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, enableSound: true)
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, webpageContent.file?.id ?? webpage.webpageId), fileReference: .message(message: MessageReference(message), media: webpageContent.file!), imageReference: webpageContent.image.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, enableSound: true)
|
||||
default:
|
||||
if let embedUrl = webpageContent.embedUrl, let image = webpageContent.image {
|
||||
if let file = webpageContent.file, file.isVideo {
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, loopVideo: loopVideos, tempFilePath: tempFilePath)
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath)
|
||||
} else if URL(string: embedUrl)?.pathExtension == "mp4" {
|
||||
content = SystemVideoContent(url: embedUrl, imageReference: .webPage(webPage: WebpageReference(webpage), media: image), dimensions: webpageContent.embedSize ?? CGSize(width: 640.0, height: 640.0), duration: Int32(webpageContent.duration ?? 0))
|
||||
}
|
||||
|
||||
@ -1021,11 +1021,29 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func presentReferenceView(item: InstantPageTextItem) {
|
||||
private func presentReferenceView(item: InstantPageTextItem, referenceAnchor: String) {
|
||||
guard let theme = self.theme, let webPage = self.webPage else {
|
||||
return
|
||||
}
|
||||
let controller = InstantPageReferenceController(context: self.context, theme: theme, webPage: webPage, item: item, openUrl: { [weak self] url in
|
||||
|
||||
var targetAnchor: InstantPageTextAnchorItem?
|
||||
for (name, (line, _)) in item.anchors {
|
||||
if name == referenceAnchor {
|
||||
let anchors = item.lines[line].anchorItems
|
||||
for anchor in anchors {
|
||||
if anchor.name == referenceAnchor {
|
||||
targetAnchor = anchor
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let anchorText = targetAnchor?.anchorText else {
|
||||
return
|
||||
}
|
||||
|
||||
let controller = InstantPageReferenceController(context: self.context, theme: theme, webPage: webPage, anchorText: anchorText, openUrl: { [weak self] url in
|
||||
self?.openUrl(url)
|
||||
}, openUrlIn: { [weak self] url in
|
||||
self?.openUrlIn(url)
|
||||
@ -1043,7 +1061,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if !anchor.isEmpty {
|
||||
if let (item, lineOffset, reference, detailsItems) = findAnchorItem(String(anchor), items: items) {
|
||||
if let item = item as? InstantPageTextItem, reference {
|
||||
self.presentReferenceView(item: item)
|
||||
self.presentReferenceView(item: item, referenceAnchor: anchor)
|
||||
} else {
|
||||
var previousDetailsNode: InstantPageDetailsNode?
|
||||
var containerOffset: CGFloat = 0.0
|
||||
|
||||
@ -28,7 +28,7 @@ struct InstantPageGalleryEntry: Equatable {
|
||||
return lhs.index == rhs.index && lhs.pageId == rhs.pageId && lhs.media == rhs.media && lhs.caption == rhs.caption && lhs.credit == rhs.credit && lhs.location == rhs.location
|
||||
}
|
||||
|
||||
func item(context: AccountContext, webPage: TelegramMediaWebpage, message: Message?, presentationData: PresentationData, fromPlayingVideo: Bool, openUrl: @escaping (InstantPageUrlItem) -> Void, openUrlOptions: @escaping (InstantPageUrlItem) -> Void) -> GalleryItem {
|
||||
func item(context: AccountContext, webPage: TelegramMediaWebpage, message: Message?, presentationData: PresentationData, fromPlayingVideo: Bool, landscape: Bool, openUrl: @escaping (InstantPageUrlItem) -> Void, openUrlOptions: @escaping (InstantPageUrlItem) -> Void) -> GalleryItem {
|
||||
let caption: NSAttributedString
|
||||
let credit: NSAttributedString
|
||||
|
||||
@ -91,10 +91,10 @@ struct InstantPageGalleryEntry: Equatable {
|
||||
nativeId = .instantPage(self.pageId, file.fileId)
|
||||
}
|
||||
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: nativeId, fileReference: .webPage(webPage: WebpageReference(webPage), media: file), streamVideo: isMediaStreamable(media: file)), originData: nil, indexData: indexData, contentInfo: .webPage(webPage, file), caption: caption, credit: credit, fromPlayingVideo: fromPlayingVideo, performAction: { _ in }, openActionOptions: { _ in })
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: nativeId, fileReference: .webPage(webPage: WebpageReference(webPage), media: file), streamVideo: isMediaStreamable(media: file) ? .conservative : .none), originData: nil, indexData: indexData, contentInfo: .webPage(webPage, file), caption: caption, credit: credit, fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _ in })
|
||||
} else if let embedWebpage = self.media.media as? TelegramMediaWebpage, case let .Loaded(webpageContent) = embedWebpage.content {
|
||||
if let content = WebEmbedVideoContent(webPage: embedWebpage, webpageContent: webpageContent) {
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: .webPage(webPage, embedWebpage), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, performAction: { _ in }, openActionOptions: { _ in })
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: .webPage(webPage, embedWebpage), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _ in })
|
||||
} else {
|
||||
preconditionFailure()
|
||||
}
|
||||
@ -133,6 +133,7 @@ class InstantPageGalleryController: ViewController {
|
||||
private var entries: [InstantPageGalleryEntry] = []
|
||||
private var centralEntryIndex: Int?
|
||||
private let fromPlayingVideo: Bool
|
||||
private let landscape: Bool
|
||||
|
||||
private let centralItemTitle = Promise<String>()
|
||||
private let centralItemTitleView = Promise<UIView?>()
|
||||
@ -152,11 +153,12 @@ class InstantPageGalleryController: ViewController {
|
||||
private var innerOpenUrl: (InstantPageUrlItem) -> Void
|
||||
private var openUrlOptions: (InstantPageUrlItem) -> Void
|
||||
|
||||
init(context: AccountContext, webPage: TelegramMediaWebpage, message: Message? = nil, entries: [InstantPageGalleryEntry], centralIndex: Int, fromPlayingVideo: Bool = false, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, baseNavigationController: NavigationController?) {
|
||||
init(context: AccountContext, webPage: TelegramMediaWebpage, message: Message? = nil, entries: [InstantPageGalleryEntry], centralIndex: Int, fromPlayingVideo: Bool = false, landscape: Bool = false, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, baseNavigationController: NavigationController?) {
|
||||
self.context = context
|
||||
self.webPage = webPage
|
||||
self.message = message
|
||||
self.fromPlayingVideo = fromPlayingVideo
|
||||
self.landscape = landscape
|
||||
self.replaceRootController = replaceRootController
|
||||
self.baseNavigationController = baseNavigationController
|
||||
|
||||
@ -186,7 +188,7 @@ class InstantPageGalleryController: ViewController {
|
||||
strongSelf.centralEntryIndex = centralIndex
|
||||
if strongSelf.isViewLoaded {
|
||||
strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({
|
||||
$0.item(context: context, webPage: webPage, message: message, presentationData: strongSelf.presentationData, fromPlayingVideo: fromPlayingVideo, openUrl: strongSelf.innerOpenUrl, openUrlOptions: strongSelf.openUrlOptions)
|
||||
$0.item(context: context, webPage: webPage, message: message, presentationData: strongSelf.presentationData, fromPlayingVideo: fromPlayingVideo, landscape: landscape, openUrl: strongSelf.innerOpenUrl, openUrlOptions: strongSelf.openUrlOptions)
|
||||
}), centralItemIndex: centralIndex, keepFirst: false)
|
||||
|
||||
let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in
|
||||
@ -325,7 +327,7 @@ class InstantPageGalleryController: ViewController {
|
||||
}
|
||||
|
||||
self.galleryNode.pager.replaceItems(self.entries.map({
|
||||
$0.item(context: self.context, webPage: self.webPage, message: self.message, presentationData: self.presentationData, fromPlayingVideo: self.fromPlayingVideo, openUrl: self.innerOpenUrl, openUrlOptions: self.openUrlOptions)
|
||||
$0.item(context: self.context, webPage: self.webPage, message: self.message, presentationData: self.presentationData, fromPlayingVideo: self.fromPlayingVideo, landscape: self.landscape, openUrl: self.innerOpenUrl, openUrlOptions: self.openUrlOptions)
|
||||
}), centralItemIndex: self.centralEntryIndex)
|
||||
|
||||
self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in
|
||||
|
||||
@ -173,7 +173,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
|
||||
let styleStack = InstantPageTextStyleStack()
|
||||
setupStyleStack(styleStack, theme: theme, category: .paragraph, link: false)
|
||||
let backgroundInset: CGFloat = 14.0
|
||||
let (_, items, contentSize) = layoutTextItemWithString(attributedStringForRichText(text, styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0 - backgroundInset * 2.0, offset: CGPoint(x: 17.0, y: backgroundInset), media: media, webpage: webpage)
|
||||
let (_, items, contentSize) = layoutTextItemWithString(attributedStringForRichText(text, styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0 - backgroundInset * 2.0, offset: CGPoint(x: 17.0, y: backgroundInset), media: media, webpage: webpage, opaqueBackground: true)
|
||||
let backgroundItem = InstantPageShapeItem(frame: CGRect(origin: CGPoint(), size: CGSize(width: boundingWidth, height: contentSize.height + backgroundInset * 2.0)), shapeFrame: CGRect(origin: CGPoint(), size: CGSize(width: boundingWidth, height: contentSize.height + backgroundInset * 2.0)), shape: .rect, color: theme.codeBlockBackgroundColor)
|
||||
var allItems: [InstantPageItem] = [backgroundItem]
|
||||
allItems.append(contentsOf: items)
|
||||
@ -751,7 +751,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
|
||||
setupStyleStack(styleStack, theme: theme, category: .paragraph, link: false)
|
||||
styleStack.push(.bold)
|
||||
let backgroundInset: CGFloat = 14.0
|
||||
let (_, textItems, textContentSize) = layoutTextItemWithString(attributedStringForRichText(title, styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0 - backgroundInset * 2.0, offset: CGPoint(x: horizontalInset, y: backgroundInset), media: media, webpage: webpage)
|
||||
let (_, textItems, textContentSize) = layoutTextItemWithString(attributedStringForRichText(title, styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0 - backgroundInset * 2.0, offset: CGPoint(x: horizontalInset, y: backgroundInset), media: media, webpage: webpage, opaqueBackground: true)
|
||||
let backgroundItem = InstantPageShapeItem(frame: CGRect(origin: CGPoint(), size: CGSize(width: boundingWidth, height: textContentSize.height + backgroundInset * 2.0)), shapeFrame: CGRect(origin: CGPoint(), size: CGSize(width: boundingWidth, height: textContentSize.height + backgroundInset * 2.0)), shape: .rect, color: theme.panelBackgroundColor)
|
||||
items.append(backgroundItem)
|
||||
items.append(contentsOf: textItems)
|
||||
|
||||
@ -45,7 +45,7 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
|
||||
streamVideo = isMediaStreamable(media: file)
|
||||
}
|
||||
|
||||
self.videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, streamVideo: streamVideo, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true)
|
||||
self.videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, streamVideo: streamVideo ? .earlierStart : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true)
|
||||
self.videoNode.isUserInteractionEnabled = false
|
||||
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
||||
|
||||
@ -15,16 +15,16 @@ final class InstantPageReferenceController: ViewController {
|
||||
private let context: AccountContext
|
||||
private let theme: InstantPageTheme
|
||||
private let webPage: TelegramMediaWebpage
|
||||
private let item: InstantPageTextItem
|
||||
private let anchorText: NSAttributedString
|
||||
private let openUrl: (InstantPageUrlItem) -> Void
|
||||
private let openUrlIn: (InstantPageUrlItem) -> Void
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
|
||||
init(context: AccountContext, theme: InstantPageTheme, webPage: TelegramMediaWebpage, item: InstantPageTextItem, openUrl: @escaping (InstantPageUrlItem) -> Void, openUrlIn: @escaping (InstantPageUrlItem) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
init(context: AccountContext, theme: InstantPageTheme, webPage: TelegramMediaWebpage, anchorText: NSAttributedString, openUrl: @escaping (InstantPageUrlItem) -> Void, openUrlIn: @escaping (InstantPageUrlItem) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.webPage = webPage
|
||||
self.item = item
|
||||
self.anchorText = anchorText
|
||||
self.openUrl = openUrl
|
||||
self.openUrlIn = openUrlIn
|
||||
self.present = present
|
||||
@ -39,7 +39,7 @@ final class InstantPageReferenceController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = InstantPageReferenceControllerNode(context: self.context, theme: self.theme, webPage: self.webPage, item: self.item, openUrl: self.openUrl, openUrlIn: self.openUrlIn, present: self.present)
|
||||
self.displayNode = InstantPageReferenceControllerNode(context: self.context, theme: self.theme, webPage: self.webPage, anchorText: self.anchorText, openUrl: self.openUrl, openUrlIn: self.openUrlIn, present: self.present)
|
||||
self.controllerNode.dismiss = { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ class InstantPageReferenceControllerNode: ViewControllerTracingNode, UIScrollVie
|
||||
private let theme: InstantPageTheme
|
||||
private var presentationData: PresentationData
|
||||
private let webPage: TelegramMediaWebpage
|
||||
private let item: InstantPageTextItem
|
||||
private let anchorText: NSAttributedString
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
private let wrappingScrollNode: ASScrollNode
|
||||
@ -32,12 +32,12 @@ class InstantPageReferenceControllerNode: ViewControllerTracingNode, UIScrollVie
|
||||
var dismiss: (() -> Void)?
|
||||
var close: (() -> Void)?
|
||||
|
||||
init(context: AccountContext, theme: InstantPageTheme, webPage: TelegramMediaWebpage, item: InstantPageTextItem, openUrl: @escaping (InstantPageUrlItem) -> Void, openUrlIn: @escaping (InstantPageUrlItem) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
init(context: AccountContext, theme: InstantPageTheme, webPage: TelegramMediaWebpage, anchorText: NSAttributedString, openUrl: @escaping (InstantPageUrlItem) -> Void, openUrlIn: @escaping (InstantPageUrlItem) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.theme = theme
|
||||
self.webPage = webPage
|
||||
self.item = item
|
||||
self.anchorText = anchorText
|
||||
self.openUrl = openUrl
|
||||
self.openUrlIn = openUrlIn
|
||||
self.present = present
|
||||
@ -195,7 +195,7 @@ class InstantPageReferenceControllerNode: ViewControllerTracingNode, UIScrollVie
|
||||
}
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let (_, items, contentSize) = layoutTextItemWithString(self.item.attributedString, boundingWidth: width - sideInset * 2.0, offset: CGPoint(x: sideInset, y: sideInset), media: media, webpage: self.webPage)
|
||||
let (_, items, contentSize) = layoutTextItemWithString(self.anchorText, boundingWidth: width - sideInset * 2.0, offset: CGPoint(x: sideInset, y: sideInset), media: media, webpage: self.webPage)
|
||||
let contentNode = InstantPageContentNode(context: self.context, strings: self.presentationData.strings, theme: self.theme, items: items, contentSize: CGSize(width: width, height: contentSize.height), inOverlayPanel: true, openMedia: { _ in }, longPressMedia: { _ in }, openPeer: { _ in }, openUrl: { _ in })
|
||||
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: titleAreaHeight), size: CGSize(width: width, height: contentSize.height)))
|
||||
self.contentContainerNode.insertSubnode(contentNode, at: 0)
|
||||
|
||||
@ -35,6 +35,7 @@ struct InstantPageTextImageItem {
|
||||
|
||||
struct InstantPageTextAnchorItem {
|
||||
let name: String
|
||||
let anchorText: NSAttributedString?
|
||||
let empty: Bool
|
||||
}
|
||||
|
||||
@ -76,6 +77,7 @@ final class InstantPageTextItem: InstantPageItem {
|
||||
let rtlLineIndices: Set<Int>
|
||||
var frame: CGRect
|
||||
let alignment: NSTextAlignment
|
||||
let opaqueBackground: Bool
|
||||
let medias: [InstantPageMedia] = []
|
||||
let anchors: [String: (Int, Bool)]
|
||||
let wantsNode: Bool = false
|
||||
@ -86,10 +88,11 @@ final class InstantPageTextItem: InstantPageItem {
|
||||
return !self.rtlLineIndices.isEmpty
|
||||
}
|
||||
|
||||
init(frame: CGRect, attributedString: NSAttributedString, alignment: NSTextAlignment, lines: [InstantPageTextLine]) {
|
||||
init(frame: CGRect, attributedString: NSAttributedString, alignment: NSTextAlignment, opaqueBackground: Bool, lines: [InstantPageTextLine]) {
|
||||
self.attributedString = attributedString
|
||||
self.alignment = alignment
|
||||
self.frame = frame
|
||||
self.opaqueBackground = opaqueBackground
|
||||
self.lines = lines
|
||||
var index = 0
|
||||
var rtlLineIndices = Set<Int>()
|
||||
@ -111,7 +114,7 @@ final class InstantPageTextItem: InstantPageItem {
|
||||
context.saveGState()
|
||||
context.textMatrix = CGAffineTransform(scaleX: 1.0, y: -1.0)
|
||||
context.translateBy(x: self.frame.minX, y: self.frame.minY)
|
||||
|
||||
|
||||
let clipRect = context.boundingBoxOfClipPath
|
||||
|
||||
let upperOriginBound = clipRect.minY - 10.0
|
||||
@ -143,7 +146,13 @@ final class InstantPageTextItem: InstantPageItem {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
if self.opaqueBackground {
|
||||
context.setBlendMode(.normal)
|
||||
}
|
||||
CTLineDraw(line.line, context)
|
||||
if self.opaqueBackground {
|
||||
context.setBlendMode(.copy)
|
||||
}
|
||||
|
||||
if !line.strikethroughItems.isEmpty {
|
||||
for item in line.strikethroughItems {
|
||||
@ -494,7 +503,6 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt
|
||||
let descent: CGFloat
|
||||
let width: CGFloat
|
||||
}
|
||||
|
||||
var dimensions = dimensions
|
||||
if let boundingWidth = boundingWidth {
|
||||
dimensions = dimensions.fittedToWidthOrSmaller(boundingWidth)
|
||||
@ -517,7 +525,6 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt
|
||||
let mutableAttributedString = attributedStringForRichText(.plain(" "), styleStack: styleStack, url: url).mutableCopy() as! NSMutableAttributedString
|
||||
mutableAttributedString.addAttributes(attrDictionaryDelegate, range: NSMakeRange(0, mutableAttributedString.length))
|
||||
return mutableAttributedString
|
||||
//return NSAttributedString(string: " ", attributes: attrDictionaryDelegate)
|
||||
case let .anchor(text, name):
|
||||
var empty = false
|
||||
var text = text
|
||||
@ -525,14 +532,15 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt
|
||||
empty = true
|
||||
text = .plain("\u{200b}")
|
||||
}
|
||||
styleStack.push(.anchor(name, empty))
|
||||
let anchorText = !empty ? attributedStringForRichText(text, styleStack: styleStack, url: url) : nil
|
||||
styleStack.push(.anchor(name, anchorText, empty))
|
||||
let result = attributedStringForRichText(text, styleStack: styleStack, url: url)
|
||||
styleStack.pop()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFloat, horizontalInset: CGFloat = 0.0, alignment: NSTextAlignment = .natural, offset: CGPoint, media: [MediaId: Media] = [:], webpage: TelegramMediaWebpage? = nil, minimizeWidth: Bool = false, maxNumberOfLines: Int = 0) -> (InstantPageTextItem?, [InstantPageItem], CGSize) {
|
||||
func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFloat, horizontalInset: CGFloat = 0.0, alignment: NSTextAlignment = .natural, offset: CGPoint, media: [MediaId: Media] = [:], webpage: TelegramMediaWebpage? = nil, minimizeWidth: Bool = false, maxNumberOfLines: Int = 0, opaqueBackground: Bool = false) -> (InstantPageTextItem?, [InstantPageItem], CGSize) {
|
||||
if string.length == 0 {
|
||||
return (nil, [], CGSize())
|
||||
}
|
||||
@ -694,7 +702,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
|
||||
markedItems.append(InstantPageTextMarkedItem(frame: CGRect(x: workingLineOrigin.x + x, y: workingLineOrigin.y + delta, width: abs(upperX - lowerX), height: lineHeight), color: color))
|
||||
}
|
||||
if let item = attributes[NSAttributedStringKey.init(rawValue: InstantPageAnchorAttribute)] as? Dictionary<String, Any>, let name = item["name"] as? String, let empty = item["empty"] as? Bool {
|
||||
anchorItems.append(InstantPageTextAnchorItem(name: name, empty: empty))
|
||||
anchorItems.append(InstantPageTextAnchorItem(name: name, anchorText: item["text"] as? NSAttributedString, empty: empty))
|
||||
}
|
||||
}
|
||||
|
||||
@ -742,7 +750,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
|
||||
requiresScroll = true
|
||||
}
|
||||
|
||||
let textItem = InstantPageTextItem(frame: CGRect(x: 0.0, y: 0.0, width: textWidth, height: height), attributedString: string, alignment: alignment, lines: lines)
|
||||
let textItem = InstantPageTextItem(frame: CGRect(x: 0.0, y: 0.0, width: textWidth, height: height), attributedString: string, alignment: alignment, opaqueBackground: opaqueBackground, lines: lines)
|
||||
if !requiresScroll {
|
||||
textItem.frame = textItem.frame.offsetBy(dx: offset.x, dy: offset.y)
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ enum InstantPageTextStyle {
|
||||
case superscript
|
||||
case markerColor(UIColor)
|
||||
case marker
|
||||
case anchor(String, Bool)
|
||||
case anchor(String, NSAttributedString?, Bool)
|
||||
case linkColor(UIColor)
|
||||
case linkMarkerColor(UIColor)
|
||||
case link(Bool)
|
||||
@ -32,12 +32,12 @@ final class InstantPageTextStyleStack {
|
||||
private var items: [InstantPageTextStyle] = []
|
||||
|
||||
func push(_ item: InstantPageTextStyle) {
|
||||
items.append(item)
|
||||
self.items.append(item)
|
||||
}
|
||||
|
||||
func pop() {
|
||||
if !items.isEmpty {
|
||||
items.removeLast()
|
||||
if !self.items.isEmpty {
|
||||
self.items.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,9 +114,13 @@ final class InstantPageTextStyleStack {
|
||||
if marker == nil {
|
||||
marker = true
|
||||
}
|
||||
case let .anchor(name, empty):
|
||||
case let .anchor(name, anchorText, empty):
|
||||
if anchor == nil {
|
||||
anchor = ["name": name, "empty": empty]
|
||||
if let anchorText = anchorText {
|
||||
anchor = ["name": name, "text": anchorText, "empty": empty]
|
||||
} else {
|
||||
anchor = ["name": name, "empty": empty]
|
||||
}
|
||||
}
|
||||
case let .linkColor(color):
|
||||
if linkColor == nil {
|
||||
|
||||
@ -9,19 +9,8 @@ final class InstantPageTile {
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func getRandomColor() -> UIColor {
|
||||
//Generate between 0 to 1
|
||||
let red:CGFloat = CGFloat(drand48())
|
||||
let green:CGFloat = CGFloat(drand48())
|
||||
let blue:CGFloat = CGFloat(drand48())
|
||||
|
||||
return UIColor(red:red, green: green, blue: blue, alpha: 1.0)
|
||||
}
|
||||
|
||||
func draw(context: CGContext) {
|
||||
context.translateBy(x: -self.frame.minX, y: -self.frame.minY)
|
||||
//context.setFillColor(getRandomColor().cgColor)
|
||||
//context.fill(self.frame)
|
||||
for item in self.items {
|
||||
item.drawInTile(context: context)
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Bool {
|
||||
guard let size = media.size else {
|
||||
return false
|
||||
}
|
||||
if size < 1 * 1024 * 1024 {
|
||||
if size < 256 * 1024 {
|
||||
return false
|
||||
}
|
||||
for attribute in media.attributes {
|
||||
|
||||
@ -480,7 +480,7 @@ class ItemListController<Entry: ItemListNodeEntry>: ViewController {
|
||||
if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments, !self.didPlayPresentationAnimation {
|
||||
self.didPlayPresentationAnimation = true
|
||||
if case .modalSheet = presentationArguments.presentationAnimation {
|
||||
(self.displayNode as! ItemListControllerNode<Entry>).animateIn()
|
||||
(self.displayNode as! ItemListControllerNode<Entry>).animateIn(completion: presentationArguments.completion)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -304,8 +304,10 @@ class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UIScrollV
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
func animateIn(completion: (() -> Void)? = nil) {
|
||||
self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
func animateOut(completion: (() -> Void)? = nil) {
|
||||
|
||||
@ -23,12 +23,6 @@ final class MediaNavigationAccessoryContainerNode: ASDisplayNode, UIGestureRecog
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.addSubnode(self.headerNode)
|
||||
|
||||
self.headerNode.tapAction = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
|
||||
@ -207,6 +207,18 @@ final class MediaNavigationAccessoryHeaderNode: ASDisplayNode {
|
||||
self.actionPauseNode.image = PresentationResourcesRootController.navigationPlayerPauseIcon(self.theme)
|
||||
self.separatorNode.backgroundColor = self.theme.rootController.navigationBar.separatorColor
|
||||
self.scrubbingNode.updateContent(.standard(lineHeight: 2.0, lineCap: .square, scrubberHandle: .none, backgroundColor: .clear, foregroundColor: self.theme.rootController.navigationBar.accentTextColor))
|
||||
|
||||
if let voiceBaseRate = self.voiceBaseRate {
|
||||
switch voiceBaseRate {
|
||||
case .x1:
|
||||
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: [])
|
||||
case .x2:
|
||||
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: [])
|
||||
}
|
||||
}
|
||||
if let (size, leftInset, rightInset) = self.validLayout {
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
|
||||
@ -60,6 +60,29 @@ enum MediaPlayerPlayOnceWithSoundSeek {
|
||||
case automatic
|
||||
}
|
||||
|
||||
enum MediaPlayerStreaming {
|
||||
case none
|
||||
case conservative
|
||||
case earlierStart
|
||||
|
||||
var enabled: Bool {
|
||||
if case .none = self {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
var parameters: (Double, Double, Double) {
|
||||
switch self {
|
||||
case .none, .conservative:
|
||||
return (1.0, 2.0, 3.0)
|
||||
case .earlierStart:
|
||||
return (0.5, 0.5, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class MediaPlayerAudioRendererContext {
|
||||
let renderer: MediaPlayerAudioRenderer
|
||||
var requestedFrames = false
|
||||
@ -76,7 +99,7 @@ private final class MediaPlayerContext {
|
||||
private let postbox: Postbox
|
||||
private let resourceReference: MediaResourceReference
|
||||
private let tempFilePath: String?
|
||||
private let streamable: Bool
|
||||
private let streamable: MediaPlayerStreaming
|
||||
private let video: Bool
|
||||
private let preferSoftwareDecoding: Bool
|
||||
private var enableSound: Bool
|
||||
@ -102,7 +125,7 @@ private final class MediaPlayerContext {
|
||||
|
||||
private var stoppedAtEnd = false
|
||||
|
||||
init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise<MediaPlayerStatus>, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, keepAudioSessionWhilePaused: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool) {
|
||||
init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise<MediaPlayerStatus>, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: MediaPlayerStreaming, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, keepAudioSessionWhilePaused: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool) {
|
||||
assert(queue.isCurrent())
|
||||
|
||||
self.queue = queue
|
||||
@ -135,12 +158,6 @@ private final class MediaPlayerContext {
|
||||
case .paused:
|
||||
if value {
|
||||
strongSelf.play()
|
||||
// if strongSelf.enableSound {
|
||||
// strongSelf.play()
|
||||
// //strongSelf.continuePlayingWithoutSound()
|
||||
// } else {
|
||||
// strongSelf.play()
|
||||
// }
|
||||
}
|
||||
case .playing:
|
||||
if !value {
|
||||
@ -151,11 +168,6 @@ private final class MediaPlayerContext {
|
||||
case .pause:
|
||||
if value {
|
||||
strongSelf.play()
|
||||
// if strongSelf.enableSound {
|
||||
// strongSelf.continuePlayingWithoutSound()
|
||||
// } else {
|
||||
// strongSelf.play()
|
||||
// }
|
||||
}
|
||||
case .play:
|
||||
if !value {
|
||||
@ -280,7 +292,7 @@ private final class MediaPlayerContext {
|
||||
self.playerStatus.set(.single(status))
|
||||
}
|
||||
|
||||
let frameSource = FFMpegMediaFrameSource(queue: self.queue, postbox: self.postbox, resourceReference: self.resourceReference, tempFilePath: self.tempFilePath, streamable: self.streamable, video: self.video, preferSoftwareDecoding: self.preferSoftwareDecoding, fetchAutomatically: self.fetchAutomatically)
|
||||
let frameSource = FFMpegMediaFrameSource(queue: self.queue, postbox: self.postbox, resourceReference: self.resourceReference, tempFilePath: self.tempFilePath, streamable: self.streamable.enabled, video: self.video, preferSoftwareDecoding: self.preferSoftwareDecoding, fetchAutomatically: self.fetchAutomatically, stallDuration: self.streamable.parameters.0, lowWaterDuration: self.streamable.parameters.1, highWaterDuration: self.streamable.parameters.2)
|
||||
let disposable = MetaDisposable()
|
||||
let updatedSeekState: MediaPlayerSeekState?
|
||||
if let loadedDuration = loadedDuration {
|
||||
@ -930,7 +942,7 @@ final class MediaPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String? = nil, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, keepAudioSessionWhilePaused: Bool = true, continuePlayingWithoutSoundOnLostAudioSession: Bool = false) {
|
||||
init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String? = nil, streamable: MediaPlayerStreaming, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, keepAudioSessionWhilePaused: Bool = true, continuePlayingWithoutSoundOnLostAudioSession: Bool = false) {
|
||||
self.queue.async {
|
||||
let context = MediaPlayerContext(queue: self.queue, audioSessionManager: audioSessionManager, playerStatus: self.statusValue, postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, playAutomatically: playAutomatically, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, playAndRecord: playAndRecord, keepAudioSessionWhilePaused: keepAudioSessionWhilePaused, continuePlayingWithoutSoundOnLostAudioSession: continuePlayingWithoutSoundOnLostAudioSession)
|
||||
self.contextRef = Unmanaged.passRetained(context)
|
||||
|
||||
@ -19,9 +19,9 @@ enum MediaTrackFrameResult {
|
||||
private let traceEvents = false
|
||||
|
||||
final class MediaTrackFrameBuffer {
|
||||
private let stallDuration: Double = 1.0
|
||||
private let lowWaterDuration: Double = 2.0
|
||||
private let highWaterDuration: Double = 3.0
|
||||
private let stallDuration: Double
|
||||
private let lowWaterDuration: Double
|
||||
private let highWaterDuration: Double
|
||||
|
||||
private let frameSource: MediaFrameSource
|
||||
private let decoder: MediaTrackFrameDecoder
|
||||
@ -38,13 +38,16 @@ final class MediaTrackFrameBuffer {
|
||||
private var endOfStream = false
|
||||
private var bufferedUntilTime: CMTime?
|
||||
|
||||
init(frameSource: MediaFrameSource, decoder: MediaTrackFrameDecoder, type: MediaTrackFrameType, duration: CMTime, rotationAngle: Double, aspect: Double) {
|
||||
init(frameSource: MediaFrameSource, decoder: MediaTrackFrameDecoder, type: MediaTrackFrameType, duration: CMTime, rotationAngle: Double, aspect: Double, stallDuration: Double = 1.0, lowWaterDuration: Double = 2.0, highWaterDuration: Double = 3.0) {
|
||||
self.frameSource = frameSource
|
||||
self.type = type
|
||||
self.decoder = decoder
|
||||
self.duration = duration
|
||||
self.rotationAngle = rotationAngle
|
||||
self.aspect = aspect
|
||||
self.stallDuration = stallDuration
|
||||
self.lowWaterDuration = lowWaterDuration
|
||||
self.highWaterDuration = highWaterDuration
|
||||
|
||||
self.frameSourceSinkIndex = self.frameSource.addEventSink { [weak self] event in
|
||||
if let strongSelf = self {
|
||||
|
||||
@ -52,7 +52,7 @@ final class NativeVideoContent: UniversalVideoContent {
|
||||
let imageReference: ImageMediaReference?
|
||||
let dimensions: CGSize
|
||||
let duration: Int32
|
||||
let streamVideo: Bool
|
||||
let streamVideo: MediaPlayerStreaming
|
||||
let loopVideo: Bool
|
||||
let enableSound: Bool
|
||||
let baseRate: Double
|
||||
@ -62,7 +62,7 @@ final class NativeVideoContent: UniversalVideoContent {
|
||||
let placeholderColor: UIColor
|
||||
let tempFilePath: String?
|
||||
|
||||
init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: Bool = false, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
|
||||
init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
|
||||
self.id = id
|
||||
self.nativeId = id
|
||||
self.fileReference = fileReference
|
||||
@ -143,7 +143,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
|
||||
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
|
||||
self.postbox = postbox
|
||||
self.fileReference = fileReference
|
||||
self.placeholderColor = placeholderColor
|
||||
|
||||
@ -3,14 +3,22 @@ import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Display
|
||||
|
||||
func openAddContact(context: AccountContext, firstName: String = "", lastName: String = "", phoneNumber: String, label: String = "_$!<Mobile>!$_", present: @escaping (ViewController, Any?) -> Void, completed: @escaping () -> Void = {}) {
|
||||
func openAddContact(context: AccountContext, firstName: String = "", lastName: String = "", phoneNumber: String, label: String = "_$!<Mobile>!$_", present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void = {}) {
|
||||
let _ = (DeviceAccess.authorizationStatus(context: context, subject: .contacts)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { value in
|
||||
switch value {
|
||||
case .allowed:
|
||||
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [])
|
||||
present(deviceContactInfoController(context: context, subject: .create(peer: nil, contactData: contactData, completion: { _, _,_ in }), completed: completed), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
present(deviceContactInfoController(context: context, subject: .create(peer: nil, contactData: contactData, completion: { peer, stableId, contactData in
|
||||
if let peer = peer {
|
||||
if let infoController = peerInfoController(context: context, peer: peer) {
|
||||
pushController(infoController)
|
||||
}
|
||||
} else {
|
||||
pushController(deviceContactInfoController(context: context, subject: .vcard(nil, stableId, contactData)))
|
||||
}
|
||||
}), completed: completed), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
case .notDetermined:
|
||||
DeviceAccess.authorizeAccess(to: .contacts)
|
||||
default:
|
||||
|
||||
@ -93,7 +93,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
}
|
||||
}
|
||||
|
||||
let gallery = InstantPageGalleryController(context: context, webPage: webPage, message: message, entries: instantPageMedia, centralIndex: centralIndex, fromPlayingVideo: fromPlayingVideo, replaceRootController: { [weak navigationController] controller, ready in
|
||||
let gallery = InstantPageGalleryController(context: context, webPage: webPage, message: message, entries: instantPageMedia, centralIndex: centralIndex, fromPlayingVideo: fromPlayingVideo, landscape: landscape, replaceRootController: { [weak navigationController] controller, ready in
|
||||
if let navigationController = navigationController {
|
||||
navigationController.replaceTopController(controller, animated: false, ready: ready)
|
||||
}
|
||||
|
||||
54
TelegramUI/OpenSettings.swift
Normal file
54
TelegramUI/OpenSettings.swift
Normal file
@ -0,0 +1,54 @@
|
||||
import Foundation
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
private let maximumNumberOfAccounts = 3
|
||||
|
||||
func openEditSettings(context: AccountContext, accountsAndPeers: Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError>, focusOnItemTag: EditSettingsEntryTag? = nil, presentController: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void) -> Disposable {
|
||||
let openEditingDisposable = MetaDisposable()
|
||||
var cancelImpl: (() -> Void)?
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
presentController(controller, nil)
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.15, queue: Queue.mainQueue())
|
||||
let progressDisposable = progressSignal.start()
|
||||
|
||||
let peerKey: PostboxViewKey = .peer(peerId: context.account.peerId, components: [])
|
||||
let cachedDataKey: PostboxViewKey = .cachedPeerData(peerId: context.account.peerId)
|
||||
let signal = (combineLatest(accountsAndPeers |> take(1), context.account.postbox.combinedView(keys: [peerKey, cachedDataKey]))
|
||||
|> mapToSignal { accountsAndPeers, view -> Signal<(TelegramUser, CachedUserData, Bool), NoError> in
|
||||
guard let cachedDataView = view.views[cachedDataKey] as? CachedPeerDataView, let cachedData = cachedDataView.cachedPeerData as? CachedUserData else {
|
||||
return .complete()
|
||||
}
|
||||
guard let peerView = view.views[peerKey] as? PeerView, let peer = peerView.peers[context.account.peerId] as? TelegramUser else {
|
||||
return .complete()
|
||||
}
|
||||
return .single((peer, cachedData, accountsAndPeers.1.count + 1 < maximumNumberOfAccounts))
|
||||
}
|
||||
|> take(1))
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
progressDisposable.dispose()
|
||||
}
|
||||
}
|
||||
cancelImpl = {
|
||||
openEditingDisposable.set(nil)
|
||||
}
|
||||
openEditingDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(next: { peer, cachedData, canAddAccounts in
|
||||
pushController(editSettingsController(context: context, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? ""), currentBioText: cachedData.about ?? "", accountManager: context.sharedContext.accountManager, canAddAccounts: canAddAccounts, focusOnItemTag: focusOnItemTag))
|
||||
}))
|
||||
return openEditingDisposable
|
||||
}
|
||||
@ -53,6 +53,9 @@ final class OverlayUniversalVideoNode: OverlayMediaItemNode {
|
||||
}
|
||||
closeImpl = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.videoNode.hasAttachedContext {
|
||||
strongSelf.videoNode.continuePlayingWithoutSound()
|
||||
}
|
||||
strongSelf.layer.animateScale(from: 1.0, to: 0.1, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
self?.dismiss()
|
||||
close()
|
||||
|
||||
@ -56,6 +56,8 @@ final class PaneSearchContainerNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.contentNode)
|
||||
self.addSubnode(self.searchBar)
|
||||
@ -133,7 +135,7 @@ final class PaneSearchContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func animateOut(to placeholder: PaneSearchBarPlaceholderNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
||||
func animateOut(to placeholder: PaneSearchBarPlaceholderNode, animateOutSearchBar: Bool, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
||||
if case let .animated(duration, curve) = transition {
|
||||
if let size = self.validLayout {
|
||||
let placeholderFrame = placeholder.view.convert(placeholder.bounds, to: self.view)
|
||||
@ -144,8 +146,10 @@ final class PaneSearchContainerNode: ASDisplayNode {
|
||||
self.searchBar.transitionOut(to: placeholder, transition: transition, completion: {
|
||||
completion()
|
||||
})
|
||||
transition.updateAlpha(node: self.backgroundNode, alpha: 0.0, completion: { _ in
|
||||
})
|
||||
transition.updateAlpha(node: self.backgroundNode, alpha: 0.0)
|
||||
if animateOutSearchBar {
|
||||
transition.updateAlpha(node: self.searchBar, alpha: 0.0)
|
||||
}
|
||||
self.contentNode.animateOut(transition: transition)
|
||||
self.deactivate()
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -383,7 +383,7 @@ private func privacyAndSecurityControllerEntries(presentationData: PresentationD
|
||||
return entries
|
||||
}
|
||||
|
||||
public func privacyAndSecurityController(context: AccountContext) -> ViewController {
|
||||
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil) -> ViewController {
|
||||
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
|
||||
let updateState: ((PrivacyAndSecurityControllerState) -> PrivacyAndSecurityControllerState) -> Void = { f in
|
||||
@ -403,7 +403,7 @@ public func privacyAndSecurityController(context: AccountContext) -> ViewControl
|
||||
actionsDisposable.add(updateAccountTimeoutDisposable)
|
||||
|
||||
let privacySettingsPromise = Promise<AccountPrivacySettings?>()
|
||||
privacySettingsPromise.set(.single(nil) |> then(requestAccountPrivacySettings(account: context.account) |> map(Optional.init)))
|
||||
privacySettingsPromise.set(.single(initialSettings) |> then(requestAccountPrivacySettings(account: context.account) |> map(Optional.init)))
|
||||
|
||||
let arguments = PrivacyAndSecurityControllerArguments(account: context.account, openBlockedUsers: {
|
||||
pushControllerImpl?(blockedPeersController(context: context))
|
||||
@ -615,6 +615,11 @@ public func privacyAndSecurityController(context: AccountContext) -> ViewControl
|
||||
})
|
||||
|
||||
actionsDisposable.add(managedUpdatedRecentPeers(accountPeerId: context.account.peerId, postbox: context.account.postbox, network: context.account.network).start())
|
||||
|
||||
actionsDisposable.add((privacySettingsPromise.get()
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
updatedSettings?(settings)
|
||||
}))
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get(), privacySettingsPromise.get(), context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.secretChatLinkPreviewsKey()), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]), recentPeers(account: context.account))
|
||||
|> map { presentationData, state, privacySettings, noticeView, sharedData, recentPeers -> (ItemListControllerState, (ItemListNodeState<PrivacyAndSecurityEntry>, PrivacyAndSecurityEntry.ItemGenerationArguments)) in
|
||||
|
||||
@ -143,7 +143,7 @@ public final class RadialStatusNode: ASControlNode {
|
||||
super.init()
|
||||
}
|
||||
|
||||
public func transitionToState(_ state: RadialStatusNodeState, animated: Bool = true, completion: @escaping () -> Void) {
|
||||
public func transitionToState(_ state: RadialStatusNodeState, animated: Bool = true, completion: @escaping () -> Void = {}) {
|
||||
if self.state != state {
|
||||
let fromState = self.state
|
||||
self.state = state
|
||||
|
||||
Binary file not shown.
@ -74,7 +74,7 @@ private func stringForUserCount(_ count: Int, strings: PresentationStrings) -> S
|
||||
|
||||
private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
||||
case forwardsPreviewHeader(PresentationTheme, String)
|
||||
case forwardsPreview(PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Bool, String)
|
||||
case forwardsPreview(PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, String, Bool, String)
|
||||
case settingHeader(PresentationTheme, String)
|
||||
case everybody(PresentationTheme, String, Bool)
|
||||
case contacts(PresentationTheme, String, Bool)
|
||||
@ -164,8 +164,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .forwardsPreview(lhsTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsLinkEnabled, lhsTooltipText):
|
||||
if case let .forwardsPreview(rhsTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsLinkEnabled, rhsTooltipText) = rhs, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsLinkEnabled == rhsLinkEnabled, lhsTooltipText == rhsTooltipText {
|
||||
case let .forwardsPreview(lhsTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsPeerName, lhsLinkEnabled, lhsTooltipText):
|
||||
if case let .forwardsPreview(rhsTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsPeerName, rhsLinkEnabled, rhsTooltipText) = rhs, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsPeerName == rhsPeerName, lhsLinkEnabled == rhsLinkEnabled, lhsTooltipText == rhsTooltipText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -289,8 +289,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case let .forwardsPreviewHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, multiline: true, sectionId: self.section)
|
||||
case let .forwardsPreview(theme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder, linkEnabled, tooltipText):
|
||||
return ForwardPrivacyChatPreviewItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, linkEnabled: linkEnabled, tooltipText: tooltipText)
|
||||
case let .forwardsPreview(theme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder, peerName, linkEnabled, tooltipText):
|
||||
return ForwardPrivacyChatPreviewItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, peerName: peerName, linkEnabled: linkEnabled, tooltipText: tooltipText)
|
||||
case let .settingHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, multiline: true, sectionId: self.section)
|
||||
case let .everybody(theme, text, value):
|
||||
@ -448,7 +448,7 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func selectivePrivacySettingsControllerEntries(presentationData: PresentationData, kind: SelectivePrivacySettingsKind, state: SelectivePrivacySettingsControllerState) -> [SelectivePrivacySettingsEntry] {
|
||||
private func selectivePrivacySettingsControllerEntries(presentationData: PresentationData, kind: SelectivePrivacySettingsKind, state: SelectivePrivacySettingsControllerState, peerName: String) -> [SelectivePrivacySettingsEntry] {
|
||||
var entries: [SelectivePrivacySettingsEntry] = []
|
||||
|
||||
let settingTitle: String
|
||||
@ -498,7 +498,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
|
||||
linkEnabled = false
|
||||
}
|
||||
entries.append(.forwardsPreviewHeader(presentationData.theme, presentationData.strings.Privacy_Forwards_Preview))
|
||||
entries.append(.forwardsPreview(presentationData.theme, presentationData.chatWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, linkEnabled, tootipText))
|
||||
entries.append(.forwardsPreview(presentationData.theme, presentationData.chatWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peerName, linkEnabled, tootipText))
|
||||
}
|
||||
|
||||
entries.append(.settingHeader(presentationData.theme, settingTitle))
|
||||
@ -693,8 +693,12 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
|
||||
}).start()
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get()) |> deliverOnMainQueue
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<SelectivePrivacySettingsEntry>, SelectivePrivacySettingsEntry.ItemGenerationArguments)) in
|
||||
let peerName = context.account.postbox.transaction { transaction -> String in
|
||||
return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.displayTitle ?? ""
|
||||
}
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peerName) |> deliverOnMainQueue
|
||||
|> map { presentationData, state, peerName -> (ItemListControllerState, (ItemListNodeState<SelectivePrivacySettingsEntry>, SelectivePrivacySettingsEntry.ItemGenerationArguments)) in
|
||||
|
||||
let title: String
|
||||
switch kind {
|
||||
@ -710,7 +714,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
|
||||
title = presentationData.strings.Privacy_Forwards
|
||||
}
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state), style: .blocks, animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName), style: .blocks, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
|
||||
@ -556,6 +556,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var dismissInputImpl: (() -> Void)?
|
||||
var setDisplayNavigationBarImpl: ((Bool) -> Void)?
|
||||
var getNavigationControllerImpl: (() -> NavigationController?)?
|
||||
|
||||
@ -588,47 +589,10 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
||||
|
||||
let contextValue = Promise<AccountContext>()
|
||||
|
||||
let networkArguments = context.account.networkArguments
|
||||
let auxiliaryMethods = context.account.auxiliaryMethods
|
||||
let rootPath = rootPathForBasePath(context.sharedContext.applicationBindings.containerPath)
|
||||
|
||||
let sharedContext = context.sharedContext
|
||||
let accountsAndPeersSignal: Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> = context.sharedContext.activeAccounts
|
||||
|> mapToSignal { primary, activeAccounts, _ -> Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> in
|
||||
var accounts: [Signal<(Account, Peer, Int32)?, NoError>] = []
|
||||
func accountWithPeer(_ account: Account) -> Signal<(Account, Peer, Int32)?, NoError> {
|
||||
return combineLatest(account.postbox.peerView(id: account.peerId), renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: account.postbox))
|
||||
|> map { view, totalUnreadCount -> (Peer?, Int32) in
|
||||
return (view.peers[view.peerId], totalUnreadCount.0)
|
||||
}
|
||||
|> distinctUntilChanged { lhs, rhs in
|
||||
return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1
|
||||
}
|
||||
|> map { peer, totalUnreadCount -> (Account, Peer, Int32)? in
|
||||
if let peer = peer {
|
||||
return (account, peer, totalUnreadCount)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
for (_, account, _) in activeAccounts {
|
||||
accounts.append(accountWithPeer(account))
|
||||
}
|
||||
|
||||
return combineLatest(accounts)
|
||||
|> map { accounts -> ((Account, Peer)?, [(Account, Peer, Int32)]) in
|
||||
var primaryRecord: (Account, Peer)?
|
||||
if let first = accounts.filter({ $0?.0.id == primary?.id }).first, let (account, peer, _) = first {
|
||||
primaryRecord = (account, peer)
|
||||
}
|
||||
return (primaryRecord, accounts.filter({ $0?.0.id != primary?.id }).compactMap({ $0 }))
|
||||
}
|
||||
}
|
||||
|
||||
let accountsAndPeers = Promise<((Account, Peer)?, [(Account, Peer, Int32)])>()
|
||||
accountsAndPeers.set(accountsAndPeersSignal)
|
||||
accountsAndPeers.set(activeAccountsAndPeers(context: context))
|
||||
|
||||
let privacySettings = Promise<[PeerId: AccountPrivacySettings]>([:])
|
||||
|
||||
let openFaq: (Promise<ResolvedUrl>) -> Void = { resolvedUrl in
|
||||
let _ = (contextValue.get()
|
||||
@ -717,7 +681,19 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
let _ = (contextValue.get()
|
||||
|> deliverOnMainQueue
|
||||
|> take(1)).start(next: { context in
|
||||
pushControllerImpl?(privacyAndSecurityController(context: context))
|
||||
let _ = (privacySettings.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
pushControllerImpl?(privacyAndSecurityController(context: context, initialSettings: settings[context.account.peerId], updatedSettings: { settings in
|
||||
let _ = ((privacySettings.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { currentPrivacySettings in
|
||||
var updatedPrivacySettings = currentPrivacySettings
|
||||
updatedPrivacySettings[context.account.peerId] = settings
|
||||
privacySettings.set(.single(updatedPrivacySettings))
|
||||
}))
|
||||
}))
|
||||
})
|
||||
})
|
||||
}, openDataAndStorage: {
|
||||
let _ = (contextValue.get()
|
||||
@ -798,48 +774,9 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
let _ = (contextValue.get()
|
||||
|> deliverOnMainQueue
|
||||
|> take(1)).start(next: { context in
|
||||
var cancelImpl: (() -> Void)?
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
presentControllerImpl?(controller, nil)
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
if let presentControllerImpl = presentControllerImpl, let pushControllerImpl = pushControllerImpl {
|
||||
openEditingDisposable.set(openEditSettings(context: context, accountsAndPeers: accountsAndPeers.get(), presentController: presentControllerImpl, pushController: pushControllerImpl))
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.15, queue: Queue.mainQueue())
|
||||
let progressDisposable = progressSignal.start()
|
||||
|
||||
let peerKey: PostboxViewKey = .peer(peerId: context.account.peerId, components: [])
|
||||
let cachedDataKey: PostboxViewKey = .cachedPeerData(peerId: context.account.peerId)
|
||||
let signal = (combineLatest(accountsAndPeers.get() |> take(1), context.account.postbox.combinedView(keys: [peerKey, cachedDataKey]))
|
||||
|> mapToSignal { accountsAndPeers, view -> Signal<(TelegramUser, CachedUserData, Bool), NoError> in
|
||||
guard let cachedDataView = view.views[cachedDataKey] as? CachedPeerDataView, let cachedData = cachedDataView.cachedPeerData as? CachedUserData else {
|
||||
return .complete()
|
||||
}
|
||||
guard let peerView = view.views[peerKey] as? PeerView, let peer = peerView.peers[context.account.peerId] as? TelegramUser else {
|
||||
return .complete()
|
||||
}
|
||||
return .single((peer, cachedData, accountsAndPeers.1.count + 1 < maximumNumberOfAccounts))
|
||||
}
|
||||
|> take(1))
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
progressDisposable.dispose()
|
||||
}
|
||||
}
|
||||
cancelImpl = {
|
||||
openEditingDisposable.set(nil)
|
||||
}
|
||||
openEditingDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(next: { peer, cachedData, canAddAccounts in
|
||||
pushControllerImpl?(editSettingsController(context: context, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? ""), currentBioText: cachedData.about ?? "", accountManager: accountManager, canAddAccounts: canAddAccounts))
|
||||
}))
|
||||
})
|
||||
}, displayCopyContextMenu: {
|
||||
let _ = (contextValue.get()
|
||||
@ -1137,10 +1074,11 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
return state
|
||||
}
|
||||
}, presentController: { v, a in
|
||||
dismissInputImpl?()
|
||||
presentControllerImpl?(v, a)
|
||||
}, pushController: { v in
|
||||
pushControllerImpl?(v)
|
||||
})
|
||||
}, getNavigationController: getNavigationControllerImpl)
|
||||
|
||||
let (hasPassport, hasWatchApp) = hasPassportAndWatch
|
||||
let listState = ItemListNodeState(entries: settingsEntries(account: context.account, presentationData: presentationData, state: state, view: view, proxySettings: proxySettings, notifyExceptions: preferencesAndExceptions.1, notificationsAuthorizationStatus: preferencesAndExceptions.2, notificationsWarningSuppressed: preferencesAndExceptions.3, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedPacks: featuredAndArchived.1, hasPassport: hasPassport, hasWatchApp: hasWatchApp, accountsAndPeers: accountsAndPeers.1, inAppNotificationSettings: inAppNotificationSettings), style: .blocks, searchItem: searchItem, initialScrollToItem: ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up))
|
||||
@ -1243,7 +1181,9 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
}
|
||||
presentControllerImpl = { [weak controller] value, arguments in
|
||||
controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true)
|
||||
|
||||
}
|
||||
dismissInputImpl = { [weak controller] in
|
||||
controller?.view.window?.endEditing(true)
|
||||
}
|
||||
getNavigationControllerImpl = { [weak controller] in
|
||||
return (controller?.navigationController as? NavigationController)
|
||||
@ -1285,7 +1225,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
let _ = (contextValue.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { accountContext in
|
||||
pushControllerImpl?(debugController(sharedContext: sharedContext, context: accountContext))
|
||||
pushControllerImpl?(debugController(sharedContext: accountContext.sharedContext, context: accountContext))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -19,32 +19,32 @@ extension NavigationBarSearchContentNode: ItemListControllerSearchNavigationCont
|
||||
extension SettingsSearchableItemIcon {
|
||||
func image() -> UIImage? {
|
||||
switch self {
|
||||
case .proxy:
|
||||
return PresentationResourcesSettings.proxy
|
||||
case .savedMessages:
|
||||
return PresentationResourcesSettings.savedMessages
|
||||
case .calls:
|
||||
return PresentationResourcesSettings.recentCalls
|
||||
case .stickers:
|
||||
return PresentationResourcesSettings.stickers
|
||||
case .notifications:
|
||||
return PresentationResourcesSettings.notifications
|
||||
case .privacy:
|
||||
return PresentationResourcesSettings.security
|
||||
case .data:
|
||||
return PresentationResourcesSettings.dataAndStorage
|
||||
case .appearance:
|
||||
return PresentationResourcesSettings.appearance
|
||||
case .language:
|
||||
return PresentationResourcesSettings.language
|
||||
case .watch:
|
||||
return PresentationResourcesSettings.watch
|
||||
case .passport:
|
||||
return PresentationResourcesSettings.passport
|
||||
case .support:
|
||||
return PresentationResourcesSettings.support
|
||||
case .faq:
|
||||
return PresentationResourcesSettings.faq
|
||||
case .proxy:
|
||||
return PresentationResourcesSettings.proxy
|
||||
case .savedMessages:
|
||||
return PresentationResourcesSettings.savedMessages
|
||||
case .calls:
|
||||
return PresentationResourcesSettings.recentCalls
|
||||
case .stickers:
|
||||
return PresentationResourcesSettings.stickers
|
||||
case .notifications:
|
||||
return PresentationResourcesSettings.notifications
|
||||
case .privacy:
|
||||
return PresentationResourcesSettings.security
|
||||
case .data:
|
||||
return PresentationResourcesSettings.dataAndStorage
|
||||
case .appearance:
|
||||
return PresentationResourcesSettings.appearance
|
||||
case .language:
|
||||
return PresentationResourcesSettings.language
|
||||
case .watch:
|
||||
return PresentationResourcesSettings.watch
|
||||
case .passport:
|
||||
return PresentationResourcesSettings.passport
|
||||
case .support:
|
||||
return PresentationResourcesSettings.support
|
||||
case .faq:
|
||||
return PresentationResourcesSettings.faq
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,12 +57,13 @@ final class SettingsSearchItem: ItemListControllerSearch {
|
||||
let updateActivated: (Bool) -> Void
|
||||
let presentController: (ViewController, Any?) -> Void
|
||||
let pushController: (ViewController) -> Void
|
||||
let getNavigationController: (() -> NavigationController?)?
|
||||
|
||||
private var updateActivity: ((Bool) -> Void)?
|
||||
private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false)
|
||||
private let activityDisposable = MetaDisposable()
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, placeholder: String, activated: Bool, updateActivated: @escaping (Bool) -> Void, presentController: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void) {
|
||||
init(context: AccountContext, theme: PresentationTheme, placeholder: String, activated: Bool, updateActivated: @escaping (Bool) -> Void, presentController: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, getNavigationController: (() -> NavigationController?)?) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.placeholder = placeholder
|
||||
@ -70,6 +71,7 @@ final class SettingsSearchItem: ItemListControllerSearch {
|
||||
self.updateActivated = updateActivated
|
||||
self.presentController = presentController
|
||||
self.pushController = pushController
|
||||
self.getNavigationController = getNavigationController
|
||||
self.activityDisposable.set((activity.get() |> mapToSignal { value -> Signal<Bool, NoError> in
|
||||
if value {
|
||||
return .single(value) |> delay(0.2, queue: Queue.mainQueue())
|
||||
@ -133,7 +135,7 @@ final class SettingsSearchItem: ItemListControllerSearch {
|
||||
pushController(c)
|
||||
}, presentController: { c, a in
|
||||
presentController(c, a)
|
||||
})
|
||||
}, getNavigationController: self.getNavigationController)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,15 +403,16 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
|
||||
|
||||
let pushController: (ViewController) -> Void
|
||||
let presentController: (ViewController, Any?) -> Void
|
||||
let getNavigationController: (() -> NavigationController?)?
|
||||
|
||||
var cancel: () -> Void
|
||||
|
||||
init(context: AccountContext, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, Any?) -> Void) {
|
||||
init(context: AccountContext, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: (() -> NavigationController?)?) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.pushController = pushController
|
||||
self.presentController = presentController
|
||||
|
||||
self.getNavigationController = getNavigationController
|
||||
self.cancel = cancel
|
||||
|
||||
super.init()
|
||||
@ -417,6 +420,7 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
self.searchDisplayController?.updatePresentationData(presentationData)
|
||||
}
|
||||
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode) {
|
||||
@ -426,13 +430,15 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: SettingsSearchContainerNode(context: self.context, listState: LocalizationListState.defaultSettings, openResult: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
result.present(strongSelf.context, { [weak self] mode, controller in
|
||||
result.present(strongSelf.context, strongSelf.getNavigationController?(), { [weak self] mode, controller in
|
||||
if let strongSelf = self {
|
||||
switch mode {
|
||||
case .push:
|
||||
strongSelf.pushController(controller)
|
||||
case .modal:
|
||||
strongSelf.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
strongSelf.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet, completion: { [weak self] in
|
||||
self?.cancel()
|
||||
}))
|
||||
case .immediate:
|
||||
strongSelf.presentController(controller, nil)
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
private let maximumNumberOfAccounts = 3
|
||||
|
||||
enum SettingsSearchableItemIcon {
|
||||
case proxy
|
||||
case savedMessages
|
||||
@ -21,6 +23,7 @@ enum SettingsSearchableItemIcon {
|
||||
}
|
||||
|
||||
enum SettingsSearchableItemId: Hashable {
|
||||
case profile(Int)
|
||||
case proxy(Int)
|
||||
case savedMessages(Int)
|
||||
case calls(Int)
|
||||
@ -48,7 +51,57 @@ struct SettingsSearchableItem {
|
||||
let alternate: [String]
|
||||
let icon: SettingsSearchableItemIcon
|
||||
let breadcrumbs: [String]
|
||||
let present: (AccountContext, @escaping (SettingsSearchableItemPresentation, ViewController) -> Void) -> Void
|
||||
let present: (AccountContext, NavigationController?, @escaping (SettingsSearchableItemPresentation, ViewController) -> Void) -> Void
|
||||
}
|
||||
|
||||
private func profileSearchableItems(context: AccountContext, canAddAccount: Bool) -> [SettingsSearchableItem] {
|
||||
let icon: SettingsSearchableItemIcon = .calls
|
||||
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
|
||||
let presentProfileSettings: (AccountContext, @escaping (SettingsSearchableItemPresentation, ViewController) -> Void, EditSettingsEntryTag?) -> Void = { context, present, itemTag in
|
||||
let _ = openEditSettings(context: context, accountsAndPeers: activeAccountsAndPeers(context: context), focusOnItemTag: itemTag, presentController: { controller, _ in
|
||||
present(.immediate, controller)
|
||||
}, pushController: { controller in
|
||||
present(.push, controller)
|
||||
})
|
||||
}
|
||||
|
||||
var items: [SettingsSearchableItem] = []
|
||||
items.append(SettingsSearchableItem(id: .profile(0), title: strings.EditProfile_Title, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentProfileSettings(context, present, nil)
|
||||
}))
|
||||
|
||||
items.append(SettingsSearchableItem(id: .profile(1), title: strings.UserInfo_About_Placeholder, alternate: [], icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
|
||||
presentProfileSettings(context, present, .bio)
|
||||
}))
|
||||
items.append(SettingsSearchableItem(id: .profile(2), title: strings.Settings_PhoneNumber, alternate: [], icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
|
||||
let _ = (context.account.postbox.transaction { transaction -> String in
|
||||
return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone ?? ""
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { phoneNumber in
|
||||
present(.push, ChangePhoneNumberIntroController(context: context, phoneNumber: formatPhoneNumber(phoneNumber)))
|
||||
})
|
||||
}))
|
||||
items.append(SettingsSearchableItem(id: .profile(3), title: strings.Settings_Username, alternate: [], icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
|
||||
present(.modal, usernameSetupController(context: context))
|
||||
}))
|
||||
if canAddAccount {
|
||||
items.append(SettingsSearchableItem(id: .profile(4), title: strings.Settings_AddAccount, alternate: [], icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
|
||||
let isTestingEnvironment = context.account.testingEnvironment
|
||||
context.sharedContext.beginNewAuth(testingEnvironment: isTestingEnvironment)
|
||||
}))
|
||||
}
|
||||
items.append(SettingsSearchableItem(id: .profile(5), title: strings.Settings_Logout, alternate: [], icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, navigationController, present in
|
||||
let _ = (context.account.postbox.transaction { transaction -> String in
|
||||
return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone ?? ""
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { phoneNumber in
|
||||
if let navigationController = navigationController {
|
||||
present(.modal, logoutOptionsController(context: context, navigationController: navigationController, canAddAccounts: canAddAccount, phoneNumber: phoneNumber))
|
||||
}
|
||||
})
|
||||
}))
|
||||
return items
|
||||
}
|
||||
|
||||
private func callSearchableItems(context: AccountContext) -> [SettingsSearchableItem] {
|
||||
@ -60,10 +113,10 @@ private func callSearchableItems(context: AccountContext) -> [SettingsSearchable
|
||||
}
|
||||
|
||||
return [
|
||||
SettingsSearchableItem(id: .calls(0), title: strings.CallSettings_RecentCalls, alternate: [], icon: icon, breadcrumbs: [], present: { context, present in
|
||||
SettingsSearchableItem(id: .calls(0), title: strings.CallSettings_RecentCalls, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentCallSettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .calls(1), title: strings.CallSettings_TabIcon, alternate: [], icon: icon, breadcrumbs: [strings.CallSettings_RecentCalls], present: { context, present in
|
||||
SettingsSearchableItem(id: .calls(1), title: strings.CallSettings_TabIcon, alternate: [], icon: icon, breadcrumbs: [strings.CallSettings_RecentCalls], present: { context, _, present in
|
||||
presentCallSettings(context, present)
|
||||
})
|
||||
]
|
||||
@ -79,23 +132,23 @@ private func stickerSearchableItems(context: AccountContext) -> [SettingsSearcha
|
||||
}
|
||||
|
||||
return [
|
||||
SettingsSearchableItem(id: .stickers(0), title: strings.ChatSettings_Stickers, alternate: [], icon: icon, breadcrumbs: [], present: { context, present in
|
||||
SettingsSearchableItem(id: .stickers(0), title: strings.ChatSettings_Stickers, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentStickerSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .stickers(1), title: strings.Stickers_SuggestStickers, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, present in
|
||||
SettingsSearchableItem(id: .stickers(1), title: strings.Stickers_SuggestStickers, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, _, present in
|
||||
presentStickerSettings(context, present, .suggestOptions)
|
||||
}),
|
||||
SettingsSearchableItem(id: .stickers(2), title: strings.StickerPacksSettings_FeaturedPacks, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, present in
|
||||
SettingsSearchableItem(id: .stickers(2), title: strings.StickerPacksSettings_FeaturedPacks, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, _, present in
|
||||
present(.push, featuredStickerPacksController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .stickers(3), title: strings.StickerPacksSettings_ArchivedPacks, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, present in
|
||||
SettingsSearchableItem(id: .stickers(3), title: strings.StickerPacksSettings_ArchivedPacks, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, _, present in
|
||||
present(.push, archivedStickerPacksController(context: context, mode: .stickers, archived: nil, updatedPacks: { _ in
|
||||
}))
|
||||
}),
|
||||
SettingsSearchableItem(id: .stickers(4), title: strings.MaskStickerSettings_Title, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, present in
|
||||
SettingsSearchableItem(id: .stickers(4), title: strings.MaskStickerSettings_Title, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, _, present in
|
||||
present(.push, installedStickerPacksController(context: context, mode: .masks, archivedPacks: nil, updatedPacks: { _ in}))
|
||||
}),
|
||||
SettingsSearchableItem(id: .stickers(5), title: strings.StickerPacksSettings_ArchivedMasks, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers, strings.MaskStickerSettings_Title], present: { context, present in
|
||||
SettingsSearchableItem(id: .stickers(5), title: strings.StickerPacksSettings_ArchivedMasks, alternate: [], icon: icon, breadcrumbs: [strings.ChatSettings_Stickers, strings.MaskStickerSettings_Title], present: { context, _, present in
|
||||
present(.push, archivedStickerPacksController(context: context, mode: .masks, archived: nil, updatedPacks: { _ in
|
||||
}))
|
||||
})
|
||||
@ -111,64 +164,64 @@ private func notificationSearchableItems(context: AccountContext, notifyExceptio
|
||||
}
|
||||
|
||||
return [
|
||||
SettingsSearchableItem(id: .notifications(0), title: strings.Settings_NotificationsAndSounds, alternate: [], icon: icon, breadcrumbs: [], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(0), title: strings.Settings_NotificationsAndSounds, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(1), title: strings.Notifications_MessageNotificationsAlert, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(1), title: strings.Notifications_MessageNotificationsAlert, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .messageAlerts)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(2), title: strings.Notifications_MessageNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(2), title: strings.Notifications_MessageNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .messagePreviews)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(3), title: strings.Notifications_MessageNotificationsSound, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(3), title: strings.Notifications_MessageNotificationsSound, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(4), title: strings.Notifications_GroupNotificationsAlert, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(4), title: strings.Notifications_GroupNotificationsAlert, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .groupAlerts)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(5), title: strings.Notifications_GroupNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(5), title: strings.Notifications_GroupNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .groupPreviews)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(6), title: strings.Notifications_GroupNotificationsSound, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(6), title: strings.Notifications_GroupNotificationsSound, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(7), title: strings.Notifications_ChannelNotificationsAlert, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(7), title: strings.Notifications_ChannelNotificationsAlert, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .channelAlerts)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(8), title: strings.Notifications_ChannelNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(8), title: strings.Notifications_ChannelNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .channelPreviews)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(9), title: strings.Notifications_ChannelNotificationsSound, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(9), title: strings.Notifications_ChannelNotificationsSound, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(10), title: strings.Notifications_InAppNotificationsSounds, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_InAppNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(10), title: strings.Notifications_InAppNotificationsSounds, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_InAppNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .inAppSounds)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(11), title: strings.Notifications_InAppNotificationsVibrate, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_InAppNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(11), title: strings.Notifications_InAppNotificationsVibrate, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_InAppNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .inAppVibrate)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(12), title: strings.Notifications_InAppNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_InAppNotifications.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(12), title: strings.Notifications_InAppNotificationsPreview, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_InAppNotifications.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .inAppPreviews)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(13), title: strings.Notifications_DisplayNamesOnLockScreen, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(13), title: strings.Notifications_DisplayNamesOnLockScreen, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .displayNamesOnLockscreen)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(14), title: strings.Notifications_Badge_IncludeMutedChats, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(14), title: strings.Notifications_Badge_IncludeMutedChats, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .unreadCountStyle)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(15), title: strings.Notifications_Badge_IncludePublicGroups, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(15), title: strings.Notifications_Badge_IncludePublicGroups, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .includePublicGroups)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(16), title: strings.Notifications_Badge_IncludeChannels, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(16), title: strings.Notifications_Badge_IncludeChannels, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .includeChannels)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(17), title: strings.Notifications_Badge_CountUnreadMessages, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(17), title: strings.Notifications_Badge_CountUnreadMessages, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge.capitalized], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .unreadCountCategory)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(18), title: strings.NotificationSettings_ContactJoined, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(18), title: strings.NotificationSettings_ContactJoined, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .joinedNotifications)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(19), title: strings.Notifications_ResetAllNotifications, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds], present: { context, present in
|
||||
SettingsSearchableItem(id: .notifications(19), title: strings.Notifications_ResetAllNotifications, alternate: [], icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .reset)
|
||||
})
|
||||
]
|
||||
@ -240,28 +293,28 @@ private func privacySearchableItems(context: AccountContext) -> [SettingsSearcha
|
||||
}
|
||||
|
||||
return [
|
||||
SettingsSearchableItem(id: .privacy(0), title: strings.Settings_PrivacySettings, alternate: [], icon: icon, breadcrumbs: [], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(0), title: strings.Settings_PrivacySettings, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentPrivacySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(1), title: strings.Settings_BlockedUsers, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(1), title: strings.Settings_BlockedUsers, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
present(.push, blockedPeersController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(2), title: strings.PrivacySettings_LastSeen, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(2), title: strings.PrivacySettings_LastSeen, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
presentSelectivePrivacySettings(context, .presence, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(3), title: strings.Privacy_ProfilePhoto, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(3), title: strings.Privacy_ProfilePhoto, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
presentSelectivePrivacySettings(context, .profilePhoto, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(4), title: strings.Privacy_Forwards, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(4), title: strings.Privacy_Forwards, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
presentSelectivePrivacySettings(context, .forwards, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(5), title: strings.Privacy_Calls, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(5), title: strings.Privacy_Calls, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
presentSelectivePrivacySettings(context, .voiceCalls, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(6), title: strings.Privacy_GroupsAndChannels, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(6), title: strings.Privacy_GroupsAndChannels, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
presentSelectivePrivacySettings(context, .groupInvitations, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(7), title: passcodeTitle, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(7), title: passcodeTitle, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
let _ = passcodeOptionsAccessController(context: context, completion: { animated in
|
||||
let controller = passcodeOptionsController(context: context)
|
||||
if animated {
|
||||
@ -275,35 +328,35 @@ private func privacySearchableItems(context: AccountContext) -> [SettingsSearcha
|
||||
}
|
||||
})
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(8), title: strings.PrivacySettings_TwoStepAuth, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(8), title: strings.PrivacySettings_TwoStepAuth, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
present(.modal, twoStepVerificationUnlockSettingsController(context: context, mode: .access))
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(9), title: strings.PrivacySettings_AuthSessions, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(9), title: strings.PrivacySettings_AuthSessions, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
present(.push, recentSessionsController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(10), title: strings.PrivacySettings_DeleteAccountTitle.capitalized, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(10), title: strings.PrivacySettings_DeleteAccountTitle.capitalized, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
presentPrivacySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(11), title: strings.PrivacySettings_DataSettings, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(11), title: strings.PrivacySettings_DataSettings, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
|
||||
presentDataPrivacySettings(context, present)
|
||||
}),
|
||||
|
||||
SettingsSearchableItem(id: .privacy(12), title: strings.Privacy_ContactsReset, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(12), title: strings.Privacy_ContactsReset, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, _, present in
|
||||
presentDataPrivacySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(13), title: strings.Privacy_ContactsSync, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(13), title: strings.Privacy_ContactsSync, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, _, present in
|
||||
presentDataPrivacySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(14), title: strings.Privacy_TopPeers, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(14), title: strings.Privacy_TopPeers, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, _, present in
|
||||
presentDataPrivacySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(15), title: strings.Privacy_DeleteDrafts, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(15), title: strings.Privacy_DeleteDrafts, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, _, present in
|
||||
presentDataPrivacySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(16), title: strings.Privacy_PaymentsClearInfo, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(16), title: strings.Privacy_PaymentsClearInfo, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings], present: { context, _, present in
|
||||
presentDataPrivacySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .privacy(17), title: strings.Privacy_SecretChatsLinkPreviews, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings, strings.Privacy_SecretChatsTitle], present: { context, present in
|
||||
SettingsSearchableItem(id: .privacy(17), title: strings.Privacy_SecretChatsLinkPreviews, alternate: [], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings, strings.PrivacySettings_DataSettings, strings.Privacy_SecretChatsTitle], present: { context, _, present in
|
||||
presentDataPrivacySettings(context, present)
|
||||
})
|
||||
]
|
||||
@ -318,46 +371,46 @@ private func dataSearchableItems(context: AccountContext) -> [SettingsSearchable
|
||||
}
|
||||
|
||||
return [
|
||||
SettingsSearchableItem(id: .data(0), title: strings.Settings_ChatSettings, alternate: [], icon: icon, breadcrumbs: [], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(0), title: strings.Settings_ChatSettings, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentDataSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(1), title: strings.ChatSettings_Cache, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(1), title: strings.ChatSettings_Cache, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
present(.push, storageUsageController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(2), title: strings.Cache_KeepMedia, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_Cache], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(2), title: strings.Cache_KeepMedia, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_Cache], present: { context, _, present in
|
||||
present(.push, storageUsageController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(3), title: strings.Cache_ClearCache, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_Cache], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(3), title: strings.Cache_ClearCache, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_Cache], present: { context, _, present in
|
||||
present(.push, storageUsageController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(4), title: strings.NetworkUsageSettings_Title, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(4), title: strings.NetworkUsageSettings_Title, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
present(.push, networkUsageStatsController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(5), title: strings.ChatSettings_AutoDownloadUsingCellular, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoDownloadTitle.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(5), title: strings.ChatSettings_AutoDownloadUsingCellular, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoDownloadTitle.capitalized], present: { context, _, present in
|
||||
present(.push, autodownloadMediaConnectionTypeController(context: context, connectionType: .cellular))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(6), title: strings.ChatSettings_AutoDownloadUsingWiFi, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoDownloadTitle.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(6), title: strings.ChatSettings_AutoDownloadUsingWiFi, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoDownloadTitle.capitalized], present: { context, _, present in
|
||||
present(.push, autodownloadMediaConnectionTypeController(context: context, connectionType: .wifi))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(7), title: strings.ChatSettings_AutoDownloadReset, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(7), title: strings.ChatSettings_AutoDownloadReset, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
presentDataSettings(context, present, .automaticDownloadReset)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(8), title: strings.ChatSettings_AutoPlayGifs, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoPlayTitle], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(8), title: strings.ChatSettings_AutoPlayGifs, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoPlayTitle], present: { context, _, present in
|
||||
presentDataSettings(context, present, .autoplayGifs)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(9), title: strings.ChatSettings_AutoPlayVideos, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoPlayTitle], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(9), title: strings.ChatSettings_AutoPlayVideos, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoPlayTitle], present: { context, _, present in
|
||||
presentDataSettings(context, present, .autoplayVideos)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(10), title: strings.CallSettings_UseLessData, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.Settings_CallSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(10), title: strings.CallSettings_UseLessData, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.Settings_CallSettings], present: { context, _, present in
|
||||
present(.push, voiceCallDataSavingController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(11), title: strings.Settings_SaveIncomingPhotos, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(11), title: strings.Settings_SaveIncomingPhotos, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
present(.push, saveIncomingMediaController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(12), title: strings.Settings_SaveEditedPhotos, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(12), title: strings.Settings_SaveEditedPhotos, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
presentDataSettings(context, present, .saveEditedPhotos)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(13), title: strings.ChatSettings_DownloadInBackground, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, present in
|
||||
SettingsSearchableItem(id: .data(13), title: strings.ChatSettings_DownloadInBackground, alternate: [], icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
presentDataSettings(context, present, .downloadInBackground)
|
||||
})
|
||||
]
|
||||
@ -372,13 +425,13 @@ private func proxySearchableItems(context: AccountContext) -> [SettingsSearchabl
|
||||
}
|
||||
|
||||
return [
|
||||
SettingsSearchableItem(id: .proxy(0), title: strings.Settings_Proxy, alternate: [], icon: icon, breadcrumbs: [], present: { context, present in
|
||||
SettingsSearchableItem(id: .proxy(0), title: strings.Settings_Proxy, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentProxySettings(context, present)
|
||||
}),
|
||||
SettingsSearchableItem(id: .proxy(1), title: strings.SocksProxySetup_AddProxy, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, present in
|
||||
SettingsSearchableItem(id: .proxy(1), title: strings.SocksProxySetup_AddProxy, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, _, present in
|
||||
present(.modal, proxyServerSettingsController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .proxy(2), title: strings.SocksProxySetup_UseForCalls, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, present in
|
||||
SettingsSearchableItem(id: .proxy(2), title: strings.SocksProxySetup_UseForCalls, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, _, present in
|
||||
presentProxySettings(context, present)
|
||||
})
|
||||
]
|
||||
@ -393,45 +446,53 @@ private func appearanceSearchableItems(context: AccountContext) -> [SettingsSear
|
||||
}
|
||||
|
||||
return [
|
||||
SettingsSearchableItem(id: .appearance(0), title: strings.Settings_Appearance, alternate: [], icon: icon, breadcrumbs: [], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(0), title: strings.Settings_Appearance, alternate: [], icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentAppearanceSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .appearance(1), title: strings.Appearance_TextSize.capitalized, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(1), title: strings.Appearance_TextSize.capitalized, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, _, present in
|
||||
presentAppearanceSettings(context, present, .fontSize)
|
||||
}),
|
||||
SettingsSearchableItem(id: .appearance(2), title: strings.Settings_ChatBackground, alternate: ["Wallpaper"], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(2), title: strings.Settings_ChatBackground, alternate: ["Wallpaper"], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, _, present in
|
||||
present(.push, ThemeGridController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .appearance(3), title: strings.Wallpaper_SetColor, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance, strings.Settings_ChatBackground], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(3), title: strings.Wallpaper_SetColor, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance, strings.Settings_ChatBackground], present: { context, _, present in
|
||||
present(.push, ThemeColorsGridController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .appearance(4), title: strings.Wallpaper_SetCustomBackground, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance, strings.Settings_ChatBackground], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(4), title: strings.Wallpaper_SetCustomBackground, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance, strings.Settings_ChatBackground], present: { context, _, present in
|
||||
presentCustomWallpaperPicker(context: context, present: { controller in
|
||||
present(.immediate, controller)
|
||||
})
|
||||
}),
|
||||
SettingsSearchableItem(id: .appearance(5), title: strings.Appearance_AutoNightTheme, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(5), title: strings.Appearance_AutoNightTheme, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, _, present in
|
||||
present(.push, themeAutoNightSettingsController(context: context))
|
||||
}),
|
||||
SettingsSearchableItem(id: .appearance(6), title: strings.Appearance_ColorTheme.capitalized, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(6), title: strings.Appearance_ColorTheme.capitalized, alternate: [], icon: icon, breadcrumbs: [strings.Settings_Appearance], present: { context, _, present in
|
||||
presentAppearanceSettings(context, present, .accentColor)
|
||||
}),
|
||||
SettingsSearchableItem(id: .appearance(7), title: strings.Appearance_ReduceMotion, alternate: ["Animations"], icon: icon, breadcrumbs: [strings.Settings_Appearance, strings.Appearance_Animations.capitalized], present: { context, present in
|
||||
SettingsSearchableItem(id: .appearance(7), title: strings.Appearance_ReduceMotion, alternate: ["Animations"], icon: icon, breadcrumbs: [strings.Settings_Appearance, strings.Appearance_Animations.capitalized], present: { context, _, present in
|
||||
presentAppearanceSettings(context, present, .animations)
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableItem], NoError> {
|
||||
let watchAppInstalled = context.watchManager?.watchAppInstalled ?? .single(false)
|
||||
return watchAppInstalled
|
||||
let watchAppInstalled = (context.watchManager?.watchAppInstalled ?? .single(false))
|
||||
|> take(1)
|
||||
|> map { watchAppInstalled in
|
||||
let canAddAccount = activeAccountsAndPeers(context: context)
|
||||
|> take(1)
|
||||
|> map { accountsAndPeers -> Bool in
|
||||
return accountsAndPeers.1.count + 1 < maximumNumberOfAccounts
|
||||
}
|
||||
return combineLatest(watchAppInstalled, canAddAccount)
|
||||
|> map { watchAppInstalled, canAddAccount in
|
||||
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
|
||||
var allItems: [SettingsSearchableItem] = []
|
||||
|
||||
let savedMessages = SettingsSearchableItem(id: .savedMessages(0), title: strings.Settings_SavedMessages, alternate: [], icon: .savedMessages, breadcrumbs: [], present: { context, present in
|
||||
let profileItems = profileSearchableItems(context: context, canAddAccount: canAddAccount)
|
||||
allItems.append(contentsOf: profileItems)
|
||||
|
||||
let savedMessages = SettingsSearchableItem(id: .savedMessages(0), title: strings.Settings_SavedMessages, alternate: [], icon: .savedMessages, breadcrumbs: [], present: { context, _, present in
|
||||
present(.push, ChatController(context: context, chatLocation: .peer(context.account.peerId)))
|
||||
})
|
||||
allItems.append(savedMessages)
|
||||
@ -457,22 +518,29 @@ func settingsSearchableItems(context: AccountContext) -> Signal<[SettingsSearcha
|
||||
let appearanceItems = appearanceSearchableItems(context: context)
|
||||
allItems.append(contentsOf: appearanceItems)
|
||||
|
||||
let language = SettingsSearchableItem(id: .language(0), title: strings.Settings_AppLanguage, alternate: [], icon: .language, breadcrumbs: [], present: { context, present in
|
||||
let language = SettingsSearchableItem(id: .language(0), title: strings.Settings_AppLanguage, alternate: [], icon: .language, breadcrumbs: [], present: { context, _, present in
|
||||
present(.push, LocalizationListController(context: context))
|
||||
})
|
||||
allItems.append(language)
|
||||
|
||||
let passport = SettingsSearchableItem(id: .passport(0), title: strings.Settings_Passport, alternate: [], icon: .passport, breadcrumbs: [], present: { context, present in
|
||||
if watchAppInstalled {
|
||||
let watch = SettingsSearchableItem(id: .watch(0), title: strings.Settings_AppleWatch, alternate: [], icon: .watch, breadcrumbs: [], present: { context, _, present in
|
||||
present(.push, watchSettingsController(context: context))
|
||||
})
|
||||
allItems.append(watch)
|
||||
}
|
||||
|
||||
let passport = SettingsSearchableItem(id: .passport(0), title: strings.Settings_Passport, alternate: [], icon: .passport, breadcrumbs: [], present: { context, _, present in
|
||||
present(.modal, SecureIdAuthController(context: context, mode: .list))
|
||||
})
|
||||
allItems.append(passport)
|
||||
|
||||
let support = SettingsSearchableItem(id: .support(0), title: strings.Settings_Support, alternate: ["Support"], icon: .support, breadcrumbs: [], present: { context, present in
|
||||
let support = SettingsSearchableItem(id: .support(0), title: strings.Settings_Support, alternate: ["Support"], icon: .support, breadcrumbs: [], present: { context, _, present in
|
||||
//return .push(ChatController(context: context, chatLocation: .peer(context.account.peerId)))
|
||||
})
|
||||
allItems.append(support)
|
||||
|
||||
let faq = SettingsSearchableItem(id: .faq(0), title: strings.Settings_FAQ, alternate: [], icon: .faq, breadcrumbs: [], present: { context, present in
|
||||
let faq = SettingsSearchableItem(id: .faq(0), title: strings.Settings_FAQ, alternate: [], icon: .faq, breadcrumbs: [], present: { context, _, present in
|
||||
//return .push(ChatController(context: context, chatLocation: .peer(context.account.peerId)))
|
||||
})
|
||||
allItems.append(faq)
|
||||
@ -535,7 +603,6 @@ private func matchStringTokens(_ tokens: [ValueBoxKey], with other: [ValueBoxKey
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
func searchSettingsItems(items: [SettingsSearchableItem], query: String) -> [SettingsSearchableItem] {
|
||||
let queryTokens = stringTokens(query.lowercased())
|
||||
|
||||
|
||||
@ -466,13 +466,13 @@ final class SharedMediaPlayer {
|
||||
case .voice, .music:
|
||||
switch playbackData.source {
|
||||
case let .telegramFile(fileReference):
|
||||
strongSelf.playbackItem = .audio(MediaPlayer(audioSessionManager: strongSelf.audioSession, postbox: strongSelf.account.postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: playbackData.type == .music, video: false, preferSoftwareDecoding: false, enableSound: true, baseRate: rateValue, fetchAutomatically: true, playAndRecord: controlPlaybackWithProximity))
|
||||
strongSelf.playbackItem = .audio(MediaPlayer(audioSessionManager: strongSelf.audioSession, postbox: strongSelf.account.postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: playbackData.type == .music ? .conservative : .none, video: false, preferSoftwareDecoding: false, enableSound: true, baseRate: rateValue, fetchAutomatically: true, playAndRecord: controlPlaybackWithProximity))
|
||||
}
|
||||
case .instantVideo:
|
||||
if let mediaManager = strongSelf.mediaManager, let item = item as? MessageMediaPlaylistItem {
|
||||
switch playbackData.source {
|
||||
case let .telegramFile(fileReference):
|
||||
let videoNode = OverlayInstantVideoNode(postbox: strongSelf.account.postbox, audioSession: strongSelf.audioSession, manager: mediaManager.universalVideoManager, content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, fileReference.media.fileId), fileReference: fileReference, streamVideo: false, enableSound: false, baseRate: rateValue), close: { [weak mediaManager] in
|
||||
let videoNode = OverlayInstantVideoNode(postbox: strongSelf.account.postbox, audioSession: strongSelf.audioSession, manager: mediaManager.universalVideoManager, content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, fileReference.media.fileId), fileReference: fileReference, enableSound: false, baseRate: rateValue), close: { [weak mediaManager] in
|
||||
mediaManager?.setPlaylist(nil, type: .voice)
|
||||
})
|
||||
strongSelf.playbackItem = .instantVideo(videoNode)
|
||||
|
||||
@ -189,7 +189,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
case let .fontSize(theme, fontSize):
|
||||
return ThemeSettingsFontSizeItem(theme: theme, fontSize: fontSize, sectionId: self.section, updated: { value in
|
||||
arguments.selectFontSize(value)
|
||||
})
|
||||
}, tag: ThemeSettingsEntryTag.fontSize)
|
||||
case let .chatPreviewHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder):
|
||||
@ -201,7 +201,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
case let .accentColor(theme, text, color):
|
||||
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: "", labelStyle: .color(UIColor(rgb: UInt32(bitPattern: color))), sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openAccentColor(color)
|
||||
})
|
||||
}, tag: ThemeSettingsEntryTag.accentColor)
|
||||
case let .autoNightTheme(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openAutoNightTheme()
|
||||
@ -217,7 +217,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
case let .animations(theme, title, value):
|
||||
return ItemListSwitchItem(theme: theme, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.disableAnimations(value)
|
||||
})
|
||||
}, tag: ThemeSettingsEntryTag.animations)
|
||||
case let .animationsInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ private func generateKnobImage() -> UIImage? {
|
||||
})
|
||||
}
|
||||
|
||||
class ThemeSettingsFontSizeItemNode: ListViewItemNode {
|
||||
class ThemeSettingsFontSizeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
@ -76,7 +76,7 @@ class ThemeSettingsFontSizeItemNode: ListViewItemNode {
|
||||
private var item: ThemeSettingsFontSizeItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
|
||||
var tag: Any? {
|
||||
var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
|
||||
@ -302,6 +302,6 @@ final class UniversalVideoContentManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
} |> runOn(Queue.mainQueue())
|
||||
} |> runOn(Queue.mainQueue())
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,7 +418,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
}
|
||||
default:
|
||||
if let content = item.content as? NativeVideoContent, !content.streamVideo {
|
||||
if let content = item.content as? NativeVideoContent, !content.streamVideo.enabled {
|
||||
if !content.enableSound {
|
||||
isPaused = false
|
||||
}
|
||||
@ -557,10 +557,21 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
|
||||
if let videoNode = self.videoNode {
|
||||
if isCentral {
|
||||
var isAnimated = false
|
||||
if let item = self.item, let content = item.content as? NativeVideoContent {
|
||||
isAnimated = content.fileReference.media.isAnimated
|
||||
}
|
||||
|
||||
self.hideStatusNodeUntilCentrality = false
|
||||
if self.shouldAutoplayOnCentrality() {
|
||||
self.initiallyActivated = true
|
||||
videoNode.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop)
|
||||
if videoNode.ownsContentNode {
|
||||
if isAnimated {
|
||||
videoNode.seek(0.0)
|
||||
videoNode.play()
|
||||
}
|
||||
else if self.shouldAutoplayOnCentrality() {
|
||||
self.initiallyActivated = true
|
||||
videoNode.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.dismissOnOrientationChange = false
|
||||
@ -604,7 +615,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
override func activateAsInitial() {
|
||||
if let videoNode = self.videoNode, self.isCentral {
|
||||
self.initiallyActivated = true
|
||||
|
||||
|
||||
var isAnimated = false
|
||||
if let item = self.item, let content = item.content as? NativeVideoContent {
|
||||
isAnimated = content.fileReference.media.isAnimated
|
||||
@ -708,9 +719,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
|
||||
videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25)
|
||||
|
||||
Queue.mainQueue().after(0.001) {
|
||||
videoNode.canAttachContent = true
|
||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
||||
if self.item?.fromPlayingVideo ?? false {
|
||||
Queue.mainQueue().after(0.001) {
|
||||
videoNode.canAttachContent = true
|
||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
||||
}
|
||||
}
|
||||
|
||||
if let pictureInPictureNode = self.pictureInPictureNode {
|
||||
|
||||
@ -33,11 +33,11 @@ struct WebSearchGalleryEntry: Equatable {
|
||||
case let .externalReference(_, _, type, _, _, _, content, thumbnail, _):
|
||||
if let content = content, type == "gif", let thumbnailResource = thumbnail?.resource, let dimensions = content.dimensions {
|
||||
let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
|
||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: fileReference, streamVideo: false, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: fileReference, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, _, file, _):
|
||||
if let file = file {
|
||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: .standalone(media: file), streamVideo: false, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: .standalone(media: file), loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
||||
}
|
||||
}
|
||||
preconditionFailure()
|
||||
|
||||
@ -203,7 +203,7 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
}
|
||||
default:
|
||||
if let content = item.content as? NativeVideoContent, !content.streamVideo {
|
||||
if let content = item.content as? NativeVideoContent, !content.streamVideo.enabled {
|
||||
if !content.enableSound {
|
||||
isPaused = false
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user