Merge commit '279b1304c7e062af774cc3b3d050cd3cf404f1cd'

This commit is contained in:
Peter 2019-10-23 16:54:36 +04:00
commit 14700df7ad
61 changed files with 4346 additions and 3874 deletions

View File

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11163.2" systemVersion="16A239j" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11133"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="O8c-13-3vw"> <view contentMode="scaleToFill" interfaceStyle="light" id="O8c-13-3vw">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<point key="canvasLocation" x="139" y="117"/>
</view> </view>
</objects> </objects>
</document> </document>

View File

@ -5029,3 +5029,11 @@ Any member of this group will be able to see messages in the channel.";
"TwoFactorSetup.Done.Title" = "Password Set!"; "TwoFactorSetup.Done.Title" = "Password Set!";
"TwoFactorSetup.Done.Text" = "Now password will be required when you log in on a new device in addition to the code you get via SMS."; "TwoFactorSetup.Done.Text" = "Now password will be required when you log in on a new device in addition to the code you get via SMS.";
"TwoFactorSetup.Done.Action" = "Return to Settings"; "TwoFactorSetup.Done.Action" = "Return to Settings";
"AutoNightTheme.System" = "System";
"ChatSettings.OpenLinksIn" = "Open Links in";
"WebBrowser.Title" = "Web Browser";
"WebBrowser.DefaultBrowser" = "DEFAULT WEB BROWSER";
"WebBrowser.InAppSafari" = "In-App Safari";

View File

@ -13,8 +13,6 @@ private func floorToScreenPixels(_ value: CGFloat) -> CGFloat {
return floor(value * UIScreenScale) / UIScreenScale return floor(value * UIScreenScale) / UIScreenScale
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 18.0) ?? UIFont.systemFont(ofSize: 18.0)
private let gradientColors: [NSArray] = [ private let gradientColors: [NSArray] = [
[UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor], [UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor],
[UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor], [UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor],

View File

@ -9,8 +9,9 @@ public final class GalleryControllerActionInteraction {
public let openHashtag: (String?, String) -> Void public let openHashtag: (String?, String) -> Void
public let openBotCommand: (String) -> Void public let openBotCommand: (String) -> Void
public let addContact: (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.openUrl = openUrl
self.openUrlIn = openUrlIn self.openUrlIn = openUrlIn
self.openPeerMention = openPeerMention self.openPeerMention = openPeerMention
@ -18,5 +19,6 @@ public final class GalleryControllerActionInteraction {
self.openHashtag = openHashtag self.openHashtag = openHashtag
self.openBotCommand = openBotCommand self.openBotCommand = openBotCommand
self.addContact = addContact self.addContact = addContact
self.storeMediaPlaybackState = storeMediaPlaybackState
} }
} }

View File

@ -43,7 +43,7 @@ public class ActionSheetPeerItem: ActionSheetItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 15.0)! private let avatarFont = avatarPlaceholderFont(size: 15.0)
public class ActionSheetPeerItemNode: ActionSheetItemNode { public class ActionSheetPeerItemNode: ActionSheetItemNode {
private let theme: ActionSheetControllerTheme private let theme: ActionSheetControllerTheme

View File

@ -69,15 +69,7 @@ NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *co
// kCTFontAttributeName -> NSFontAttributeName // kCTFontAttributeName -> NSFontAttributeName
if ([coreTextKey isEqualToString:(NSString *)kCTFontAttributeName]) { if ([coreTextKey isEqualToString:(NSString *)kCTFontAttributeName]) {
CTFontRef coreTextFont = (__bridge CTFontRef)coreTextValue; CTFontRef coreTextFont = (__bridge CTFontRef)coreTextValue;
NSString *fontName = (__bridge_transfer NSString *)CTFontCopyPostScriptName(coreTextFont); cleanAttributes[NSFontAttributeName] = (__bridge UIFont *)coreTextFont;
CGFloat fontSize = CTFontGetSize(coreTextFont);
UIFont *font = [UIFont fontWithName:fontName size:fontSize];
ASDisplayNodeCAssertNotNil(font, @"unable to load font %@ with size %f", fontName, fontSize);
if (font == nil) {
// Gracefully fail if we were unable to load the font.
font = [UIFont systemFontOfSize:fontSize];
}
cleanAttributes[NSFontAttributeName] = font;
} }
// kCTKernAttributeName -> NSKernAttributeName // kCTKernAttributeName -> NSKernAttributeName
else if ([coreTextKey isEqualToString:(NSString *)kCTKernAttributeName]) { else if ([coreTextKey isEqualToString:(NSString *)kCTKernAttributeName]) {

View File

@ -14,6 +14,10 @@ private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precom
private let savedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/SavedMessagesIcon"), color: .white) private let savedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/SavedMessagesIcon"), color: .white)
private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed() private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed()
public func avatarPlaceholderFont(size: CGFloat) -> UIFont {
return Font.with(size: size, design: .round, traits: [.bold])
}
public enum AvatarNodeClipStyle { public enum AvatarNodeClipStyle {
case none case none
case round case round

View File

@ -64,7 +64,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) { if let imageData = peerAvatarImageData(account: account, peer: peer, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) {
return imageData return imageData
|> mapToSignal { data -> Signal<UIImage?, NoError> in |> mapToSignal { data -> Signal<UIImage?, NoError> in
@ -78,19 +78,29 @@ public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: Messa
context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
context.setBlendMode(.copy) context.setBlendMode(.copy)
context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
if round {
context.setBlendMode(.destinationOut) context.setBlendMode(.destinationOut)
context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
}
} else { } else {
if let emptyColor = emptyColor { if let emptyColor = emptyColor {
context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
context.setFillColor(emptyColor.cgColor) context.setFillColor(emptyColor.cgColor)
if round {
context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) 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 { } else if let emptyColor = emptyColor {
context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
context.setFillColor(emptyColor.cgColor) context.setFillColor(emptyColor.cgColor)
if round {
context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) 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))
}
} }
})) }))
} }

View File

@ -174,7 +174,7 @@ class CallListCallItem: ListViewItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let avatarFont = avatarPlaceholderFont(size: 16.0)
class CallListCallItemNode: ItemListRevealOptionsItemNode { class CallListCallItemNode: ItemListRevealOptionsItemNode {
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode

View File

@ -287,8 +287,6 @@ private final class ChatListItemAccessibilityCustomAction: UIAccessibilityCustom
private let separatorHeight = 1.0 / UIScreen.main.scale private let separatorHeight = 1.0 / UIScreen.main.scale
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 26.0)!
private final class CachedChatListSearchResult { private final class CachedChatListSearchResult {
let text: String let text: String
let searchQuery: String let searchQuery: String
@ -433,7 +431,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.backgroundNode.isLayerBacked = true self.backgroundNode.isLayerBacked = true
self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displaysAsynchronously = false
self.avatarNode = AvatarNode(font: avatarFont) self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isLayerBacked = true self.highlightedBackgroundNode.isLayerBacked = true

View File

@ -281,7 +281,7 @@ public class ContactsPeerItem: ListViewItem, ListViewItemWithHeader {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let avatarFont = avatarPlaceholderFont(size: 16.0)
public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode

View File

@ -16,6 +16,8 @@ public enum DeleteChatPeerAction {
case clearCacheSuggestion case clearCacheSuggestion
} }
private let avatarFont = avatarPlaceholderFont(size: 26.0)
public final class DeleteChatPeerActionSheetItem: ActionSheetItem { public final class DeleteChatPeerActionSheetItem: ActionSheetItem {
let context: AccountContext let context: AccountContext
let peer: Peer let peer: Peer
@ -41,8 +43,6 @@ public final class DeleteChatPeerActionSheetItem: ActionSheetItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 26.0)!
private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode { private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode {
private let theme: ActionSheetControllerTheme private let theme: ActionSheetControllerTheme
private let strings: PresentationStrings private let strings: PresentationStrings

View File

@ -65,7 +65,7 @@ public func childWindowHostView(parent: UIView) -> WindowHostView {
let hostView = WindowHostView(containerView: view, eventView: view, isRotating: { let hostView = WindowHostView(containerView: view, eventView: view, isRotating: {
return false return false
}, updateSupportedInterfaceOrientations: { orientations in }, systemUserInterfaceStyle: .single(.light), updateSupportedInterfaceOrientations: { orientations in
}, updateDeferScreenEdgeGestures: { edges in }, updateDeferScreenEdgeGestures: { edges in
}, updatePrefersOnScreenNavigationHidden: { value in }, updatePrefersOnScreenNavigationHidden: { value in
}) })

View File

@ -2,6 +2,108 @@ import Foundation
import UIKit import UIKit
public struct Font { public struct Font {
public enum Design {
case regular
case serif
case monospace
case round
}
public struct Traits: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let bold = Traits(rawValue: 1 << 0)
public static let italic = Traits(rawValue: 1 << 1)
}
public enum Weight {
case regular
case light
case medium
case semibold
case bold
}
public static func with(size: CGFloat, design: Design = .regular, traits: Traits = []) -> UIFont {
if #available(iOS 13.0, *) {
var descriptor = UIFont.systemFont(ofSize: size).fontDescriptor
var symbolicTraits = descriptor.symbolicTraits
if traits.contains(.bold) {
symbolicTraits.insert(.traitBold)
}
if traits.contains(.italic) {
symbolicTraits.insert(.traitItalic)
}
var updatedDescriptor: UIFontDescriptor? = descriptor.withSymbolicTraits(symbolicTraits)
switch design {
case .serif:
updatedDescriptor = updatedDescriptor?.withDesign(.serif)
case .monospace:
updatedDescriptor = updatedDescriptor?.withDesign(.monospaced)
case .round:
updatedDescriptor = updatedDescriptor?.withDesign(.rounded)
default:
updatedDescriptor = updatedDescriptor?.withDesign(.default)
}
if let updatedDescriptor = updatedDescriptor {
return UIFont(descriptor: updatedDescriptor, size: size)
} else {
return UIFont(descriptor: descriptor, size: size)
}
} else {
switch design {
case .regular:
if traits.contains(.bold) && traits.contains(.italic) {
if let descriptor = UIFont.systemFont(ofSize: size).fontDescriptor.withSymbolicTraits([.traitBold, .traitItalic]) {
return UIFont(descriptor: descriptor, size: size)
} else {
return UIFont.italicSystemFont(ofSize: size)
}
} else if traits.contains(.bold) {
if #available(iOS 8.2, *) {
return UIFont.boldSystemFont(ofSize: size)
} else {
return CTFontCreateWithName("HelveticaNeue-Bold" as CFString, size, nil)
}
} else if traits.contains(.italic) {
return UIFont.italicSystemFont(ofSize: size)
} else {
return UIFont.systemFont(ofSize: size)
}
case .serif:
if traits.contains(.bold) && traits.contains(.italic) {
return UIFont(name: "Georgia-BoldItalic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if traits.contains(.bold) {
return UIFont(name: "Georgia-Bold", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if traits.contains(.italic) {
return UIFont(name: "Georgia-Italic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else {
return UIFont(name: "Georgia", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
}
case .monospace:
if traits.contains(.bold) && traits.contains(.italic) {
return UIFont(name: "Menlo-BoldItalic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if traits.contains(.bold) {
return UIFont(name: "Menlo-Bold", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if traits.contains(.italic) {
return UIFont(name: "Menlo-Italic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else {
return UIFont(name: "Menlo", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
}
case .round:
return UIFont(name: ".SFCompactRounded-Semibold", size: size) ?? UIFont.systemFont(ofSize: size)
}
}
}
public static func regular(_ size: CGFloat) -> UIFont { public static func regular(_ size: CGFloat) -> UIFont {
return UIFont.systemFont(ofSize: size) return UIFont.systemFont(ofSize: size)
} }

View File

@ -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 final class PreviewingHostViewDelegate {
public let controllerForLocation: (UIView, CGPoint) -> (UIViewController, CGRect)? public let controllerForLocation: (UIView, CGPoint) -> (UIViewController, CGRect)?
public let commitController: (UIViewController) -> Void public let commitController: (UIViewController) -> Void
@ -59,6 +74,11 @@ private final class WindowRootViewController: UIViewController, UIViewController
var presentController: ((UIViewController, PresentationSurfaceLevel, Bool, (() -> Void)?) -> Void)? var presentController: ((UIViewController, PresentationSurfaceLevel, Bool, (() -> Void)?) -> Void)?
var transitionToSize: ((CGSize, Double) -> 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 { var orientations: UIInterfaceOrientationMask = defaultOrientations {
didSet { didSet {
if oldValue != self.orientations { if oldValue != self.orientations {
@ -106,6 +126,12 @@ private final class WindowRootViewController: UIViewController, UIViewController
return orientations return orientations
} }
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if #available(iOS 12.0, *) {
self._systemUserInterfaceStyle.set(WindowUserInterfaceStyle(style: self.traitCollection.userInterfaceStyle))
}
}
init() { init() {
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
@ -118,6 +144,12 @@ private final class WindowRootViewController: UIViewController, UIViewController
} }
}) })
} }
if #available(iOS 13.0, *) {
self._systemUserInterfaceStyle.set(WindowUserInterfaceStyle(style: self.traitCollection.userInterfaceStyle))
} else {
self._systemUserInterfaceStyle.set(.light)
}
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
@ -350,7 +382,7 @@ public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView) {
let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, isRotating: { let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, isRotating: {
return window.isRotating() return window.isRotating()
}, updateSupportedInterfaceOrientations: { orientations in }, systemUserInterfaceStyle: rootViewController.systemUserInterfaceStyle, updateSupportedInterfaceOrientations: { orientations in
rootViewController.orientations = orientations rootViewController.orientations = orientations
}, updateDeferScreenEdgeGestures: { edges in }, updateDeferScreenEdgeGestures: { edges in
rootViewController.gestureEdges = edges rootViewController.gestureEdges = edges

View File

@ -203,6 +203,7 @@ public final class WindowHostView {
public let containerView: UIView public let containerView: UIView
public let eventView: UIView public let eventView: UIView
public let isRotating: () -> Bool public let isRotating: () -> Bool
public let systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError>
let updateSupportedInterfaceOrientations: (UIInterfaceOrientationMask) -> Void let updateSupportedInterfaceOrientations: (UIInterfaceOrientationMask) -> Void
let updateDeferScreenEdgeGestures: (UIRectEdge) -> Void let updateDeferScreenEdgeGestures: (UIRectEdge) -> Void
@ -224,10 +225,11 @@ public final class WindowHostView {
var forEachController: (((ContainableController) -> Void) -> Void)? var forEachController: (((ContainableController) -> Void) -> Void)?
var getAccessibilityElements: (() -> [Any]?)? 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.containerView = containerView
self.eventView = eventView self.eventView = eventView
self.isRotating = isRotating self.isRotating = isRotating
self.systemUserInterfaceStyle = systemUserInterfaceStyle
self.updateSupportedInterfaceOrientations = updateSupportedInterfaceOrientations self.updateSupportedInterfaceOrientations = updateSupportedInterfaceOrientations
self.updateDeferScreenEdgeGestures = updateDeferScreenEdgeGestures self.updateDeferScreenEdgeGestures = updateDeferScreenEdgeGestures
self.updatePrefersOnScreenNavigationHidden = updatePrefersOnScreenNavigationHidden self.updatePrefersOnScreenNavigationHidden = updatePrefersOnScreenNavigationHidden
@ -317,6 +319,8 @@ public class Window1 {
} }
} }
public let systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError>
private var windowPanRecognizer: WindowPanRecognizer? private var windowPanRecognizer: WindowPanRecognizer?
private let keyboardGestureRecognizerDelegate = WindowKeyboardGestureRecognizerDelegate() private let keyboardGestureRecognizerDelegate = WindowKeyboardGestureRecognizerDelegate()
private var keyboardGestureBeginLocation: CGPoint? private var keyboardGestureBeginLocation: CGPoint?
@ -331,6 +335,7 @@ public class Window1 {
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) { public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
self.hostView = hostView self.hostView = hostView
self.systemUserInterfaceStyle = hostView.systemUserInterfaceStyle
let boundsSize = self.hostView.eventView.bounds.size let boundsSize = self.hostView.eventView.bounds.size
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? defaultStatusBarHeight, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight) self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? defaultStatusBarHeight, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight)

View File

@ -140,7 +140,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String {
return message.text 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 message = entry.message
let location = entry.location let location = entry.location
if let (media, mediaImage) = mediaForMessage(message: message) { if let (media, mediaImage) = mediaForMessage(message: message) {
@ -173,7 +173,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
} }
let caption = galleryCaptionStringWithAppliedEntities(text, entities: entities) let caption = galleryCaptionStringWithAppliedEntities(text, entities: entities)
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), 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(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), 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 { } else {
if let fileName = file.fileName, (fileName as NSString).pathExtension.lowercased() == "json" { if let fileName = file.fileName, (fileName as NSString).pathExtension.lowercased() == "json" {
return ChatAnimationGalleryItem(context: context, presentationData: presentationData, message: message, location: location) return ChatAnimationGalleryItem(context: context, presentationData: presentationData, message: message, location: location)
@ -212,7 +212,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
} }
} }
if let content = content { if let content = content {
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), 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(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), 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 { } else {
return nil return nil
} }
@ -435,7 +435,7 @@ public class GalleryController: ViewController, StandalonePresentableController
if entry.message.stableId == strongSelf.centralEntryStableId { if entry.message.stableId == strongSelf.centralEntryStableId {
isCentral = true 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 { if isCentral {
centralItemIndex = items.count centralItemIndex = items.count
} }
@ -857,7 +857,7 @@ public class GalleryController: ViewController, StandalonePresentableController
if entry.message.stableId == self.centralEntryStableId { if entry.message.stableId == self.centralEntryStableId {
isCentral = true 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 { if isCentral {
centralItemIndex = items.count centralItemIndex = items.count
} }

View File

@ -34,8 +34,9 @@ public class UniversalVideoGalleryItem: GalleryItem {
let playbackCompleted: () -> Void let playbackCompleted: () -> Void
let performAction: (GalleryControllerInteractionTapAction) -> Void let performAction: (GalleryControllerInteractionTapAction) -> Void
let openActionOptions: (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.context = context
self.presentationData = presentationData self.presentationData = presentationData
self.content = content self.content = content
@ -51,6 +52,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
self.playbackCompleted = playbackCompleted self.playbackCompleted = playbackCompleted
self.performAction = performAction self.performAction = performAction
self.openActionOptions = openActionOptions self.openActionOptions = openActionOptions
self.storeMediaPlaybackState = storeMediaPlaybackState
} }
public func node() -> GalleryItemNode { public func node() -> GalleryItemNode {
@ -179,6 +181,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
private var item: UniversalVideoGalleryItem? private var item: UniversalVideoGalleryItem?
private let statusDisposable = MetaDisposable() private let statusDisposable = MetaDisposable()
private let mediaPlaybackStateDisposable = MetaDisposable()
private let fetchDisposable = MetaDisposable() private let fetchDisposable = MetaDisposable()
private var fetchStatus: MediaResourceStatus? private var fetchStatus: MediaResourceStatus?
@ -304,6 +307,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
deinit { deinit {
self.statusDisposable.dispose() self.statusDisposable.dispose()
self.mediaPlaybackStateDisposable.dispose()
self.scrubbingFrameDisposable?.dispose() self.scrubbingFrameDisposable?.dispose()
} }
@ -401,6 +405,21 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
if let contentInfo = item.contentInfo, case let .message(message) = contentInfo { if let contentInfo = item.contentInfo, case let .message(message) = contentInfo {
if Namespaces.Message.allScheduled.contains(message.id.namespace) { if Namespaces.Message.allScheduled.contains(message.id.namespace) {
disablePictureInPicture = true 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? var file: TelegramMediaFile?

View File

@ -113,7 +113,7 @@ public struct InstantPageGalleryEntry: Equatable {
nativeId = .instantPage(self.pageId, file.fileId) 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 { } else {
var representations: [TelegramMediaImageRepresentation] = [] var representations: [TelegramMediaImageRepresentation] = []
representations.append(contentsOf: file.previewRepresentations) representations.append(contentsOf: file.previewRepresentations)
@ -125,7 +125,7 @@ public struct InstantPageGalleryEntry: Equatable {
} }
} else if let embedWebpage = self.media.media as? TelegramMediaWebpage, case let .Loaded(webpageContent) = embedWebpage.content { } else if let embedWebpage = self.media.media as? TelegramMediaWebpage, case let .Loaded(webpageContent) = embedWebpage.content {
if let content = WebEmbedVideoContent(webPage: embedWebpage, webpageContent: webpageContent) { 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 { } else {
preconditionFailure() preconditionFailure()
} }

View File

@ -285,10 +285,10 @@ func instantPageThemeTypeForSettingsAndTime(themeSettings: PresentationThemeSett
var fallback = true var fallback = true
if let themeSettings = themeSettings { if let themeSettings = themeSettings {
if case .none = themeSettings.automaticThemeSwitchSetting.trigger { if case .explicitNone = themeSettings.automaticThemeSwitchSetting.trigger {
} else { } else {
fallback = false fallback = false
useDarkTheme = automaticThemeShouldSwitchNow(settings: themeSettings.automaticThemeSwitchSetting, currentTheme: themeSettings.theme) useDarkTheme = automaticThemeShouldSwitchNow(settings: themeSettings.automaticThemeSwitchSetting, systemUserInterfaceStyle: .light)
} }
} }
if fallback, let time = time { if fallback, let time = time {

View File

@ -233,7 +233,7 @@ public class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 28.0)! private let avatarFont = avatarPlaceholderFont(size: 28.0)
private let nameFont = Font.medium(19.0) private let nameFont = Font.medium(19.0)
private let statusFont = Font.regular(15.0) private let statusFont = Font.regular(15.0)

View File

@ -216,7 +216,7 @@ private let titleBoldFont = Font.medium(17.0)
private let statusFont = Font.regular(14.0) private let statusFont = Font.regular(14.0)
private let labelFont = Font.regular(13.0) private let labelFont = Font.regular(13.0)
private let labelDisclosureFont = Font.regular(17.0) private let labelDisclosureFont = Font.regular(17.0)
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 15.0)! private let avatarFont = avatarPlaceholderFont(size: 15.0)
private let badgeFont = Font.regular(15.0) private let badgeFont = Font.regular(15.0)
public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNode { public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNode {

View File

@ -11,7 +11,7 @@ import AccountContext
import SelectablePeerNode import SelectablePeerNode
import ShareController import ShareController
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 26.0)! private let avatarFont = avatarPlaceholderFont(size: 26.0)
private final class MoreNode: ASDisplayNode { private final class MoreNode: ASDisplayNode {
private let avatarNode = AvatarNode(font: Font.regular(24.0)) private let avatarNode = AvatarNode(font: Font.regular(24.0))

View File

@ -9,7 +9,7 @@ import TelegramPresentationData
import AvatarNode import AvatarNode
import AppBundle import AppBundle
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 24.0)! private let avatarFont = avatarPlaceholderFont(size: 24.0)
private let avatarBackgroundImage = UIImage(bundleImageName: "Chat/Message/LocationPin")?.precomposed() private let avatarBackgroundImage = UIImage(bundleImageName: "Chat/Message/LocationPin")?.precomposed()
private func addPulseAnimations(layer: CALayer) { private func addPulseAnimations(layer: CALayer) {

View File

@ -4,7 +4,7 @@ import AsyncDisplayKit
import Display import Display
import TelegramPresentationData import TelegramPresentationData
private let textFont: UIFont = UIFont(name: ".SFCompactRounded-Semibold", size: 13.0)! private let textFont = Font.with(size: 13.0, design: .round, traits: [.bold])
private class ChatMessageLiveLocationTimerNodeParams: NSObject { private class ChatMessageLiveLocationTimerNodeParams: NSObject {
let backgroundColor: UIColor let backgroundColor: UIColor

View File

@ -25,10 +25,12 @@ public enum OpenInAction {
} }
public final class OpenInOption { public final class OpenInOption {
public let identifier: String
public let application: OpenInApplication public let application: OpenInApplication
public let action: () -> OpenInAction 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.application = application
self.action = action self.action = action
} }
@ -61,11 +63,11 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
var options: [OpenInOption] = [] var options: [OpenInOption] = []
switch item { switch item {
case let .url(url): case let .url(url):
options.append(OpenInOption(application: .safari, action: { options.append(OpenInOption(identifier: "safari", application: .safari, action: {
return .openUrl(url: url) 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) { if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) {
components.scheme = components.scheme == "https" ? "googlechromes" : "googlechrome" components.scheme = components.scheme == "https" ? "googlechromes" : "googlechrome"
if let url = components.string { if let url = components.string {
@ -75,21 +77,21 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
return .none 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) { if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) {
return .openUrl(url: "firefox://open-url?url=\(escapedUrl)") return .openUrl(url: "firefox://open-url?url=\(escapedUrl)")
} }
return .none 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) { if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) {
return .openUrl(url: "firefox-focus://open-url?url=\(escapedUrl)") return .openUrl(url: "firefox-focus://open-url?url=\(escapedUrl)")
} }
return .none 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) { if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) {
components.scheme = components.scheme == "https" ? "opera-https" : "opera-http" components.scheme = components.scheme == "https" ? "opera-https" : "opera-http"
if let url = components.string { if let url = components.string {
@ -99,7 +101,7 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
return .none 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) { if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) {
components.scheme = components.scheme == "https" ? "touch-https" : "touch-http" components.scheme = components.scheme == "https" ? "touch-https" : "touch-http"
if let url = components.string { if let url = components.string {
@ -109,14 +111,14 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
return .none 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) { if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) {
return .openUrl(url: "yandexbrowser-open-url://\(escapedUrl)") return .openUrl(url: "yandexbrowser-open-url://\(escapedUrl)")
} }
return .none return .none
})) }))
options.append(OpenInOption(application: .other(title: "Microsoft Edge", identifier: 1288723196, scheme: "microsoft-edge-http", store: nil), action: { options.append(OpenInOption(identifier: "edge", application: .other(title: "Microsoft Edge", identifier: 1288723196, scheme: "microsoft-edge-http", store: nil), action: {
if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) { if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) {
components.scheme = components.scheme == "https" ? "microsoft-edge-https" : "microsoft-edge-http" components.scheme = components.scheme == "https" ? "microsoft-edge-https" : "microsoft-edge-http"
if let url = components.string { if let url = components.string {
@ -126,11 +128,11 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
return .none 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)") 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)") return .openUrl(url: "alook://\(url)")
})) }))
@ -140,17 +142,17 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
if !withDirections { if !withDirections {
if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "foursquare" { 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)") 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) 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)" let coordinates = "\(lat),\(lon)"
if withDirections { if withDirections {
return .openUrl(url: "comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram") return .openUrl(url: "comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram")
@ -159,7 +161,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 { if withDirections {
return .openUrl(url: "yandexmaps://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)") return .openUrl(url: "yandexmaps://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)")
} else { } else {
@ -167,7 +169,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 dropoffName: String
let dropoffAddress: String let dropoffAddress: String
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 { if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
@ -183,12 +185,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)") 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)") return .openUrl(url: "lyft://ridetype?id=lyft&destination[latitude]=\(lat)&destination[longitude]=\(lon)")
})) }))
if withDirections { 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 endName: String
let endAddress: String let endAddress: String
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 { if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
@ -204,12 +206,12 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
return .openUrl(url: "citymapper://directions?endcoord=\(lat),\(lon)&endname=\(endName)&endaddress=\(endAddress)") return .openUrl(url: "citymapper://directions?endcoord=\(lat),\(lon)&endname=\(endName)&endaddress=\(endAddress)")
})) }))
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)") 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 { if withDirections {
let destName: String let destName: String
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 { if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
@ -224,12 +226,12 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
})) }))
if !withDirections { 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)") 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)" let url = "waze://?ll=\(lat),\(lon)"
if withDirections { if withDirections {
return .openUrl(url: url.appending("&navigate=yes")) return .openUrl(url: url.appending("&navigate=yes"))

View File

@ -11,7 +11,7 @@ import TelegramUIPreferences
import AvatarNode import AvatarNode
import AppBundle import AppBundle
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 26.0)! private let avatarFont = avatarPlaceholderFont(size: 26.0)
private let titleFont = Font.semibold(14.0) private let titleFont = Font.semibold(14.0)
private let textFont = Font.regular(14.0) private let textFont = Font.regular(14.0)

View File

@ -34,7 +34,7 @@ final class ChannelDiscussionGroupActionSheetItem: ActionSheetItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 26.0)! private let avatarFont = avatarPlaceholderFont(size: 26.0)
private final class ChannelDiscussionGroupActionSheetItemNode: ActionSheetItemNode { private final class ChannelDiscussionGroupActionSheetItemNode: ActionSheetItemNode {
private let theme: ActionSheetControllerTheme private let theme: ActionSheetControllerTheme

View File

@ -13,7 +13,7 @@ import LegacyComponents
import ContextUI import ContextUI
import LocalizedPeerData import LocalizedPeerData
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 24.0)! private let avatarFont = avatarPlaceholderFont(size: 24.0)
private let textFont = Font.regular(11.0) private let textFont = Font.regular(11.0)
public final class SelectablePeerNodeTheme { public final class SelectablePeerNodeTheme {

View File

@ -83,6 +83,7 @@ static_library(
"//submodules/UndoUI:UndoUI", "//submodules/UndoUI:UndoUI",
"//submodules/DeleteChatPeerActionSheetItem:DeleteChatPeerActionSheetItem", "//submodules/DeleteChatPeerActionSheetItem:DeleteChatPeerActionSheetItem",
"//submodules/PhoneNumberFormat:PhoneNumberFormat", "//submodules/PhoneNumberFormat:PhoneNumberFormat",
"//submodules/OpenInExternalAppUI:OpenInExternalAppUI",
], ],
frameworks = [ frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework", "$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -11,6 +11,7 @@ import TelegramUIPreferences
import ItemListUI import ItemListUI
import PresentationDataUtils import PresentationDataUtils
import AccountContext import AccountContext
import OpenInExternalAppUI
private final class DataAndStorageControllerArguments { private final class DataAndStorageControllerArguments {
let openStorageUsage: () -> Void let openStorageUsage: () -> Void
@ -24,8 +25,9 @@ private final class DataAndStorageControllerArguments {
let toggleAutoplayGifs: (Bool) -> Void let toggleAutoplayGifs: (Bool) -> Void
let toggleAutoplayVideos: (Bool) -> Void let toggleAutoplayVideos: (Bool) -> Void
let toggleDownloadInBackground: (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.openStorageUsage = openStorageUsage
self.openNetworkUsage = openNetworkUsage self.openNetworkUsage = openNetworkUsage
self.openProxy = openProxy self.openProxy = openProxy
@ -37,6 +39,7 @@ private final class DataAndStorageControllerArguments {
self.toggleAutoplayGifs = toggleAutoplayGifs self.toggleAutoplayGifs = toggleAutoplayGifs
self.toggleAutoplayVideos = toggleAutoplayVideos self.toggleAutoplayVideos = toggleAutoplayVideos
self.toggleDownloadInBackground = toggleDownloadInBackground self.toggleDownloadInBackground = toggleDownloadInBackground
self.openBrowserSelection = openBrowserSelection
} }
} }
@ -80,6 +83,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
case otherHeader(PresentationTheme, String) case otherHeader(PresentationTheme, String)
case saveIncomingPhotos(PresentationTheme, String) case saveIncomingPhotos(PresentationTheme, String)
case saveEditedPhotos(PresentationTheme, String, Bool) case saveEditedPhotos(PresentationTheme, String, Bool)
case openLinksIn(PresentationTheme, String, String)
case downloadInBackground(PresentationTheme, String, Bool) case downloadInBackground(PresentationTheme, String, Bool)
case downloadInBackgroundInfo(PresentationTheme, String) case downloadInBackgroundInfo(PresentationTheme, String)
case connectionHeader(PresentationTheme, String) case connectionHeader(PresentationTheme, String)
@ -95,7 +99,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return DataAndStorageSection.autoPlay.rawValue return DataAndStorageSection.autoPlay.rawValue
case .voiceCallsHeader, .useLessVoiceData: case .voiceCallsHeader, .useLessVoiceData:
return DataAndStorageSection.voiceCalls.rawValue return DataAndStorageSection.voiceCalls.rawValue
case .otherHeader, .saveIncomingPhotos, .saveEditedPhotos, .downloadInBackground, .downloadInBackgroundInfo: case .otherHeader, .saveIncomingPhotos, .saveEditedPhotos, .openLinksIn, .downloadInBackground, .downloadInBackgroundInfo:
return DataAndStorageSection.other.rawValue return DataAndStorageSection.other.rawValue
case .connectionHeader, .connectionProxy: case .connectionHeader, .connectionProxy:
return DataAndStorageSection.connection.rawValue return DataAndStorageSection.connection.rawValue
@ -132,14 +136,16 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return 12 return 12
case .saveEditedPhotos: case .saveEditedPhotos:
return 13 return 13
case .downloadInBackground: case .openLinksIn:
return 14 return 14
case .downloadInBackgroundInfo: case .downloadInBackground:
return 15 return 15
case .connectionHeader: case .downloadInBackgroundInfo:
return 16 return 16
case .connectionProxy: case .connectionHeader:
return 17 return 17
case .connectionProxy:
return 18
} }
} }
@ -229,6 +235,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
} else { } else {
return false 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): case let .downloadInBackground(lhsTheme, lhsText, lhsValue):
if case let .downloadInBackground(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { if case let .downloadInBackground(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true return true
@ -313,6 +325,10 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleSaveEditedPhotos(value) arguments.toggleSaveEditedPhotos(value)
}, tag: DataAndStorageEntryTag.saveEditedPhotos) }, 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): case let .downloadInBackground(theme, text, value):
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleDownloadInBackground(value) arguments.toggleDownloadInBackground(value)
@ -415,7 +431,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] = [] var entries: [DataAndStorageEntry] = []
entries.append(.storageUsage(presentationData.theme, presentationData.strings.ChatSettings_Cache)) entries.append(.storageUsage(presentationData.theme, presentationData.strings.ChatSettings_Cache))
@ -439,6 +455,7 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
entries.append(.otherHeader(presentationData.theme, presentationData.strings.ChatSettings_Other)) entries.append(.otherHeader(presentationData.theme, presentationData.strings.ChatSettings_Other))
entries.append(.saveIncomingPhotos(presentationData.theme, presentationData.strings.Settings_SaveIncomingPhotos)) entries.append(.saveIncomingPhotos(presentationData.theme, presentationData.strings.Settings_SaveIncomingPhotos))
entries.append(.saveEditedPhotos(presentationData.theme, presentationData.strings.Settings_SaveEditedPhotos, data.generatedMediaStoreSettings.storeEditedPhotos)) 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(.downloadInBackground(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackground, data.automaticMediaDownloadSettings.downloadInBackground))
entries.append(.downloadInBackgroundInfo(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackgroundInfo)) entries.append(.downloadInBackgroundInfo(presentationData.theme, presentationData.strings.ChatSettings_DownloadInBackgroundInfo))
@ -564,13 +581,24 @@ func dataAndStorageController(context: AccountContext, focusOnItemTag: DataAndSt
settings.downloadInBackground = value settings.downloadInBackground = value
return settings return settings
}).start() }).start()
}, openBrowserSelection: {
let controller = webBrowserSettingsController(context: context)
pushControllerImpl?(controller)
}) })
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), dataAndStorageDataPromise.get()) |> deliverOnMainQueue let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), dataAndStorageDataPromise.get(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings])) |> deliverOnMainQueue
|> map { presentationData, state, dataAndStorageData -> (ItemListControllerState, (ItemListNodeState, Any)) in |> 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: "https://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 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)) return (controllerState, (listState, arguments))
} |> afterDisposed { } |> afterDisposed {

View File

@ -0,0 +1,124 @@
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
}

View File

@ -99,7 +99,7 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 9.0)! private let avatarFont = avatarPlaceholderFont(size: 9.0)
private let titleFont = Font.medium(15.0) private let titleFont = Font.medium(15.0)
private let textFont = Font.regular(13.0) private let textFont = Font.regular(13.0)

View File

@ -38,7 +38,7 @@ import PhoneNumberFormat
private let maximumNumberOfAccounts = 3 private let maximumNumberOfAccounts = 3
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 13.0)! private let avatarFont = avatarPlaceholderFont(size: 13.0)
private final class ContextControllerContentSourceImpl: ContextControllerContentSource { private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController let controller: ViewController

View File

@ -11,7 +11,7 @@ import AccountContext
import LocalizedPeerData import LocalizedPeerData
private let animationDurationFactor: Double = 1.0 private let animationDurationFactor: Double = 1.0
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let avatarFont = avatarPlaceholderFont(size: 16.0)
private protocol AbstractSwitchAccountItemNode { private protocol AbstractSwitchAccountItemNode {
func updateLayout(maxWidth: CGFloat) -> (CGFloat, CGFloat, (CGFloat) -> Void) func updateLayout(maxWidth: CGFloat) -> (CGFloat, CGFloat, (CGFloat) -> Void)

View File

@ -16,6 +16,7 @@ import Geocoding
import WallpaperResources import WallpaperResources
private enum TriggerMode { private enum TriggerMode {
case system
case none case none
case timeBased case timeBased
case brightness case brightness
@ -53,6 +54,7 @@ private enum ThemeAutoNightSettingsControllerSection: Int32 {
} }
private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry { private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
case modeSystem(PresentationTheme, String, Bool)
case modeDisabled(PresentationTheme, String, Bool) case modeDisabled(PresentationTheme, String, Bool)
case modeTimeBased(PresentationTheme, String, Bool) case modeTimeBased(PresentationTheme, String, Bool)
case modeBrightness(PresentationTheme, String, Bool) case modeBrightness(PresentationTheme, String, Bool)
@ -70,7 +72,7 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
case .modeDisabled, .modeTimeBased, .modeBrightness: case .modeSystem, .modeDisabled, .modeTimeBased, .modeBrightness:
return ThemeAutoNightSettingsControllerSection.mode.rawValue return ThemeAutoNightSettingsControllerSection.mode.rawValue
case .settingsHeader, .timeBasedAutomaticLocation, .timeBasedAutomaticLocationValue, .timeBasedManualFrom, .timeBasedManualTo, .brightnessValue, .settingInfo: case .settingsHeader, .timeBasedAutomaticLocation, .timeBasedAutomaticLocationValue, .timeBasedManualFrom, .timeBasedManualTo, .brightnessValue, .settingInfo:
return ThemeAutoNightSettingsControllerSection.settings.rawValue return ThemeAutoNightSettingsControllerSection.settings.rawValue
@ -81,35 +83,43 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
var stableId: Int32 { var stableId: Int32 {
switch self { switch self {
case .modeDisabled: case .modeSystem:
return 0 return 0
case .modeTimeBased: case .modeDisabled:
return 1 return 1
case .modeBrightness: case .modeTimeBased:
return 2 return 2
case .settingsHeader: case .modeBrightness:
return 3 return 3
case .timeBasedAutomaticLocation: case .settingsHeader:
return 4 return 4
case .timeBasedAutomaticLocationValue: case .timeBasedAutomaticLocation:
return 5 return 5
case .timeBasedManualFrom: case .timeBasedAutomaticLocationValue:
return 6 return 6
case .timeBasedManualTo: case .timeBasedManualFrom:
return 7 return 7
case .brightnessValue: case .timeBasedManualTo:
return 8 return 8
case .settingInfo: case .brightnessValue:
return 9 return 9
case .themeHeader: case .settingInfo:
return 10 return 10
case .themeItem: case .themeHeader:
return 11 return 11
case .themeItem:
return 12
} }
} }
static func ==(lhs: ThemeAutoNightSettingsControllerEntry, rhs: ThemeAutoNightSettingsControllerEntry) -> Bool { static func ==(lhs: ThemeAutoNightSettingsControllerEntry, rhs: ThemeAutoNightSettingsControllerEntry) -> Bool {
switch lhs { 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): case let .modeDisabled(lhsTheme, lhsTitle, lhsValue):
if case let .modeDisabled(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue { if case let .modeDisabled(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
return true return true
@ -192,6 +202,10 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
func item(_ arguments: Any) -> ListViewItem { func item(_ arguments: Any) -> ListViewItem {
let arguments = arguments as! ThemeAutoNightSettingsControllerArguments let arguments = arguments as! ThemeAutoNightSettingsControllerArguments
switch self { 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): case let .modeDisabled(theme, title, value):
return ItemListCheckboxItem(theme: theme, title: title, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { return ItemListCheckboxItem(theme: theme, title: title, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateMode(.none) arguments.updateMode(.none)
@ -243,7 +257,13 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
let activeTriggerMode: TriggerMode let activeTriggerMode: TriggerMode
switch switchSetting.trigger { switch switchSetting.trigger {
case .none: case .system:
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
activeTriggerMode = .system
} else {
activeTriggerMode = .none
}
case .explicitNone:
activeTriggerMode = .none activeTriggerMode = .none
case .timeBased: case .timeBased:
activeTriggerMode = .timeBased activeTriggerMode = .timeBased
@ -251,12 +271,15 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
activeTriggerMode = .brightness 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(.modeDisabled(theme, strings.AutoNightTheme_Disabled, activeTriggerMode == .none))
entries.append(.modeTimeBased(theme, strings.AutoNightTheme_Scheduled, activeTriggerMode == .timeBased)) entries.append(.modeTimeBased(theme, strings.AutoNightTheme_Scheduled, activeTriggerMode == .timeBased))
entries.append(.modeBrightness(theme, strings.AutoNightTheme_Automatic, activeTriggerMode == .brightness)) entries.append(.modeBrightness(theme, strings.AutoNightTheme_Automatic, activeTriggerMode == .brightness))
switch switchSetting.trigger { switch switchSetting.trigger {
case .none: case .system, .explicitNone:
break break
case let .timeBased(setting): case let .timeBased(setting):
entries.append(.settingsHeader(theme, strings.AutoNightTheme_ScheduleSection)) entries.append(.settingsHeader(theme, strings.AutoNightTheme_ScheduleSection))
@ -286,9 +309,9 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
} }
switch switchSetting.trigger { switch switchSetting.trigger {
case .none: case .explicitNone:
break break
case .timeBased, .brightness: case .system, .timeBased, .brightness:
entries.append(.themeHeader(theme, strings.AutoNightTheme_PreferredTheme)) entries.append(.themeHeader(theme, strings.AutoNightTheme_PreferredTheme))
entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors)) entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors))
} }
@ -305,7 +328,7 @@ private func roundTimeToDay(_ timestamp: Int32) -> Int32 {
private func areSettingsValid(_ settings: AutomaticThemeSwitchSetting) -> Bool { private func areSettingsValid(_ settings: AutomaticThemeSwitchSetting) -> Bool {
switch settings.trigger { switch settings.trigger {
case .none: case .system, .explicitNone, .brightness:
return true return true
case let .timeBased(setting): case let .timeBased(setting):
switch setting { switch setting {
@ -318,8 +341,6 @@ private func areSettingsValid(_ settings: AutomaticThemeSwitchSetting) -> Bool {
case .manual: case .manual:
return true return true
} }
case .brightness:
return true
} }
} }
@ -386,8 +407,10 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon
updateSettings { settings in updateSettings { settings in
var settings = settings var settings = settings
switch mode { switch mode {
case .system:
settings.trigger = .system
case .none: case .none:
settings.trigger = .none settings.trigger = .explicitNone
case .timeBased: case .timeBased:
if case .timeBased = settings.trigger { if case .timeBased = settings.trigger {
} else { } else {

View File

@ -366,7 +366,13 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
let title: String let title: String
switch autoNightSettings.trigger { 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 title = strings.AutoNightTheme_Disabled
case .timeBased: case .timeBased:
title = strings.AutoNightTheme_Scheduled title = strings.AutoNightTheme_Scheduled

View File

@ -387,7 +387,7 @@ public class WallpaperGalleryController: ViewController {
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
var chatWallpaper = current.chatWallpaper 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 themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
} else { } else {
themeSpecificChatWallpapers[current.theme.index] = wallpaper themeSpecificChatWallpapers[current.theme.index] = wallpaper

View File

@ -60,7 +60,7 @@ private struct ShareGridTransaction {
let animated: Bool let animated: Bool
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 17.0)! private let avatarFont = avatarPlaceholderFont(size: 17.0)
private func preparedGridEntryTransition(account: Account, from fromEntries: [SharePeerEntry], to toEntries: [SharePeerEntry], interfaceInteraction: ShareControllerInteraction) -> ShareGridTransaction { private func preparedGridEntryTransition(account: Account, from fromEntries: [SharePeerEntry], to toEntries: [SharePeerEntry], interfaceInteraction: ShareControllerInteraction) -> ShareGridTransaction {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)

View File

@ -45,7 +45,7 @@ public class LocationBroadcastActionSheetItem: ActionSheetItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 15.0)! private let avatarFont = avatarPlaceholderFont(size: 15.0)
public class LocationBroadcastActionSheetItemNode: ActionSheetItemNode { public class LocationBroadcastActionSheetItemNode: ActionSheetItemNode {
private let theme: ActionSheetControllerTheme private let theme: ActionSheetControllerTheme

View File

@ -249,7 +249,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager) -
var effectiveChatWallpaper: TelegramWallpaper = themeSettings.chatWallpaper var effectiveChatWallpaper: TelegramWallpaper = themeSettings.chatWallpaper
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting) let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
if automaticThemeShouldSwitchNow(parameters, currentTheme: themeSettings.theme) { if automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: .light) {
effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme
} else { } else {
effectiveTheme = themeSettings.theme effectiveTheme = themeSettings.theme
@ -290,7 +290,8 @@ private func roundTimeToDay(_ timestamp: Int32) -> Int32 {
} }
private enum PreparedAutomaticThemeSwitchTrigger { private enum PreparedAutomaticThemeSwitchTrigger {
case none case explicitNone
case system
case time(fromSeconds: Int32, toSeconds: Int32) case time(fromSeconds: Int32, toSeconds: Int32)
case brightness(threshold: Double) case brightness(threshold: Double)
} }
@ -302,8 +303,10 @@ private struct AutomaticThemeSwitchParameters {
init(settings: AutomaticThemeSwitchSetting) { init(settings: AutomaticThemeSwitchSetting) {
let trigger: PreparedAutomaticThemeSwitchTrigger let trigger: PreparedAutomaticThemeSwitchTrigger
switch settings.trigger { switch settings.trigger {
case .none: case .system:
trigger = .none trigger = .system
case .explicitNone:
trigger = .explicitNone
case let .timeBased(setting): case let .timeBased(setting):
let fromValue: Int32 let fromValue: Int32
let toValue: Int32 let toValue: Int32
@ -325,10 +328,12 @@ private struct AutomaticThemeSwitchParameters {
} }
} }
private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchParameters, currentTheme: PresentationThemeReference) -> Bool { private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchParameters, systemUserInterfaceStyle: WindowUserInterfaceStyle) -> Bool {
switch parameters.trigger { switch parameters.trigger {
case .none: case .explicitNone:
return false return false
case .system:
return systemUserInterfaceStyle == .dark
case let .time(fromValue, toValue): case let .time(fromValue, toValue):
let roundedTimestamp = roundTimeToDay(Int32(Date().timeIntervalSince1970)) let roundedTimestamp = roundTimeToDay(Int32(Date().timeIntervalSince1970))
if roundedTimestamp >= fromValue || roundedTimestamp <= toValue { if roundedTimestamp >= fromValue || roundedTimestamp <= toValue {
@ -341,21 +346,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) 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> { private func automaticThemeShouldSwitch(_ settings: AutomaticThemeSwitchSetting, systemUserInterfaceStyle: WindowUserInterfaceStyle) -> Signal<Bool, NoError> {
if case .none = settings.trigger { if case .explicitNone = settings.trigger {
return .single(false) return .single(false)
} else { } else {
return Signal { subscriber in return Signal { subscriber in
let parameters = AutomaticThemeSwitchParameters(settings: settings) 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: { 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()) }, queue: Queue.mainQueue())
timer.start() timer.start()
@ -467,9 +472,9 @@ public func chatServiceBackgroundColor(wallpaper: TelegramWallpaper, mediaBox: M
} }
} }
public func updatedPresentationData(accountManager: AccountManager, applicationInForeground: Signal<Bool, NoError>) -> Signal<PresentationData, NoError> { public func updatedPresentationData(accountManager: AccountManager, applicationInForeground: Signal<Bool, NoError>, systemUserInterfaceStyle: Signal<WindowUserInterfaceStyle, NoError>) -> Signal<PresentationData, NoError> {
return accountManager.sharedData(keys: [SharedDataKeys.localizationSettings, ApplicationSpecificSharedDataKeys.presentationThemeSettings, ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]) return combineLatest(accountManager.sharedData(keys: [SharedDataKeys.localizationSettings, ApplicationSpecificSharedDataKeys.presentationThemeSettings, ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]), systemUserInterfaceStyle)
|> mapToSignal { sharedData -> Signal<PresentationData, NoError> in |> mapToSignal { sharedData, systemUserInterfaceStyle -> Signal<PresentationData, NoError> in
let themeSettings: PresentationThemeSettings let themeSettings: PresentationThemeSettings
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings { if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings {
themeSettings = current themeSettings = current
@ -492,7 +497,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
return applicationInForeground return applicationInForeground
|> mapToSignal { inForeground -> Signal<PresentationData, NoError> in |> mapToSignal { inForeground -> Signal<PresentationData, NoError> in
if inForeground { if inForeground {
return automaticThemeShouldSwitch(themeSettings.automaticThemeSwitchSetting, currentTheme: themeSettings.theme) return automaticThemeShouldSwitch(themeSettings.automaticThemeSwitchSetting, systemUserInterfaceStyle: systemUserInterfaceStyle)
|> distinctUntilChanged |> distinctUntilChanged
|> map { shouldSwitch in |> map { shouldSwitch in
var effectiveTheme: PresentationThemeReference var effectiveTheme: PresentationThemeReference

View File

@ -5,8 +5,8 @@ import Display
import AvatarNode import AvatarNode
import ContextUI import ContextUI
private let normalFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let normalFont = avatarPlaceholderFont(size: 16.0)
private let smallFont = UIFont(name: ".SFCompactRounded-Semibold", size: 12.0)! private let smallFont = avatarPlaceholderFont(size: 12.0)
final class ChatAvatarNavigationNodeView: UIView, PreviewingHostView { final class ChatAvatarNavigationNodeView: UIView, PreviewingHostView {
var previewingDelegate: PreviewingHostViewDelegate? { var previewingDelegate: PreviewingHostViewDelegate? {

View File

@ -46,6 +46,7 @@ import PeerInfoUI
import RaiseToListen import RaiseToListen
import UrlHandling import UrlHandling
import ReactionSelectionNode import ReactionSelectionNode
import AvatarNode
import MessageReactionListUI import MessageReactionListUI
import AppBundle import AppBundle
import WalletUI import WalletUI
@ -527,6 +528,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self { if let strongSelf = self {
strongSelf.controllerInteraction?.addContact(phoneNumber) 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 }, openPeer: { [weak self] id, navigation, fromMessage in
self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage) self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage)
@ -2786,7 +2793,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}) })
strongSelf.donateIntent() strongSelf.donateSendMessageIntent()
} }
} }
@ -5965,7 +5972,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}) })
self.donateIntent() self.donateSendMessageIntent()
} else { } else {
let mode: ChatScheduleTimeControllerMode let mode: ChatScheduleTimeControllerMode
if peerId == self.context.account.peerId { if peerId == self.context.account.peerId {
@ -8037,17 +8044,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 { guard case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser && peerId != context.account.peerId else {
return return
} }
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
let _ = (self.context.account.postbox.loadedPeerWithId(peerId) let _ = (self.context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { [weak self] 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: { [weak self] peer, avatarImage in
if let strongSelf = self, let peer = peer as? TelegramUser { if let strongSelf = self, let peer = peer as? TelegramUser {
let recipientHandle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown) let recipientHandle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown)
let recipient = INPerson(personHandle: recipientHandle, nameComponents: nil, displayName: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)") var nameComponents = PersonNameComponents()
let intent = INSendMessageIntent(recipients: [recipient], content: nil, groupName: nil, serviceName: nil, sender: nil) nameComponents.givenName = peer.firstName
nameComponents.familyName = peer.lastName
let displayTitle = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)")
let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: displayTitle), conversationIdentifier: "tg\(peerId.id)", 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) let interaction = INInteraction(intent: intent, response: nil)
interaction.direction = .outgoing interaction.direction = .outgoing
interaction.donate { error in interaction.donate { error in

View File

@ -60,7 +60,7 @@ final class ChatMediaInputPeerSpecificItem: ListViewItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 12.0)! private let avatarFont = avatarPlaceholderFont(size: 12.0)
private let boundingSize = CGSize(width: 41.0, height: 41.0) private let boundingSize = CGSize(width: 41.0, height: 41.0)
private let boundingImageSize = CGSize(width: 28.0, height: 28.0) private let boundingImageSize = CGSize(width: 28.0, height: 28.0)
private let highlightSize = CGSize(width: 35.0, height: 35.0) private let highlightSize = CGSize(width: 35.0, height: 35.0)

View File

@ -8,7 +8,7 @@ import TelegramPresentationData
import AvatarNode import AvatarNode
import AccountContext import AccountContext
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let avatarFont = avatarPlaceholderFont(size: 16.0)
final class ChatMessageAvatarAccessoryItem: ListViewAccessoryItem { final class ChatMessageAvatarAccessoryItem: ListViewAccessoryItem {
private let context: AccountContext private let context: AccountContext

View File

@ -11,7 +11,7 @@ import AvatarNode
import AccountContext import AccountContext
import PhoneNumberFormat import PhoneNumberFormat
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let avatarFont = avatarPlaceholderFont(size: 16.0)
private let titleFont = Font.medium(14.0) private let titleFont = Font.medium(14.0)
private let textFont = Font.regular(14.0) private let textFont = Font.regular(14.0)

View File

@ -57,8 +57,8 @@ public final class ChatMessageNotificationItem: NotificationItem {
} }
} }
private let compactAvatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 20.0)! private let compactAvatarFont = avatarPlaceholderFont(size: 20.0)
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 24.0)! private let avatarFont = avatarPlaceholderFont(size: 24.0)
final class ChatMessageNotificationItemNode: NotificationItemNode { final class ChatMessageNotificationItemNode: NotificationItemNode {
private var item: ChatMessageNotificationItem? private var item: ChatMessageNotificationItem?

View File

@ -76,7 +76,7 @@ final class CommandChatInputPanelItem: ListViewItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let avatarFont = avatarPlaceholderFont(size: 16.0)
private let textFont = Font.medium(14.0) private let textFont = Font.medium(14.0)
private let descriptionFont = Font.regular(14.0) private let descriptionFont = Font.regular(14.0)

View File

@ -50,6 +50,7 @@ private var telegramUIDeclaredEncodables: Void = {
declareEncodable(VoipDerivedState.self, f: { VoipDerivedState(decoder: $0) }) declareEncodable(VoipDerivedState.self, f: { VoipDerivedState(decoder: $0) })
declareEncodable(ChatArchiveSettings.self, f: { ChatArchiveSettings(decoder: $0) }) declareEncodable(ChatArchiveSettings.self, f: { ChatArchiveSettings(decoder: $0) })
declareEncodable(MediaPlaybackStoredState.self, f: { MediaPlaybackStoredState(decoder: $0) }) declareEncodable(MediaPlaybackStoredState.self, f: { MediaPlaybackStoredState(decoder: $0) })
declareEncodable(WebBrowserSettings.self, f: { WebBrowserSettings(decoder: $0) })
return return
}() }()

View File

@ -78,7 +78,7 @@ final class MentionChatInputPanelItem: ListViewItem {
} }
} }
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let avatarFont = avatarPlaceholderFont(size: 16.0)
private let primaryFont = Font.medium(14.0) private let primaryFont = Font.medium(14.0)
private let secondaryFont = Font.regular(14.0) private let secondaryFont = Font.regular(14.0)

View File

@ -29,7 +29,7 @@ private enum ChatMessageGalleryControllerData {
case stickerPack(StickerPackReference) case stickerPack(StickerPackReference)
case audio(TelegramMediaFile) case audio(TelegramMediaFile)
case document(TelegramMediaFile, Bool) case document(TelegramMediaFile, Bool)
case gallery(GalleryController) case gallery(Signal<GalleryController, NoError>)
case secretGallery(SecretMediaPreviewController) case secretGallery(SecretMediaPreviewController)
case chatAvatars(AvatarGalleryController, Media) case chatAvatars(AvatarGalleryController, Media)
case theme(TelegramMediaFile) case theme(TelegramMediaFile)
@ -151,7 +151,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 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) navigationController?.replaceTopController(controller, animated: false, ready: ready)
}, baseNavigationController: navigationController, actionInteraction: actionInteraction) }, baseNavigationController: navigationController, actionInteraction: actionInteraction)
return .gallery(gallery) return .gallery(.single(gallery))
} }
} }
@ -164,7 +164,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 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) navigationController?.replaceTopController(controller, animated: false, ready: ready)
}, baseNavigationController: navigationController, actionInteraction: actionInteraction) }, baseNavigationController: navigationController, actionInteraction: actionInteraction)
return .gallery(gallery) return .gallery(.single(gallery))
} }
if !file.isVideo { if !file.isVideo {
@ -176,11 +176,25 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
let gallery = SecretMediaPreviewController(context: context, messageId: message.id) let gallery = SecretMediaPreviewController(context: context, messageId: message.id)
return .secretGallery(gallery) return .secretGallery(gallery)
} else { } else {
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 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) navigationController?.replaceTopController(controller, animated: false, ready: ready)
}, baseNavigationController: navigationController, actionInteraction: actionInteraction) }, baseNavigationController: navigationController, actionInteraction: actionInteraction)
gallery.temporaryDoNotWaitForReady = autoplayingVideo gallery.temporaryDoNotWaitForReady = autoplayingVideo
return .gallery(gallery) return gallery
})
} }
} }
} }
@ -200,7 +214,7 @@ 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) { if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, synchronousLoad: true, actionInteraction: nil) {
switch mediaData { switch mediaData {
case let .gallery(gallery): case let .gallery(gallery):
return .gallery(gallery) break
case let .instantPage(gallery, centralIndex, galleryMedia): case let .instantPage(gallery, centralIndex, galleryMedia):
return .instantPage(gallery, centralIndex, galleryMedia) return .instantPage(gallery, centralIndex, galleryMedia)
default: default:
@ -311,6 +325,8 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
return true return true
case let .gallery(gallery): case let .gallery(gallery):
params.dismissInput() params.dismissInput()
let _ = (gallery
|> deliverOnMainQueue).start(next: { gallery in
params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in params.present(gallery, GalleryControllerPresentationArguments(transitionArguments: { messageId, media in
let selectedTransitionNode = params.transitionNode(messageId, media) let selectedTransitionNode = params.transitionNode(messageId, media)
if let selectedTransitionNode = selectedTransitionNode { if let selectedTransitionNode = selectedTransitionNode {
@ -318,6 +334,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
} }
return nil return nil
})) }))
})
return true return true
case let .secretGallery(gallery): case let .secretGallery(gallery):
params.dismissInput() params.dismissInput()

View File

@ -11,12 +11,14 @@ import MtProtoKit
import MtProtoKitDynamic import MtProtoKitDynamic
#endif #endif
import TelegramPresentationData import TelegramPresentationData
import TelegramUIPreferences
import AccountContext import AccountContext
import UrlEscaping import UrlEscaping
import PassportUI import PassportUI
import UrlHandling import UrlHandling
import WalletUI import WalletUI
import WalletUrl import WalletUrl
import OpenInExternalAppUI
public struct ParsedSecureIdUrl { public struct ParsedSecureIdUrl {
public let peerId: PeerId public let peerId: PeerId

View File

@ -183,7 +183,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
self._presentationData.set(.single(initialPresentationDataAndSettings.presentationData) self._presentationData.set(.single(initialPresentationDataAndSettings.presentationData)
|> then( |> 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) self._automaticMediaDownloadSettings.set(.single(initialPresentationDataAndSettings.automaticMediaDownloadSettings)
|> then(accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]) |> then(accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings])

View File

@ -67,7 +67,7 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
validIds.insert(themeSettings.theme.index) validIds.insert(themeSettings.theme.index)
themes[themeSettings.theme.index] = (themeSettings.theme, false) 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) validIds.insert(themeSettings.automaticThemeSwitchSetting.theme.index)
themes[themeSettings.automaticThemeSwitchSetting.theme.index] = (themeSettings.automaticThemeSwitchSetting.theme, true) themes[themeSettings.automaticThemeSwitchSetting.theme.index] = (themeSettings.automaticThemeSwitchSetting.theme, true)
} }

View File

@ -30,6 +30,7 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 {
case watchPresetSettings = 13 case watchPresetSettings = 13
case webSearchSettings = 14 case webSearchSettings = 14
case contactSynchronizationSettings = 15 case contactSynchronizationSettings = 15
case webBrowserSettings = 16
} }
public struct ApplicationSpecificSharedDataKeys { public struct ApplicationSpecificSharedDataKeys {
@ -49,6 +50,7 @@ public struct ApplicationSpecificSharedDataKeys {
public static let watchPresetSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.watchPresetSettings.rawValue) public static let watchPresetSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.watchPresetSettings.rawValue)
public static let webSearchSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.webSearchSettings.rawValue) public static let webSearchSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.webSearchSettings.rawValue)
public static let contactSynchronizationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.contactSynchronizationSettings.rawValue) public static let contactSynchronizationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.contactSynchronizationSettings.rawValue)
public static let webBrowserSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.webBrowserSettings.rawValue)
} }
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {

View File

@ -238,27 +238,30 @@ public enum AutomaticThemeSwitchTimeBasedSetting: PostboxCoding, Equatable {
} }
public enum AutomaticThemeSwitchTrigger: PostboxCoding, Equatable { public enum AutomaticThemeSwitchTrigger: PostboxCoding, Equatable {
case none case system
case explicitNone
case timeBased(setting: AutomaticThemeSwitchTimeBasedSetting) case timeBased(setting: AutomaticThemeSwitchTimeBasedSetting)
case brightness(threshold: Double) case brightness(threshold: Double)
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("_t", orElse: 0) { switch decoder.decodeInt32ForKey("_t", orElse: 0) {
case 0: case 0:
self = .none self = .system
case 1: case 1:
self = .timeBased(setting: decoder.decodeObjectForKey("setting", decoder: { AutomaticThemeSwitchTimeBasedSetting(decoder: $0) }) as! AutomaticThemeSwitchTimeBasedSetting) self = .timeBased(setting: decoder.decodeObjectForKey("setting", decoder: { AutomaticThemeSwitchTimeBasedSetting(decoder: $0) }) as! AutomaticThemeSwitchTimeBasedSetting)
case 2: case 2:
self = .brightness(threshold: decoder.decodeDoubleForKey("threshold", orElse: 0.2)) self = .brightness(threshold: decoder.decodeDoubleForKey("threshold", orElse: 0.2))
case 3:
self = .explicitNone
default: default:
assertionFailure() assertionFailure()
self = .none self = .system
} }
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
switch self { switch self {
case .none: case .system:
encoder.encodeInt32(0, forKey: "_t") encoder.encodeInt32(0, forKey: "_t")
case let .timeBased(setting): case let .timeBased(setting):
encoder.encodeInt32(1, forKey: "_t") encoder.encodeInt32(1, forKey: "_t")
@ -266,6 +269,8 @@ public enum AutomaticThemeSwitchTrigger: PostboxCoding, Equatable {
case let .brightness(threshold): case let .brightness(threshold):
encoder.encodeInt32(2, forKey: "_t") encoder.encodeInt32(2, forKey: "_t")
encoder.encodeDouble(threshold, forKey: "threshold") encoder.encodeDouble(threshold, forKey: "threshold")
case .explicitNone:
encoder.encodeInt32(3, forKey: "_t")
} }
} }
} }
@ -450,7 +455,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
} }
public static var defaultSettings: PresentationThemeSettings { 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(.night)), largeEmoji: true, disableAnimations: true)
} }
public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) { public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
@ -506,7 +511,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
} }
self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular 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(.night))
self.largeEmoji = decoder.decodeBoolForKey("largeEmoji", orElse: true) self.largeEmoji = decoder.decodeBoolForKey("largeEmoji", orElse: true)
self.disableAnimations = decoder.decodeBoolForKey("disableAnimations", orElse: true) self.disableAnimations = decoder.decodeBoolForKey("disableAnimations", orElse: true)
} }

View File

@ -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)
})
}
}

View File

@ -12,7 +12,7 @@ final class WebSearchBadgeNode: ASDisplayNode {
private let textNode: ASTextNode private let textNode: ASTextNode
private let backgroundNode: ASImageNode private let backgroundNode: ASImageNode
private let font: UIFont = UIFont(name: ".SFCompactRounded-Semibold", size: 17.0)! private let font: UIFont = Font.with(size: 17.0, design: .round, traits: [.bold])
var text: String = "" { var text: String = "" {
didSet { didSet {

@ -1 +1 @@
Subproject commit 699d822fc547e6d9b997667f8d5aecf75182d0a3 Subproject commit a09896b3e72e76681c12e80572a7d570108cf885