mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various iOS 13 improvements
Dark Mode support Share Suggestions support Video playback position storing Default browser selection
This commit is contained in:
parent
221db9bc02
commit
03c7d2d99e
@ -24,6 +24,10 @@
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>IntentsSupported</key>
|
||||
<array>
|
||||
<string>INSendMessageIntent</string>
|
||||
</array>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<string>SUBQUERY (
|
||||
extensionItems,
|
||||
|
@ -257,6 +257,7 @@
|
||||
<string>ddgQuickLink</string>
|
||||
<string>moovit</string>
|
||||
<string>alook</string>
|
||||
<string>dgis</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
|
@ -4906,3 +4906,11 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Conversation.WalletRequiredSetup" = "Set Up";
|
||||
|
||||
"Wallet.CreateInvoice.Title" = "Create Invoice";
|
||||
|
||||
"AutoNightTheme.System" = "System";
|
||||
|
||||
"ChatSettings.OpenLinksIn" = "Open Links in";
|
||||
|
||||
"WebBrowser.Title" = "Web Browser";
|
||||
"WebBrowser.DefaultBrowser" = "DEFAULT WEB BROWSER";
|
||||
"WebBrowser.InAppSafari" = "In-App Safari";
|
||||
|
@ -9,8 +9,9 @@ public final class GalleryControllerActionInteraction {
|
||||
public let openHashtag: (String?, String) -> Void
|
||||
public let openBotCommand: (String) -> Void
|
||||
public let addContact: (String) -> Void
|
||||
public let storeMediaPlaybackState: (MessageId, Double?) -> Void
|
||||
|
||||
public init(openUrl: @escaping (String, Bool) -> Void, openUrlIn: @escaping (String) -> Void, openPeerMention: @escaping (String) -> Void, openPeer: @escaping (PeerId) -> Void, openHashtag: @escaping (String?, String) -> Void, openBotCommand: @escaping (String) -> Void, addContact: @escaping (String) -> Void) {
|
||||
public init(openUrl: @escaping (String, Bool) -> Void, openUrlIn: @escaping (String) -> Void, openPeerMention: @escaping (String) -> Void, openPeer: @escaping (PeerId) -> Void, openHashtag: @escaping (String?, String) -> Void, openBotCommand: @escaping (String) -> Void, addContact: @escaping (String) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void) {
|
||||
self.openUrl = openUrl
|
||||
self.openUrlIn = openUrlIn
|
||||
self.openPeerMention = openPeerMention
|
||||
@ -18,5 +19,6 @@ public final class GalleryControllerActionInteraction {
|
||||
self.openHashtag = openHashtag
|
||||
self.openBotCommand = openBotCommand
|
||||
self.addContact = addContact
|
||||
self.storeMediaPlaybackState = storeMediaPlaybackState
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public func peerAvatarImageData(account: Account, peer: Peer, authorOfMessage: M
|
||||
}
|
||||
}
|
||||
|
||||
public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false) -> Signal<UIImage?, NoError>? {
|
||||
public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), round: Bool = true, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false) -> Signal<UIImage?, NoError>? {
|
||||
if let imageData = peerAvatarImageData(account: account, peer: peer, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) {
|
||||
return imageData
|
||||
|> mapToSignal { data -> Signal<UIImage?, NoError> in
|
||||
@ -77,19 +77,29 @@ public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: Messa
|
||||
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
||||
context.setBlendMode(.copy)
|
||||
context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
context.setBlendMode(.destinationOut)
|
||||
context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
if round {
|
||||
context.setBlendMode(.destinationOut)
|
||||
context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
}
|
||||
} else {
|
||||
if let emptyColor = emptyColor {
|
||||
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
||||
context.setFillColor(emptyColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
if round {
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
} else {
|
||||
context.fill(CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let emptyColor = emptyColor {
|
||||
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
||||
context.setFillColor(emptyColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
if round {
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
} else {
|
||||
context.fill(CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ public func childWindowHostView(parent: UIView) -> WindowHostView {
|
||||
|
||||
let hostView = WindowHostView(containerView: view, eventView: view, isRotating: {
|
||||
return false
|
||||
}, updateSupportedInterfaceOrientations: { orientations in
|
||||
}, systemUserInterfaceStyle: .single(.light), updateSupportedInterfaceOrientations: { orientations in
|
||||
}, updateDeferScreenEdgeGestures: { edges in
|
||||
}, updatePrefersOnScreenNavigationHidden: { value in
|
||||
})
|
||||
|
@ -12,6 +12,21 @@ private let defaultOrientations: UIInterfaceOrientationMask = {
|
||||
}
|
||||
}()
|
||||
|
||||
public enum WindowUserInterfaceStyle {
|
||||
case light
|
||||
case dark
|
||||
|
||||
@available(iOS 12.0, *)
|
||||
fileprivate init(style: UIUserInterfaceStyle) {
|
||||
switch style {
|
||||
case .light, .unspecified:
|
||||
self = .light
|
||||
case .dark:
|
||||
self = .dark
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class PreviewingHostViewDelegate {
|
||||
public let controllerForLocation: (UIView, CGPoint) -> (UIViewController, CGRect)?
|
||||
public let commitController: (UIViewController) -> Void
|
||||
@ -59,6 +74,11 @@ private final class WindowRootViewController: UIViewController, UIViewController
|
||||
var presentController: ((UIViewController, PresentationSurfaceLevel, Bool, (() -> Void)?) -> Void)?
|
||||
var transitionToSize: ((CGSize, Double) -> Void)?
|
||||
|
||||
private var _systemUserInterfaceStyle = ValuePromise<WindowUserInterfaceStyle>(ignoreRepeated: true)
|
||||
var systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError> {
|
||||
return self._systemUserInterfaceStyle.get()
|
||||
}
|
||||
|
||||
var orientations: UIInterfaceOrientationMask = defaultOrientations {
|
||||
didSet {
|
||||
if oldValue != self.orientations {
|
||||
@ -106,6 +126,12 @@ private final class WindowRootViewController: UIViewController, UIViewController
|
||||
return orientations
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
if #available(iOS 12.0, *) {
|
||||
self._systemUserInterfaceStyle.set(WindowUserInterfaceStyle(style: self.traitCollection.userInterfaceStyle))
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
@ -118,6 +144,12 @@ private final class WindowRootViewController: UIViewController, UIViewController
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if #available(iOS 12.0, *) {
|
||||
self._systemUserInterfaceStyle.set(WindowUserInterfaceStyle(style: self.traitCollection.userInterfaceStyle))
|
||||
} else {
|
||||
self._systemUserInterfaceStyle.set(.light)
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
@ -350,7 +382,7 @@ public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView) {
|
||||
|
||||
let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, isRotating: {
|
||||
return window.isRotating()
|
||||
}, updateSupportedInterfaceOrientations: { orientations in
|
||||
}, systemUserInterfaceStyle: rootViewController.systemUserInterfaceStyle, updateSupportedInterfaceOrientations: { orientations in
|
||||
rootViewController.orientations = orientations
|
||||
}, updateDeferScreenEdgeGestures: { edges in
|
||||
rootViewController.gestureEdges = edges
|
||||
|
@ -16,6 +16,8 @@ public enum StatusBarStyle {
|
||||
self = .White
|
||||
case .blackOpaque:
|
||||
self = .Black
|
||||
case .darkContent:
|
||||
self = .Black
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +203,7 @@ public final class WindowHostView {
|
||||
public let containerView: UIView
|
||||
public let eventView: UIView
|
||||
public let isRotating: () -> Bool
|
||||
public let systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError>
|
||||
|
||||
let updateSupportedInterfaceOrientations: (UIInterfaceOrientationMask) -> Void
|
||||
let updateDeferScreenEdgeGestures: (UIRectEdge) -> Void
|
||||
@ -223,10 +224,11 @@ public final class WindowHostView {
|
||||
var forEachController: (((ContainableController) -> Void) -> Void)?
|
||||
var getAccessibilityElements: (() -> [Any]?)?
|
||||
|
||||
init(containerView: UIView, eventView: UIView, isRotating: @escaping () -> Bool, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void, updateDeferScreenEdgeGestures: @escaping (UIRectEdge) -> Void, updatePrefersOnScreenNavigationHidden: @escaping (Bool) -> Void) {
|
||||
init(containerView: UIView, eventView: UIView, isRotating: @escaping () -> Bool, systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError>, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void, updateDeferScreenEdgeGestures: @escaping (UIRectEdge) -> Void, updatePrefersOnScreenNavigationHidden: @escaping (Bool) -> Void) {
|
||||
self.containerView = containerView
|
||||
self.eventView = eventView
|
||||
self.isRotating = isRotating
|
||||
self.systemUserInterfaceStyle = systemUserInterfaceStyle
|
||||
self.updateSupportedInterfaceOrientations = updateSupportedInterfaceOrientations
|
||||
self.updateDeferScreenEdgeGestures = updateDeferScreenEdgeGestures
|
||||
self.updatePrefersOnScreenNavigationHidden = updatePrefersOnScreenNavigationHidden
|
||||
@ -316,6 +318,8 @@ public class Window1 {
|
||||
}
|
||||
}
|
||||
|
||||
public let systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError>
|
||||
|
||||
private var windowPanRecognizer: WindowPanRecognizer?
|
||||
private let keyboardGestureRecognizerDelegate = WindowKeyboardGestureRecognizerDelegate()
|
||||
private var keyboardGestureBeginLocation: CGPoint?
|
||||
@ -330,6 +334,7 @@ public class Window1 {
|
||||
|
||||
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
|
||||
self.hostView = hostView
|
||||
self.systemUserInterfaceStyle = hostView.systemUserInterfaceStyle
|
||||
|
||||
//self.volumeControlStatusBar = VolumeControlStatusBar(frame: CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 100.0, height: 20.0)), shouldBeVisible: statusBarHost?.handleVolumeControl ?? .single(false))
|
||||
//self.volumeControlStatusBarNode = VolumeControlStatusBarNode()
|
||||
|
@ -139,7 +139,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String {
|
||||
return message.text
|
||||
}
|
||||
|
||||
public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }) -> GalleryItem? {
|
||||
public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void = { _, _ in }) -> GalleryItem? {
|
||||
let message = entry.message
|
||||
let location = entry.location
|
||||
if let (media, mediaImage) = mediaForMessage(message: message) {
|
||||
@ -172,7 +172,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
|
||||
}
|
||||
|
||||
let caption = galleryCaptionStringWithAppliedEntities(text, entities: entities)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, hideControls: hideControls, fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, hideControls: hideControls, fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState)
|
||||
} else {
|
||||
if let fileName = file.fileName, (fileName as NSString).pathExtension.lowercased() == "json" {
|
||||
return ChatAnimationGalleryItem(context: context, presentationData: presentationData, message: message, location: location)
|
||||
@ -211,7 +211,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
|
||||
}
|
||||
}
|
||||
if let content = content {
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, performAction: performAction, openActionOptions: openActionOptions)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, timecode: timecode, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -434,7 +434,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
if entry.message.stableId == strongSelf.centralEntryStableId {
|
||||
isCentral = true
|
||||
}
|
||||
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions) {
|
||||
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }) {
|
||||
if isCentral {
|
||||
centralItemIndex = items.count
|
||||
}
|
||||
@ -856,7 +856,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
||||
if entry.message.stableId == self.centralEntryStableId {
|
||||
isCentral = true
|
||||
}
|
||||
if let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: isCentral && self.fromPlayingVideo, landscape: isCentral && self.landscape, timecode: isCentral ? self.timecode : nil, performAction: self.performAction, openActionOptions: self.openActionOptions) {
|
||||
if let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: isCentral && self.fromPlayingVideo, landscape: isCentral && self.landscape, timecode: isCentral ? self.timecode : nil, performAction: self.performAction, openActionOptions: self.openActionOptions, storeMediaPlaybackState: self.actionInteraction?.storeMediaPlaybackState ?? { _, _ in }) {
|
||||
if isCentral {
|
||||
centralItemIndex = items.count
|
||||
}
|
||||
|
@ -33,8 +33,9 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
||||
let playbackCompleted: () -> Void
|
||||
let performAction: (GalleryControllerInteractionTapAction) -> Void
|
||||
let openActionOptions: (GalleryControllerInteractionTapAction) -> Void
|
||||
let storeMediaPlaybackState: (MessageId, Double?) -> Void
|
||||
|
||||
public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void) {
|
||||
public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, hideControls: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.content = content
|
||||
@ -50,6 +51,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
||||
self.playbackCompleted = playbackCompleted
|
||||
self.performAction = performAction
|
||||
self.openActionOptions = openActionOptions
|
||||
self.storeMediaPlaybackState = storeMediaPlaybackState
|
||||
}
|
||||
|
||||
public func node() -> GalleryItemNode {
|
||||
@ -178,6 +180,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
private var item: UniversalVideoGalleryItem?
|
||||
|
||||
private let statusDisposable = MetaDisposable()
|
||||
private let mediaPlaybackStateDisposable = MetaDisposable()
|
||||
|
||||
private let fetchDisposable = MetaDisposable()
|
||||
private var fetchStatus: MediaResourceStatus?
|
||||
@ -304,6 +307,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
deinit {
|
||||
self.statusDisposable.dispose()
|
||||
self.scrubbingFrameDisposable?.dispose()
|
||||
self.mediaPlaybackStateDisposable.dispose()
|
||||
}
|
||||
|
||||
override func ready() -> Signal<Void, NoError> {
|
||||
@ -400,6 +404,21 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
if let contentInfo = item.contentInfo, case let .message(message) = contentInfo {
|
||||
if Namespaces.Message.allScheduled.contains(message.id.namespace) {
|
||||
disablePictureInPicture = true
|
||||
} else {
|
||||
let throttledSignal = videoNode.status
|
||||
|> mapToThrottled { next -> Signal<MediaPlayerStatus?, NoError> in
|
||||
return .single(next) |> then(.complete() |> delay(4.0, queue: Queue.concurrentDefaultQueue()))
|
||||
}
|
||||
|
||||
self.mediaPlaybackStateDisposable.set(throttledSignal.start(next: { status in
|
||||
if let status = status, status.duration > 60.0 * 20.0 {
|
||||
var timestamp: Double?
|
||||
if status.timestamp > 5.0 && status.timestamp < status.duration - 5.0 {
|
||||
timestamp = status.timestamp
|
||||
}
|
||||
item.storeMediaPlaybackState(message.id, timestamp)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
var file: TelegramMediaFile?
|
||||
|
@ -112,7 +112,7 @@ public 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) ? .conservative : .none), originData: nil, indexData: indexData, contentInfo: .webPage(webPage, file), caption: caption, credit: credit, fromPlayingVideo: fromPlayingVideo, landscape: landscape, 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 }, storeMediaPlaybackState: { _, _ in })
|
||||
} else {
|
||||
var representations: [TelegramMediaImageRepresentation] = []
|
||||
representations.append(contentsOf: file.previewRepresentations)
|
||||
@ -124,7 +124,7 @@ public struct InstantPageGalleryEntry: Equatable {
|
||||
}
|
||||
} 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, landscape: landscape, 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 }, storeMediaPlaybackState: { _, _ in })
|
||||
} else {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
@ -285,10 +285,10 @@ func instantPageThemeTypeForSettingsAndTime(themeSettings: PresentationThemeSett
|
||||
|
||||
var fallback = true
|
||||
if let themeSettings = themeSettings {
|
||||
if case .none = themeSettings.automaticThemeSwitchSetting.trigger {
|
||||
if case .explicitNone = themeSettings.automaticThemeSwitchSetting.trigger {
|
||||
} else {
|
||||
fallback = false
|
||||
useDarkTheme = automaticThemeShouldSwitchNow(settings: themeSettings.automaticThemeSwitchSetting, currentTheme: themeSettings.theme)
|
||||
useDarkTheme = automaticThemeShouldSwitchNow(settings: themeSettings.automaticThemeSwitchSetting, systemUserInterfaceStyle: .light)
|
||||
}
|
||||
}
|
||||
if fallback, let time = time {
|
||||
|
@ -191,18 +191,20 @@ public class ItemListMultilineTextItemNode: ListViewItemNode {
|
||||
var boldFont = titleBoldFont
|
||||
var italicFont = titleItalicFont
|
||||
var boldItalicFont = titleBoldItalicFont
|
||||
var alignment = NSTextAlignment.natural
|
||||
if case .monospace = item.font {
|
||||
baseFont = Font.monospace(17.0)
|
||||
linkFont = Font.monospace(17.0)
|
||||
boldFont = Font.semiboldMonospace(17.0)
|
||||
italicFont = Font.italicMonospace(17.0)
|
||||
boldItalicFont = Font.semiboldItalicMonospace(17.0)
|
||||
alignment = .justified
|
||||
}
|
||||
|
||||
let entities = generateTextEntities(item.text, enabledTypes: item.enabledEntityTypes)
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: titleFont)
|
||||
|
||||
let (titleLayout, titleApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (titleLayout, titleApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: alignment, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
|
@ -24,10 +24,12 @@ public enum OpenInAction {
|
||||
}
|
||||
|
||||
public final class OpenInOption {
|
||||
public let identifier: String
|
||||
public let application: OpenInApplication
|
||||
public let action: () -> OpenInAction
|
||||
|
||||
public init(application: OpenInApplication, action: @escaping () -> OpenInAction) {
|
||||
public init(identifier: String, application: OpenInApplication, action: @escaping () -> OpenInAction) {
|
||||
self.!identifier = identifier
|
||||
self.application = application
|
||||
self.action = action
|
||||
}
|
||||
@ -60,11 +62,11 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
var options: [OpenInOption] = []
|
||||
switch item {
|
||||
case let .url(url):
|
||||
options.append(OpenInOption(application: .safari, action: {
|
||||
options.append(OpenInOption(identifier: "safari", application: .safari, action: {
|
||||
return .openUrl(url: url)
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Chrome", identifier: 535886823, scheme: "googlechrome", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "chrome", application: .other(title: "Chrome", identifier: 535886823, scheme: "googlechrome", store: nil), action: {
|
||||
if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) {
|
||||
components.scheme = components.scheme == "https" ? "googlechromes" : "googlechrome"
|
||||
if let url = components.string {
|
||||
@ -74,21 +76,21 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
return .none
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Firefox", identifier: 989804926, scheme: "firefox", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "firefox", application: .other(title: "Firefox", identifier: 989804926, scheme: "firefox", store: nil), action: {
|
||||
if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) {
|
||||
return .openUrl(url: "firefox://open-url?url=\(escapedUrl)")
|
||||
}
|
||||
return .none
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Firefox Focus", identifier: 1055677337, scheme: "firefox-focus", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "firefoxFocus", application: .other(title: "Firefox Focus", identifier: 1055677337, scheme: "firefox-focus", store: nil), action: {
|
||||
if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) {
|
||||
return .openUrl(url: "firefox-focus://open-url?url=\(escapedUrl)")
|
||||
}
|
||||
return .none
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Opera Mini", identifier: 363729560, scheme: "opera-http", store: "es"), action: {
|
||||
options.append(OpenInOption(identifier: "operaMini", application: .other(title: "Opera Mini", identifier: 363729560, scheme: "opera-http", store: "es"), action: {
|
||||
if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) {
|
||||
components.scheme = components.scheme == "https" ? "opera-https" : "opera-http"
|
||||
if let url = components.string {
|
||||
@ -98,7 +100,7 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
return .none
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Opera Touch", identifier: 1411869974, scheme: "touch-http", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "operaTouch", application: .other(title: "Opera Touch", identifier: 1411869974, scheme: "touch-http", store: nil), action: {
|
||||
if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) {
|
||||
components.scheme = components.scheme == "https" ? "touch-https" : "touch-http"
|
||||
if let url = components.string {
|
||||
@ -108,18 +110,18 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
return .none
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Yandex", identifier: 483693909, scheme: "yandexbrowser-open-url", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "yandex", application: .other(title: "Yandex", identifier: 483693909, scheme: "yandexbrowser-open-url", store: nil), action: {
|
||||
if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) {
|
||||
return .openUrl(url: "yandexbrowser-open-url://\(escapedUrl)")
|
||||
}
|
||||
return .none
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "DuckDuckGo", identifier: 663592361, scheme: "ddgQuickLink", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "duckDuckGo", application: .other(title: "DuckDuckGo", identifier: 663592361, scheme: "ddgQuickLink", store: nil), action: {
|
||||
return .openUrl(url: "ddgQuickLink://\(url)")
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Alook Browser", identifier: 1261944766, scheme: "alook", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "alook", application: .other(title: "Alook Browser", identifier: 1261944766, scheme: "alook", store: nil), action: {
|
||||
return .openUrl(url: "alook://\(url)")
|
||||
}))
|
||||
|
||||
@ -129,17 +131,17 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
|
||||
if !withDirections {
|
||||
if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "foursquare" {
|
||||
options.append(OpenInOption(application: .other(title: "Foursquare", identifier: 306934924, scheme: "foursquare", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "foursquare", application: .other(title: "Foursquare", identifier: 306934924, scheme: "foursquare", store: nil), action: {
|
||||
return .openUrl(url: "foursquare://venues/\(venueId)")
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
options.append(OpenInOption(application: .maps, action: {
|
||||
options.append(OpenInOption(identifier: "appleMaps", application: .maps, action: {
|
||||
return .openLocation(latitude: lat, longitude: lon, withDirections: withDirections)
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Google Maps", identifier: 585027354, scheme: "comgooglemaps-x-callback", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "googleMaps", application: .other(title: "Google Maps", identifier: 585027354, scheme: "comgooglemaps-x-callback", store: nil), action: {
|
||||
let coordinates = "\(lat),\(lon)"
|
||||
if withDirections {
|
||||
return .openUrl(url: "comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram")
|
||||
@ -148,7 +150,7 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
}
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Yandex.Maps", identifier: 313877526, scheme: "yandexmaps", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "yandexMaps", application: .other(title: "Yandex.Maps", identifier: 313877526, scheme: "yandexmaps", store: nil), action: {
|
||||
if withDirections {
|
||||
return .openUrl(url: "yandexmaps://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)")
|
||||
} else {
|
||||
@ -156,7 +158,7 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
}
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Uber", identifier: 368677368, scheme: "uber", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "uber", application: .other(title: "Uber", identifier: 368677368, scheme: "uber", store: nil), action: {
|
||||
let dropoffName: String
|
||||
let dropoffAddress: String
|
||||
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
|
||||
@ -172,12 +174,12 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
return .openUrl(url: "uber://?client_id=&action=setPickup&pickup=my_location&dropoff[latitude]=\(lat)&dropoff[longitude]=\(lon)&dropoff[nickname]=\(dropoffName)&dropoff[formatted_address]=\(dropoffAddress)")
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Lyft", identifier: 529379082, scheme: "lyft", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "lyft", application: .other(title: "Lyft", identifier: 529379082, scheme: "lyft", store: nil), action: {
|
||||
return .openUrl(url: "lyft://ridetype?id=lyft&destination[latitude]=\(lat)&destination[longitude]=\(lon)")
|
||||
}))
|
||||
|
||||
if withDirections {
|
||||
options.append(OpenInOption(application: .other(title: "Citymapper", identifier: 469463298, scheme: "citymapper", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "citymapper", application: .other(title: "Citymapper", identifier: 469463298, scheme: "citymapper", store: nil), action: {
|
||||
let endName: String
|
||||
let endAddress: String
|
||||
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
|
||||
@ -192,13 +194,17 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
}
|
||||
return .openUrl(url: "citymapper://directions?endcoord=\(lat),\(lon)&endname=\(endName)&endaddress=\(endAddress)")
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(identifier: "2gis", application: .other(title: "2GIS", identifier: 481627348, scheme: "dgis", store: nil), action: {
|
||||
return .openUrl(url: "dgis://2gis.ru/routeSearch/rsType/car/to/\(lon),\(lat)")
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Yandex.Navi", identifier: 474500851, scheme: "yandexnavi", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "yandexNavi", application: .other(title: "Yandex.Navi", identifier: 474500851, scheme: "yandexnavi", store: nil), action: {
|
||||
return .openUrl(url: "yandexnavi://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)")
|
||||
}))
|
||||
}
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Moovit", identifier: 498477945, scheme: "moovit", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "moovit", application: .other(title: "Moovit", identifier: 498477945, scheme: "moovit", store: nil), action: {
|
||||
if withDirections {
|
||||
let destName: String
|
||||
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
|
||||
@ -213,12 +219,12 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
}))
|
||||
|
||||
if !withDirections {
|
||||
options.append(OpenInOption(application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "hereMaps", application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location", store: nil), action: {
|
||||
return .openUrl(url: "here-location://\(lat),\(lon)")
|
||||
}))
|
||||
}
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Waze", identifier: 323229106, scheme: "waze", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "waze", application: .other(title: "Waze", identifier: 323229106, scheme: "waze", store: nil), action: {
|
||||
let url = "waze://?ll=\(lat),\(lon)"
|
||||
if withDirections {
|
||||
return .openUrl(url: url.appending("&navigate=yes"))
|
||||
|
@ -39,6 +39,7 @@ private enum AutodownloadMediaCategorySection: Int32 {
|
||||
case master
|
||||
case dataUsage
|
||||
case types
|
||||
case lowDataMode
|
||||
}
|
||||
|
||||
private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
||||
@ -50,6 +51,8 @@ private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
||||
case videos(PresentationTheme, String, String, Bool)
|
||||
case files(PresentationTheme, String, String, Bool)
|
||||
case voiceMessagesInfo(PresentationTheme, String)
|
||||
case lowDataMode(PresentationTheme, String, Bool)
|
||||
case lowDataModeInfo(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -59,6 +62,8 @@ private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
||||
return AutodownloadMediaCategorySection.dataUsage.rawValue
|
||||
case .typesHeader, .photos, .videos, .files, .voiceMessagesInfo:
|
||||
return AutodownloadMediaCategorySection.types.rawValue
|
||||
case .lowDataMode, .lowDataModeInfo:
|
||||
return AutodownloadMediaCategorySection.lowDataMode.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,6 +85,10 @@ private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
||||
return 6
|
||||
case .voiceMessagesInfo:
|
||||
return 7
|
||||
case .lowDataMode:
|
||||
return 8
|
||||
case .lowDataModeInfo:
|
||||
return 9
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +142,18 @@ private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .lowDataMode(lhsTheme, lhsText, lhsValue):
|
||||
if case let .lowDataMode(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .lowDataModeInfo(lhsTheme, lhsText):
|
||||
if case let .lowDataModeInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,6 +190,12 @@ private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .voiceMessagesInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .lowDataMode(theme, text, value):
|
||||
return ItemListSwitchItem(theme: theme, title: text, value: value, enableInteractiveChanges: true, enabled: true, sectionId: self.section, style: .blocks, updated: { value in
|
||||
|
||||
})
|
||||
case let .lowDataModeInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,6 +299,11 @@ private func autodownloadMediaConnectionTypeControllerEntries(presentationData:
|
||||
entries.append(.files(presentationData.theme, presentationData.strings.AutoDownloadSettings_Files, stringForAutomaticDownloadPeers(strings: presentationData.strings, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator, peers: file, category: .file), master))
|
||||
entries.append(.voiceMessagesInfo(presentationData.theme, presentationData.strings.AutoDownloadSettings_VoiceMessagesInfo))
|
||||
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
|
||||
entries.append(.lowDataMode(presentationData.theme, "Follow Low Data Mode", true))
|
||||
entries.append(.lowDataModeInfo(presentationData.theme, "The app won't download media automatically if Low Data Mode is active."))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import AccountContext
|
||||
import OpenInExternalAppUI
|
||||
|
||||
private final class DataAndStorageControllerArguments {
|
||||
let openStorageUsage: () -> Void
|
||||
@ -22,8 +23,9 @@ private final class DataAndStorageControllerArguments {
|
||||
let toggleAutoplayGifs: (Bool) -> Void
|
||||
let toggleAutoplayVideos: (Bool) -> Void
|
||||
let toggleDownloadInBackground: (Bool) -> Void
|
||||
let openBrowserSelection: () -> Void
|
||||
|
||||
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, openVoiceUseLessData: @escaping () -> Void, openSaveIncomingPhotos: @escaping () -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void) {
|
||||
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, openVoiceUseLessData: @escaping () -> Void, openSaveIncomingPhotos: @escaping () -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void) {
|
||||
self.openStorageUsage = openStorageUsage
|
||||
self.openNetworkUsage = openNetworkUsage
|
||||
self.openProxy = openProxy
|
||||
@ -35,6 +37,7 @@ private final class DataAndStorageControllerArguments {
|
||||
self.toggleAutoplayGifs = toggleAutoplayGifs
|
||||
self.toggleAutoplayVideos = toggleAutoplayVideos
|
||||
self.toggleDownloadInBackground = toggleDownloadInBackground
|
||||
self.openBrowserSelection = openBrowserSelection
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +81,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
case otherHeader(PresentationTheme, String)
|
||||
case saveIncomingPhotos(PresentationTheme, String)
|
||||
case saveEditedPhotos(PresentationTheme, String, Bool)
|
||||
case openLinksIn(PresentationTheme, String, String)
|
||||
case downloadInBackground(PresentationTheme, String, Bool)
|
||||
case downloadInBackgroundInfo(PresentationTheme, String)
|
||||
case connectionHeader(PresentationTheme, String)
|
||||
@ -93,7 +97,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
return DataAndStorageSection.autoPlay.rawValue
|
||||
case .voiceCallsHeader, .useLessVoiceData:
|
||||
return DataAndStorageSection.voiceCalls.rawValue
|
||||
case .otherHeader, .saveIncomingPhotos, .saveEditedPhotos, .downloadInBackground, .downloadInBackgroundInfo:
|
||||
case .otherHeader, .saveIncomingPhotos, .saveEditedPhotos, .openLinksIn, .downloadInBackground, .downloadInBackgroundInfo:
|
||||
return DataAndStorageSection.other.rawValue
|
||||
case .connectionHeader, .connectionProxy:
|
||||
return DataAndStorageSection.connection.rawValue
|
||||
@ -130,14 +134,16 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
return 12
|
||||
case .saveEditedPhotos:
|
||||
return 13
|
||||
case .downloadInBackground:
|
||||
case .openLinksIn:
|
||||
return 14
|
||||
case .downloadInBackgroundInfo:
|
||||
case .downloadInBackground:
|
||||
return 15
|
||||
case .connectionHeader:
|
||||
case .downloadInBackgroundInfo:
|
||||
return 16
|
||||
case .connectionProxy:
|
||||
case .connectionHeader:
|
||||
return 17
|
||||
case .connectionProxy:
|
||||
return 18
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,6 +233,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .openLinksIn(lhsTheme, lhsText, lhsValue):
|
||||
if case let .openLinksIn(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .downloadInBackground(lhsTheme, lhsText, lhsValue):
|
||||
if case let .downloadInBackground(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
@ -311,6 +323,10 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
||||
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleSaveEditedPhotos(value)
|
||||
}, tag: DataAndStorageEntryTag.saveEditedPhotos)
|
||||
case let .openLinksIn(theme, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openBrowserSelection()
|
||||
})
|
||||
case let .downloadInBackground(theme, text, value):
|
||||
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleDownloadInBackground(value)
|
||||
@ -413,7 +429,7 @@ private func stringForAutoDownloadSetting(strings: PresentationStrings, decimalS
|
||||
}
|
||||
}
|
||||
|
||||
private func dataAndStorageControllerEntries(state: DataAndStorageControllerState, data: DataAndStorageData, presentationData: PresentationData) -> [DataAndStorageEntry] {
|
||||
private func dataAndStorageControllerEntries(state: DataAndStorageControllerState, data: DataAndStorageData, presentationData: PresentationData, defaultWebBrowser: String) -> [DataAndStorageEntry] {
|
||||
var entries: [DataAndStorageEntry] = []
|
||||
|
||||
entries.append(.storageUsage(presentationData.theme, presentationData.strings.ChatSettings_Cache))
|
||||
@ -437,6 +453,9 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
|
||||
entries.append(.otherHeader(presentationData.theme, presentationData.strings.ChatSettings_Other))
|
||||
entries.append(.saveIncomingPhotos(presentationData.theme, presentationData.strings.Settings_SaveIncomingPhotos))
|
||||
entries.append(.saveEditedPhotos(presentationData.theme, presentationData.strings.Settings_SaveEditedPhotos, data.generatedMediaStoreSettings.storeEditedPhotos))
|
||||
|
||||
entries.append(.openLinksIn(presentationData.theme, presentationData.strings.ChatSettings_OpenLinksIn, defaultWebBrowser))
|
||||
|
||||
entries.append(.downloadInBackground(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackground, data.automaticMediaDownloadSettings.downloadInBackground))
|
||||
entries.append(.downloadInBackgroundInfo(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackgroundInfo))
|
||||
|
||||
@ -556,19 +575,30 @@ func dataAndStorageController(context: AccountContext, focusOnItemTag: DataAndSt
|
||||
settings.autoplayVideos = value
|
||||
return settings
|
||||
}).start()
|
||||
}, toggleDownloadInBackground: { value in
|
||||
}, toggleDownloadInBackground: { value in
|
||||
let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
||||
var settings = settings
|
||||
settings.downloadInBackground = value
|
||||
return settings
|
||||
}).start()
|
||||
}, openBrowserSelection: {
|
||||
let controller = webBrowserSettingsController(context: context)
|
||||
pushControllerImpl?(controller)
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), dataAndStorageDataPromise.get()) |> deliverOnMainQueue
|
||||
|> map { presentationData, state, dataAndStorageData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), dataAndStorageDataPromise.get(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings])) |> deliverOnMainQueue
|
||||
|> map { presentationData, state, dataAndStorageData, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let webBrowserSettings = (sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings] as? WebBrowserSettings) ?? WebBrowserSettings.defaultSettings
|
||||
let options = availableOpenInOptions(context: context, item: .url(url: "http://telegram.org"))
|
||||
let defaultWebBrowser: String
|
||||
if let option = options.first(where: { $0.identifier == webBrowserSettings.defaultWebBrowser }) {
|
||||
defaultWebBrowser = option.title
|
||||
} else {
|
||||
defaultWebBrowser = presentationData.strings.WebBrowser_InAppSafari
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.ChatSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: dataAndStorageControllerEntries(state: state, data: dataAndStorageData, presentationData: presentationData), style: .blocks, ensureVisibleItemTag: focusOnItemTag, emptyStateItem: nil, animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: dataAndStorageControllerEntries(state: state, data: dataAndStorageData, presentationData: presentationData, defaultWebBrowser: defaultWebBrowser), style: .blocks, ensureVisibleItemTag: focusOnItemTag, emptyStateItem: nil, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
|
@ -0,0 +1,125 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import AccountContext
|
||||
import OpenInExternalAppUI
|
||||
|
||||
private final class WebBrowserSettingsControllerArguments {
|
||||
let updateDefaultBrowser: (String?) -> Void
|
||||
|
||||
init(updateDefaultBrowser: @escaping (String?) -> Void) {
|
||||
self.updateDefaultBrowser = updateDefaultBrowser
|
||||
}
|
||||
}
|
||||
|
||||
private enum WebBrowserSettingsSection: Int32 {
|
||||
case browsers
|
||||
}
|
||||
|
||||
private enum WebBrowserSettingsControllerEntry: ItemListNodeEntry {
|
||||
case browserHeader(PresentationTheme, String)
|
||||
case browser(PresentationTheme, String, String?, Bool, Int32)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .browserHeader, .browser:
|
||||
return WebBrowserSettingsSection.browsers.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .browserHeader:
|
||||
return 0
|
||||
case let .browser(_, _, _, _, index):
|
||||
return 1 + index
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: WebBrowserSettingsControllerEntry, rhs: WebBrowserSettingsControllerEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .browserHeader(lhsTheme, lhsText):
|
||||
if case let .browserHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .browser(lhsTheme, lhsTitle, lhsIdentifier, lhsSelected, lhsIndex):
|
||||
if case let .browser(rhsTheme, rhsTitle, rhsIdentifier, rhsSelected, rhsIndex) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsIdentifier == rhsIdentifier, lhsSelected == rhsSelected, lhsIndex == rhsIndex {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: WebBrowserSettingsControllerEntry, rhs: WebBrowserSettingsControllerEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(_ arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! WebBrowserSettingsControllerArguments
|
||||
switch self {
|
||||
case let .browserHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .browser(theme, title, identifier, selected, _):
|
||||
return ItemListCheckboxItem(theme: theme, title: title, style: .right, checked: selected, zeroSeparatorInsets: false, sectionId: self.section) {
|
||||
arguments.updateDefaultBrowser(identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func webBrowserSettingsControllerEntries(context: AccountContext, presentationData: PresentationData, selectedBrowser: String?) -> [WebBrowserSettingsControllerEntry] {
|
||||
var entries: [WebBrowserSettingsControllerEntry] = []
|
||||
|
||||
let options = availableOpenInOptions(context: context, item: .url(url: "http://telegram.org"))
|
||||
|
||||
entries.append(.browserHeader(presentationData.theme, presentationData.strings.WebBrowser_DefaultBrowser))
|
||||
entries.append(.browser(presentationData.theme, presentationData.strings.WebBrowser_InAppSafari, nil, selectedBrowser == nil, 0))
|
||||
|
||||
var index: Int32 = 1
|
||||
for option in options {
|
||||
entries.append(.browser(presentationData.theme, option.title, option.identifier, option.identifier == selectedBrowser, index))
|
||||
index += 1
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
public func webBrowserSettingsController(context: AccountContext) -> ViewController {
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let updateDisposable = MetaDisposable()
|
||||
let arguments = WebBrowserSettingsControllerArguments(updateDefaultBrowser: { identifier in
|
||||
let _ = updateWebBrowserSettingsInteractively(accountManager: context.sharedContext.accountManager, { $0.withUpdatedDefaultWebBrowser(identifier) }).start()
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings]))
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings] as? WebBrowserSettings) ?? WebBrowserSettings.defaultSettings
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.WebBrowser_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: webBrowserSettingsControllerEntries(context: context, presentationData: presentationData, selectedBrowser: settings.defaultWebBrowser), style: .blocks, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
presentControllerImpl = { [weak controller] c in
|
||||
controller?.present(c, in: .window(.root))
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import Geocoding
|
||||
import WallpaperResources
|
||||
|
||||
private enum TriggerMode {
|
||||
case system
|
||||
case none
|
||||
case timeBased
|
||||
case brightness
|
||||
@ -51,6 +52,7 @@ private enum ThemeAutoNightSettingsControllerSection: Int32 {
|
||||
}
|
||||
|
||||
private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
||||
case modeSystem(PresentationTheme, String, Bool)
|
||||
case modeDisabled(PresentationTheme, String, Bool)
|
||||
case modeTimeBased(PresentationTheme, String, Bool)
|
||||
case modeBrightness(PresentationTheme, String, Bool)
|
||||
@ -68,7 +70,7 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .modeDisabled, .modeTimeBased, .modeBrightness:
|
||||
case .modeSystem, .modeDisabled, .modeTimeBased, .modeBrightness:
|
||||
return ThemeAutoNightSettingsControllerSection.mode.rawValue
|
||||
case .settingsHeader, .timeBasedAutomaticLocation, .timeBasedAutomaticLocationValue, .timeBasedManualFrom, .timeBasedManualTo, .brightnessValue, .settingInfo:
|
||||
return ThemeAutoNightSettingsControllerSection.settings.rawValue
|
||||
@ -79,35 +81,43 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .modeDisabled:
|
||||
case .modeSystem:
|
||||
return 0
|
||||
case .modeTimeBased:
|
||||
case .modeDisabled:
|
||||
return 1
|
||||
case .modeBrightness:
|
||||
case .modeTimeBased:
|
||||
return 2
|
||||
case .settingsHeader:
|
||||
case .modeBrightness:
|
||||
return 3
|
||||
case .timeBasedAutomaticLocation:
|
||||
case .settingsHeader:
|
||||
return 4
|
||||
case .timeBasedAutomaticLocationValue:
|
||||
case .timeBasedAutomaticLocation:
|
||||
return 5
|
||||
case .timeBasedManualFrom:
|
||||
case .timeBasedAutomaticLocationValue:
|
||||
return 6
|
||||
case .timeBasedManualTo:
|
||||
case .timeBasedManualFrom:
|
||||
return 7
|
||||
case .brightnessValue:
|
||||
case .timeBasedManualTo:
|
||||
return 8
|
||||
case .settingInfo:
|
||||
case .brightnessValue:
|
||||
return 9
|
||||
case .themeHeader:
|
||||
case .settingInfo:
|
||||
return 10
|
||||
case .themeItem:
|
||||
case .themeHeader:
|
||||
return 11
|
||||
case .themeItem:
|
||||
return 12
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: ThemeAutoNightSettingsControllerEntry, rhs: ThemeAutoNightSettingsControllerEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .modeSystem(lhsTheme, lhsTitle, lhsValue):
|
||||
if case let .modeSystem(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .modeDisabled(lhsTheme, lhsTitle, lhsValue):
|
||||
if case let .modeDisabled(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
|
||||
return true
|
||||
@ -190,6 +200,10 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
||||
func item(_ arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! ThemeAutoNightSettingsControllerArguments
|
||||
switch self {
|
||||
case let .modeSystem(theme, title, value):
|
||||
return ItemListCheckboxItem(theme: theme, title: title, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
arguments.updateMode(.system)
|
||||
})
|
||||
case let .modeDisabled(theme, title, value):
|
||||
return ItemListCheckboxItem(theme: theme, title: title, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
arguments.updateMode(.none)
|
||||
@ -241,7 +255,13 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
|
||||
|
||||
let activeTriggerMode: TriggerMode
|
||||
switch switchSetting.trigger {
|
||||
case .none:
|
||||
case .system:
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
|
||||
activeTriggerMode = .system
|
||||
} else {
|
||||
activeTriggerMode = .none
|
||||
}
|
||||
case .explicitNone:
|
||||
activeTriggerMode = .none
|
||||
case .timeBased:
|
||||
activeTriggerMode = .timeBased
|
||||
@ -249,12 +269,15 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
|
||||
activeTriggerMode = .brightness
|
||||
}
|
||||
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
|
||||
entries.append(.modeSystem(theme, strings.AutoNightTheme_System, activeTriggerMode == .system))
|
||||
}
|
||||
entries.append(.modeDisabled(theme, strings.AutoNightTheme_Disabled, activeTriggerMode == .none))
|
||||
entries.append(.modeTimeBased(theme, strings.AutoNightTheme_Scheduled, activeTriggerMode == .timeBased))
|
||||
entries.append(.modeBrightness(theme, strings.AutoNightTheme_Automatic, activeTriggerMode == .brightness))
|
||||
|
||||
switch switchSetting.trigger {
|
||||
case .none:
|
||||
case .system, .explicitNone:
|
||||
break
|
||||
case let .timeBased(setting):
|
||||
entries.append(.settingsHeader(theme, strings.AutoNightTheme_ScheduleSection))
|
||||
@ -284,9 +307,9 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
|
||||
}
|
||||
|
||||
switch switchSetting.trigger {
|
||||
case .none:
|
||||
case .explicitNone:
|
||||
break
|
||||
case .timeBased, .brightness:
|
||||
case .system, .timeBased, .brightness:
|
||||
entries.append(.themeHeader(theme, strings.AutoNightTheme_PreferredTheme))
|
||||
entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors))
|
||||
}
|
||||
@ -303,7 +326,7 @@ private func roundTimeToDay(_ timestamp: Int32) -> Int32 {
|
||||
|
||||
private func areSettingsValid(_ settings: AutomaticThemeSwitchSetting) -> Bool {
|
||||
switch settings.trigger {
|
||||
case .none:
|
||||
case .system, .explicitNone, .brightness:
|
||||
return true
|
||||
case let .timeBased(setting):
|
||||
switch setting {
|
||||
@ -316,8 +339,6 @@ private func areSettingsValid(_ settings: AutomaticThemeSwitchSetting) -> Bool {
|
||||
case .manual:
|
||||
return true
|
||||
}
|
||||
case .brightness:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,8 +405,10 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon
|
||||
updateSettings { settings in
|
||||
var settings = settings
|
||||
switch mode {
|
||||
case .system:
|
||||
settings.trigger = .system
|
||||
case .none:
|
||||
settings.trigger = .none
|
||||
settings.trigger = .explicitNone
|
||||
case .timeBased:
|
||||
if case .timeBased = settings.trigger {
|
||||
} else {
|
||||
|
@ -363,7 +363,13 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
|
||||
|
||||
let title: String
|
||||
switch autoNightSettings.trigger {
|
||||
case .none:
|
||||
case .system:
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
|
||||
title = strings.AutoNightTheme_System
|
||||
} else {
|
||||
title = strings.AutoNightTheme_Disabled
|
||||
}
|
||||
case .explicitNone:
|
||||
title = strings.AutoNightTheme_Disabled
|
||||
case .timeBased:
|
||||
title = strings.AutoNightTheme_Scheduled
|
||||
|
@ -386,7 +386,7 @@ public class WallpaperGalleryController: ViewController {
|
||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
if automaticThemeShouldSwitchNow(settings: current.automaticThemeSwitchSetting, currentTheme: current.theme) {
|
||||
if automaticThemeShouldSwitchNow(settings: current.automaticThemeSwitchSetting, systemUserInterfaceStyle: .light) {
|
||||
themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
|
||||
} else {
|
||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
||||
|
@ -281,7 +281,7 @@ public final class ShareController: ViewController {
|
||||
private let peers = Promise<([(RenderedPeer, PeerPresence?)], Peer)>()
|
||||
private let peersDisposable = MetaDisposable()
|
||||
private let readyDisposable = MetaDisposable()
|
||||
private let acountActiveDisposable = MetaDisposable()
|
||||
private let accountActiveDisposable = MetaDisposable()
|
||||
|
||||
private var defaultAction: ShareControllerAction?
|
||||
|
||||
@ -411,7 +411,7 @@ public final class ShareController: ViewController {
|
||||
deinit {
|
||||
self.peersDisposable.dispose()
|
||||
self.readyDisposable.dispose()
|
||||
self.acountActiveDisposable.dispose()
|
||||
self.accountActiveDisposable.dispose()
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
@ -787,7 +787,7 @@ public final class ShareController: ViewController {
|
||||
|
||||
private func switchToAccount(account: Account, animateIn: Bool) {
|
||||
self.currentAccount = account
|
||||
self.acountActiveDisposable.set(self.sharedContext.setAccountUserInterfaceInUse(account.id))
|
||||
self.accountActiveDisposable.set(self.sharedContext.setAccountUserInterfaceInUse(account.id))
|
||||
|
||||
self.peers.set(combineLatest(
|
||||
self.currentAccount.postbox.loadedPeerWithId(self.currentAccount.peerId)
|
||||
@ -838,4 +838,8 @@ public final class ShareController: ViewController {
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
public func send(peerId: PeerId) {
|
||||
self.controllerNode.send(peerId: peerId)
|
||||
}
|
||||
}
|
||||
|
@ -498,7 +498,11 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
|
||||
@objc func actionButtonPressed() {
|
||||
if self.controllerInteraction!.selectedPeers.isEmpty {
|
||||
|
||||
}
|
||||
|
||||
func send(peerId: PeerId?) {
|
||||
if peerId == nil && self.controllerInteraction!.selectedPeers.isEmpty {
|
||||
if let defaultAction = self.defaultAction {
|
||||
defaultAction.action()
|
||||
}
|
||||
@ -513,13 +517,20 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
|
||||
self.inputFieldNode.deactivateInput()
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.12, curve: .easeInOut)
|
||||
let transition: ContainedViewLayoutTransition = peerId != nil ? .immediate : .animated(duration: 0.12, curve: .easeInOut)
|
||||
transition.updateAlpha(node: self.actionButtonNode, alpha: 0.0)
|
||||
transition.updateAlpha(node: self.inputFieldNode, alpha: 0.0)
|
||||
transition.updateAlpha(node: self.actionSeparatorNode, alpha: 0.0)
|
||||
transition.updateAlpha(node: self.actionsBackgroundNode, alpha: 0.0)
|
||||
|
||||
if let signal = self.share?(self.inputFieldNode.text, self.controllerInteraction!.selectedPeers.map { $0.peerId }) {
|
||||
let peerIds: [PeerId]
|
||||
if let peerId = peerId {
|
||||
peerIds = [peerId]
|
||||
} else {
|
||||
peerIds = self.controllerInteraction!.selectedPeers.map { $0.peerId }
|
||||
}
|
||||
|
||||
if let signal = self.share?(self.inputFieldNode.text, peerIds) {
|
||||
self.transitionToContentNode(ShareLoadingContainerNode(theme: self.presentationData.theme, forceNativeAppearance: true), fastOut: true)
|
||||
let timestamp = CACurrentMediaTime()
|
||||
var wasDone = false
|
||||
|
@ -254,7 +254,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager) -
|
||||
var effectiveChatWallpaper: TelegramWallpaper = themeSettings.chatWallpaper
|
||||
|
||||
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
|
||||
if automaticThemeShouldSwitchNow(parameters, currentTheme: themeSettings.theme) {
|
||||
if automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: .light) {
|
||||
effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme
|
||||
} else {
|
||||
effectiveTheme = themeSettings.theme
|
||||
@ -295,7 +295,8 @@ private func roundTimeToDay(_ timestamp: Int32) -> Int32 {
|
||||
}
|
||||
|
||||
private enum PreparedAutomaticThemeSwitchTrigger {
|
||||
case none
|
||||
case explicitNone
|
||||
case system
|
||||
case time(fromSeconds: Int32, toSeconds: Int32)
|
||||
case brightness(threshold: Double)
|
||||
}
|
||||
@ -307,8 +308,10 @@ private struct AutomaticThemeSwitchParameters {
|
||||
init(settings: AutomaticThemeSwitchSetting) {
|
||||
let trigger: PreparedAutomaticThemeSwitchTrigger
|
||||
switch settings.trigger {
|
||||
case .none:
|
||||
trigger = .none
|
||||
case .system:
|
||||
trigger = .system
|
||||
case .explicitNone:
|
||||
trigger = .explicitNone
|
||||
case let .timeBased(setting):
|
||||
let fromValue: Int32
|
||||
let toValue: Int32
|
||||
@ -330,10 +333,12 @@ private struct AutomaticThemeSwitchParameters {
|
||||
}
|
||||
}
|
||||
|
||||
private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchParameters, currentTheme: PresentationThemeReference) -> Bool {
|
||||
private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchParameters, systemUserInterfaceStyle: WindowUserInterfaceStyle) -> Bool {
|
||||
switch parameters.trigger {
|
||||
case .none:
|
||||
case .explicitNone:
|
||||
return false
|
||||
case .system:
|
||||
return systemUserInterfaceStyle == .dark
|
||||
case let .time(fromValue, toValue):
|
||||
let roundedTimestamp = roundTimeToDay(Int32(Date().timeIntervalSince1970))
|
||||
if roundedTimestamp >= fromValue || roundedTimestamp <= toValue {
|
||||
@ -346,21 +351,21 @@ private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchPar
|
||||
}
|
||||
}
|
||||
|
||||
public func automaticThemeShouldSwitchNow(settings: AutomaticThemeSwitchSetting, currentTheme: PresentationThemeReference) -> Bool {
|
||||
public func automaticThemeShouldSwitchNow(settings: AutomaticThemeSwitchSetting, systemUserInterfaceStyle: WindowUserInterfaceStyle) -> Bool {
|
||||
let parameters = AutomaticThemeSwitchParameters(settings: settings)
|
||||
return automaticThemeShouldSwitchNow(parameters, currentTheme: currentTheme)
|
||||
return automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle)
|
||||
}
|
||||
|
||||
private func automaticThemeShouldSwitch(_ settings: AutomaticThemeSwitchSetting, currentTheme: PresentationThemeReference) -> Signal<Bool, NoError> {
|
||||
if case .none = settings.trigger {
|
||||
private func automaticThemeShouldSwitch(_ settings: AutomaticThemeSwitchSetting, systemUserInterfaceStyle: WindowUserInterfaceStyle) -> Signal<Bool, NoError> {
|
||||
if case .explicitNone = settings.trigger {
|
||||
return .single(false)
|
||||
} else {
|
||||
return Signal { subscriber in
|
||||
let parameters = AutomaticThemeSwitchParameters(settings: settings)
|
||||
subscriber.putNext(automaticThemeShouldSwitchNow(parameters, currentTheme: currentTheme))
|
||||
subscriber.putNext(automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle))
|
||||
|
||||
let timer = SwiftSignalKit.Timer(timeout: 1.0, repeat: true, completion: {
|
||||
subscriber.putNext(automaticThemeShouldSwitchNow(parameters, currentTheme: currentTheme))
|
||||
subscriber.putNext(automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle))
|
||||
}, queue: Queue.mainQueue())
|
||||
timer.start()
|
||||
|
||||
@ -472,9 +477,9 @@ public func chatServiceBackgroundColor(wallpaper: TelegramWallpaper, mediaBox: M
|
||||
}
|
||||
}
|
||||
|
||||
public func updatedPresentationData(accountManager: AccountManager, applicationInForeground: Signal<Bool, NoError>) -> Signal<PresentationData, NoError> {
|
||||
return accountManager.sharedData(keys: [SharedDataKeys.localizationSettings, ApplicationSpecificSharedDataKeys.presentationThemeSettings, ApplicationSpecificSharedDataKeys.contactSynchronizationSettings])
|
||||
|> mapToSignal { sharedData -> Signal<PresentationData, NoError> in
|
||||
public func updatedPresentationData(accountManager: AccountManager, applicationInForeground: Signal<Bool, NoError>, systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError>) -> Signal<PresentationData, NoError> {
|
||||
return combineLatest(accountManager.sharedData(keys: [SharedDataKeys.localizationSettings, ApplicationSpecificSharedDataKeys.presentationThemeSettings, ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]), systemUserInterfaceStyle)
|
||||
|> mapToSignal { sharedData, systemUserInterfaceStyle -> Signal<PresentationData, NoError> in
|
||||
let themeSettings: PresentationThemeSettings
|
||||
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings {
|
||||
themeSettings = current
|
||||
@ -497,7 +502,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
|
||||
return applicationInForeground
|
||||
|> mapToSignal { inForeground -> Signal<PresentationData, NoError> in
|
||||
if inForeground {
|
||||
return automaticThemeShouldSwitch(themeSettings.automaticThemeSwitchSetting, currentTheme: themeSettings.theme)
|
||||
return automaticThemeShouldSwitch(themeSettings.automaticThemeSwitchSetting, systemUserInterfaceStyle: systemUserInterfaceStyle)
|
||||
|> distinctUntilChanged
|
||||
|> map { shouldSwitch in
|
||||
var effectiveTheme: PresentationThemeReference
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,6 +44,7 @@ import PeerInfoUI
|
||||
import RaiseToListen
|
||||
import UrlHandling
|
||||
import ReactionSelectionNode
|
||||
import AvatarNode
|
||||
import MessageReactionListUI
|
||||
import AppBundle
|
||||
import WalletUI
|
||||
@ -513,6 +514,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.addContact(phoneNumber)
|
||||
}
|
||||
}, storeMediaPlaybackState: { [weak self] messageId, timestamp in
|
||||
var storedState: MediaPlaybackStoredState?
|
||||
if let timestamp = timestamp {
|
||||
storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: .x1)
|
||||
}
|
||||
let _ = updateMediaPlaybackStoredStateInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, state: storedState).start()
|
||||
})))
|
||||
}, openPeer: { [weak self] id, navigation, fromMessage in
|
||||
self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage)
|
||||
@ -2746,7 +2753,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
|
||||
strongSelf.donateIntent()
|
||||
strongSelf.donateSendMessageIntent()
|
||||
}
|
||||
}
|
||||
|
||||
@ -5642,7 +5649,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
|
||||
self.donateIntent()
|
||||
self.donateSendMessageIntent()
|
||||
} else {
|
||||
let mode: ChatScheduleTimeControllerMode
|
||||
if peerId == self.context.account.peerId {
|
||||
@ -7681,17 +7688,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}
|
||||
|
||||
private func donateIntent() {
|
||||
private func donateSendMessageIntent() {
|
||||
guard case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser && peerId != context.account.peerId else {
|
||||
return
|
||||
}
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
let _ = (self.context.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
|> mapToSignal { peer -> Signal<(Peer, UIImage?), NoError> in
|
||||
let avatarImage = peerAvatarImage(account: self.context.account, peer: peer, authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil)
|
||||
|
||||
return avatarImage
|
||||
|> map { avatarImage in
|
||||
return (peer, avatarImage)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer, avatarImage in
|
||||
if let peer = peer as? TelegramUser {
|
||||
let recipientHandle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown)
|
||||
let recipient = INPerson(personHandle: recipientHandle, nameComponents: nil, displayName: peer.displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)")
|
||||
let intent = INSendMessageIntent(recipients: [recipient], content: nil, groupName: nil, serviceName: nil, sender: nil)
|
||||
var nameComponents = PersonNameComponents()
|
||||
nameComponents.givenName = peer.firstName
|
||||
nameComponents.familyName = peer.lastName
|
||||
let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: peer.displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)")
|
||||
let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: peer.displayTitle), conversationIdentifier: "tg\(peerId.id)", serviceName: nil, sender: nil)
|
||||
//let intent = INSendMessageIntent(recipients: [recipient], content: nil, groupName: INSpeakableString(spokenPhrase: peer.displayTitle), serviceName: nil, sender: nil)
|
||||
if #available(iOS 12.0, *), let avatarImage = avatarImage, let avatarImageData = avatarImage.jpegData(compressionQuality: 0.8) {
|
||||
intent.setImage(INImage(imageData: avatarImageData), forParameterNamed: \.groupName)
|
||||
}
|
||||
let interaction = INInteraction(intent: intent, response: nil)
|
||||
interaction.direction = .outgoing
|
||||
interaction.donate { error in
|
||||
|
@ -50,6 +50,7 @@ private var telegramUIDeclaredEncodables: Void = {
|
||||
declareEncodable(VoipDerivedState.self, f: { VoipDerivedState(decoder: $0) })
|
||||
declareEncodable(ChatArchiveSettings.self, f: { ChatArchiveSettings(decoder: $0) })
|
||||
declareEncodable(MediaPlaybackStoredState.self, f: { MediaPlaybackStoredState(decoder: $0) })
|
||||
declareEncodable(WebBrowserSettings.self, f: { WebBrowserSettings(decoder: $0) })
|
||||
return
|
||||
}()
|
||||
|
||||
|
@ -26,7 +26,7 @@ private enum ChatMessageGalleryControllerData {
|
||||
case stickerPack(StickerPackReference)
|
||||
case audio(TelegramMediaFile)
|
||||
case document(TelegramMediaFile)
|
||||
case gallery(GalleryController)
|
||||
case gallery(Signal<GalleryController, NoError>)
|
||||
case secretGallery(SecretMediaPreviewController)
|
||||
case chatAvatars(AvatarGalleryController, Media)
|
||||
case theme(TelegramMediaFile)
|
||||
@ -148,7 +148,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
return .gallery(gallery)
|
||||
return .gallery(.single(gallery))
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
@ -156,7 +156,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
return .gallery(gallery)
|
||||
return .gallery(.single(gallery))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -165,7 +165,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
return .gallery(gallery)
|
||||
return .gallery(.single(gallery))
|
||||
}
|
||||
|
||||
if !file.isVideo {
|
||||
@ -177,11 +177,25 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
let gallery = SecretMediaPreviewController(context: context, messageId: message.id)
|
||||
return .secretGallery(gallery)
|
||||
} else {
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
let startTimecode: Signal<Double?, NoError>
|
||||
if let timecode = timecode {
|
||||
startTimecode = .single(timecode)
|
||||
} else {
|
||||
startTimecode = mediaPlaybackStoredState(postbox: context.account.postbox, messageId: message.id)
|
||||
|> map { state in
|
||||
return state?.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
return .gallery(startTimecode
|
||||
|> deliverOnMainQueue
|
||||
|> map { timecode in
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
gallery.temporaryDoNotWaitForReady = autoplayingVideo
|
||||
return .gallery(gallery)
|
||||
gallery.temporaryDoNotWaitForReady = autoplayingVideo
|
||||
return gallery
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,7 +215,8 @@ func chatMessagePreviewControllerData(context: AccountContext, message: Message,
|
||||
if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, synchronousLoad: true, actionInteraction: nil) {
|
||||
switch mediaData {
|
||||
case let .gallery(gallery):
|
||||
return .gallery(gallery)
|
||||
break
|
||||
//return .gallery(gallery)
|
||||
case let .instantPage(gallery, centralIndex, galleryMedia):
|
||||
return .instantPage(gallery, centralIndex, galleryMedia)
|
||||
default:
|
||||
@ -309,13 +324,16 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
return true
|
||||
case let .gallery(gallery):
|
||||
params.dismissInput()
|
||||
params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in
|
||||
let selectedTransitionNode = params.transitionNode(messageId, media)
|
||||
if let selectedTransitionNode = selectedTransitionNode {
|
||||
return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: params.addToTransitionSurface)
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
let _ = (gallery
|
||||
|> deliverOnMainQueue).start(next: { gallery in
|
||||
params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in
|
||||
let selectedTransitionNode = params.transitionNode(messageId, media)
|
||||
if let selectedTransitionNode = selectedTransitionNode {
|
||||
return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: params.addToTransitionSurface)
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
})
|
||||
return true
|
||||
case let .secretGallery(gallery):
|
||||
params.dismissInput()
|
||||
|
@ -10,11 +10,13 @@ import MtProtoKit
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import UrlEscaping
|
||||
import PassportUI
|
||||
import UrlHandling
|
||||
import WalletUI
|
||||
import OpenInExternalAppUI
|
||||
|
||||
public struct ParsedSecureIdUrl {
|
||||
public let peerId: PeerId
|
||||
@ -640,20 +642,42 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
if parsedUrl.host == "t.me" || parsedUrl.host == "telegram.me" {
|
||||
handleInternalUrl(parsedUrl.absoluteString)
|
||||
} else {
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
if let window = navigationController?.view.window {
|
||||
let controller = SFSafariViewController(url: parsedUrl)
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
controller.preferredBarTintColor = presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
controller.preferredControlTintColor = presentationData.theme.rootController.navigationBar.accentTextColor
|
||||
let settings = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings])
|
||||
|> take(1)
|
||||
|> map { sharedData -> WebBrowserSettings in
|
||||
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings] as? WebBrowserSettings {
|
||||
return current
|
||||
} else {
|
||||
return WebBrowserSettings.defaultSettings
|
||||
}
|
||||
}
|
||||
|
||||
let _ = (settings
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
if settings.defaultWebBrowser == nil {
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
if let window = navigationController?.view.window {
|
||||
let controller = SFSafariViewController(url: parsedUrl)
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
controller.preferredBarTintColor = presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
controller.preferredControlTintColor = presentationData.theme.rootController.navigationBar.accentTextColor
|
||||
}
|
||||
window.rootViewController?.present(controller, animated: true)
|
||||
} else {
|
||||
context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString)
|
||||
}
|
||||
} else {
|
||||
context.sharedContext.applicationBindings.openUrl(url)
|
||||
}
|
||||
window.rootViewController?.present(controller, animated: true)
|
||||
} else {
|
||||
context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString)
|
||||
let openInOptions = availableOpenInOptions(context: context, item: .url(url: url))
|
||||
if let option = openInOptions.first(where: { $0.identifier == settings.defaultWebBrowser }) {
|
||||
if case let .openUrl(url) = option.action() {
|
||||
context.sharedContext.applicationBindings.openUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context.sharedContext.applicationBindings.openUrl(url)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
context.sharedContext.applicationBindings.openUrl(url)
|
||||
|
Binary file not shown.
@ -11,6 +11,7 @@ import LegacyUI
|
||||
import PeerInfoUI
|
||||
import ShareItems
|
||||
import SettingsUI
|
||||
import Intents
|
||||
|
||||
private let inForeground = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
|
||||
@ -295,13 +296,12 @@ public class ShareRootControllerImpl {
|
||||
shareController.dismissed = { _ in
|
||||
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||
}
|
||||
|
||||
cancelImpl = { [weak shareController] in
|
||||
shareController?.dismiss(completion: { [weak self] in
|
||||
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if let strongSelf = self {
|
||||
if let currentShareController = strongSelf.currentShareController {
|
||||
currentShareController.dismiss()
|
||||
@ -310,6 +310,15 @@ public class ShareRootControllerImpl {
|
||||
strongSelf.mainWindow?.present(shareController, on: .root)
|
||||
}
|
||||
|
||||
if #available(iOS 13.0, *), let sendMessageIntent = self?.getExtensionContext()?.intent as? INSendMessageIntent {
|
||||
if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") {
|
||||
let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2))
|
||||
if let userId = Int32(string) {
|
||||
shareController.send(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.account.resetStateManagement()
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
|
||||
self._presentationData.set(.single(initialPresentationDataAndSettings.presentationData)
|
||||
|> then(
|
||||
updatedPresentationData(accountManager: self.accountManager, applicationInForeground: self.applicationBindings.applicationInForeground)
|
||||
updatedPresentationData(accountManager: self.accountManager, applicationInForeground: self.applicationBindings.applicationInForeground, systemUserInterfaceStyle: mainWindow?.systemUserInterfaceStyle ?? .single(.light))
|
||||
))
|
||||
self._automaticMediaDownloadSettings.set(.single(initialPresentationDataAndSettings.automaticMediaDownloadSettings)
|
||||
|> then(accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings])
|
||||
|
@ -66,7 +66,7 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
|
||||
validIds.insert(themeSettings.theme.index)
|
||||
themes[themeSettings.theme.index] = (themeSettings.theme, false)
|
||||
}
|
||||
if case .cloud = themeSettings.automaticThemeSwitchSetting.theme, themeSettings.automaticThemeSwitchSetting.trigger != .none {
|
||||
if case .cloud = themeSettings.automaticThemeSwitchSetting.theme, themeSettings.automaticThemeSwitchSetting.trigger != .explicitNone {
|
||||
validIds.insert(themeSettings.automaticThemeSwitchSetting.theme.index)
|
||||
themes[themeSettings.automaticThemeSwitchSetting.theme.index] = (themeSettings.automaticThemeSwitchSetting.theme, true)
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 {
|
||||
case watchPresetSettings = 13
|
||||
case webSearchSettings = 14
|
||||
case contactSynchronizationSettings = 15
|
||||
case webBrowserSettings = 16
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificSharedDataKeys {
|
||||
@ -48,6 +49,7 @@ public struct ApplicationSpecificSharedDataKeys {
|
||||
public static let watchPresetSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.watchPresetSettings.rawValue)
|
||||
public static let webSearchSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.webSearchSettings.rawValue)
|
||||
public static let contactSynchronizationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.contactSynchronizationSettings.rawValue)
|
||||
public static let webBrowserSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.webBrowserSettings.rawValue)
|
||||
}
|
||||
|
||||
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
||||
|
@ -237,27 +237,30 @@ public enum AutomaticThemeSwitchTimeBasedSetting: PostboxCoding, Equatable {
|
||||
}
|
||||
|
||||
public enum AutomaticThemeSwitchTrigger: PostboxCoding, Equatable {
|
||||
case none
|
||||
case system
|
||||
case explicitNone
|
||||
case timeBased(setting: AutomaticThemeSwitchTimeBasedSetting)
|
||||
case brightness(threshold: Double)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_t", orElse: 0) {
|
||||
case 0:
|
||||
self = .none
|
||||
self = .system
|
||||
case 1:
|
||||
self = .timeBased(setting: decoder.decodeObjectForKey("setting", decoder: { AutomaticThemeSwitchTimeBasedSetting(decoder: $0) }) as! AutomaticThemeSwitchTimeBasedSetting)
|
||||
case 2:
|
||||
self = .brightness(threshold: decoder.decodeDoubleForKey("threshold", orElse: 0.2))
|
||||
case 3:
|
||||
self = .explicitNone
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .none
|
||||
self = .system
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case .none:
|
||||
case .system:
|
||||
encoder.encodeInt32(0, forKey: "_t")
|
||||
case let .timeBased(setting):
|
||||
encoder.encodeInt32(1, forKey: "_t")
|
||||
@ -265,6 +268,8 @@ public enum AutomaticThemeSwitchTrigger: PostboxCoding, Equatable {
|
||||
case let .brightness(threshold):
|
||||
encoder.encodeInt32(2, forKey: "_t")
|
||||
encoder.encodeDouble(threshold, forKey: "threshold")
|
||||
case .explicitNone:
|
||||
encoder.encodeInt32(3, forKey: "_t")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -449,7 +454,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
}
|
||||
|
||||
public static var defaultSettings: PresentationThemeSettings {
|
||||
return PresentationThemeSettings(chatWallpaper: .builtin(WallpaperSettings()), theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .builtin(.nightAccent)), largeEmoji: true, disableAnimations: true)
|
||||
return PresentationThemeSettings(chatWallpaper: .builtin(WallpaperSettings()), theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.nightAccent)), largeEmoji: true, disableAnimations: true)
|
||||
}
|
||||
|
||||
public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
|
||||
@ -505,7 +510,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
}
|
||||
|
||||
self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular
|
||||
self.automaticThemeSwitchSetting = (decoder.decodeObjectForKey("automaticThemeSwitchSetting", decoder: { AutomaticThemeSwitchSetting(decoder: $0) }) as? AutomaticThemeSwitchSetting) ?? AutomaticThemeSwitchSetting(trigger: .none, theme: .builtin(.nightAccent))
|
||||
self.automaticThemeSwitchSetting = (decoder.decodeObjectForKey("automaticThemeSwitchSetting", decoder: { AutomaticThemeSwitchSetting(decoder: $0) }) as? AutomaticThemeSwitchSetting) ?? AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.nightAccent))
|
||||
self.largeEmoji = decoder.decodeBoolForKey("largeEmoji", orElse: true)
|
||||
self.disableAnimations = decoder.decodeBoolForKey("disableAnimations", orElse: true)
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
|
||||
public struct WebBrowserSettings: PreferencesEntry, Equatable {
|
||||
public let defaultWebBrowser: String?
|
||||
|
||||
public static var defaultSettings: WebBrowserSettings {
|
||||
return WebBrowserSettings(defaultWebBrowser: nil)
|
||||
}
|
||||
|
||||
public init(defaultWebBrowser: String?) {
|
||||
self.defaultWebBrowser = defaultWebBrowser
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.defaultWebBrowser = decoder.decodeOptionalStringForKey("defaultWebBrowser")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
if let defaultWebBrowser = self.defaultWebBrowser {
|
||||
encoder.encodeString(defaultWebBrowser, forKey: "defaultWebBrowser")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "defaultWebBrowser")
|
||||
}
|
||||
}
|
||||
|
||||
public func isEqual(to: PreferencesEntry) -> Bool {
|
||||
if let to = to as? WebBrowserSettings {
|
||||
return self == to
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: WebBrowserSettings, rhs: WebBrowserSettings) -> Bool {
|
||||
return lhs.defaultWebBrowser == rhs.defaultWebBrowser
|
||||
}
|
||||
|
||||
public func withUpdatedDefaultWebBrowser(_ defaultWebBrowser: String?) -> WebBrowserSettings {
|
||||
return WebBrowserSettings(defaultWebBrowser: defaultWebBrowser)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateWebBrowserSettingsInteractively(accountManager: AccountManager, _ f: @escaping (WebBrowserSettings) -> WebBrowserSettings) -> Signal<Void, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.webBrowserSettings, { entry in
|
||||
let currentSettings: WebBrowserSettings
|
||||
if let entry = entry as? WebBrowserSettings {
|
||||
currentSettings = entry
|
||||
} else {
|
||||
currentSettings = WebBrowserSettings.defaultSettings
|
||||
}
|
||||
return f(currentSettings)
|
||||
})
|
||||
}
|
||||
}
|
@ -170,7 +170,8 @@ private func walletReceiveScreenEntries(presentationData: PresentationData, addr
|
||||
entries.append(.addressCode(presentationData.theme, walletInvoiceUrl(address: address)))
|
||||
entries.append(.addressHeader(presentationData.theme, presentationData.strings.Wallet_Receive_AddressHeader))
|
||||
|
||||
entries.append(.address(presentationData.theme, formatAddress(address), true))
|
||||
let addressText = formatAddress(address)
|
||||
entries.append(.address(presentationData.theme, addressText, true))
|
||||
entries.append(.copyAddress(presentationData.theme, presentationData.strings.Wallet_Receive_CopyAddress))
|
||||
entries.append(.shareAddressLink(presentationData.theme, presentationData.strings.Wallet_Receive_ShareAddress))
|
||||
entries.append(.addressInfo(presentationData.theme, presentationData.strings.Wallet_Receive_ShareUrlInfo))
|
||||
|
Loading…
x
Reference in New Issue
Block a user