mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 17:30:12 +00:00
Various improvements
This commit is contained in:
parent
b006c36c59
commit
8120dde68c
@ -981,7 +981,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
|
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
|
||||||
|
|
||||||
func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: String?, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController
|
func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: (url: String, name: String?)?, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController
|
||||||
|
|
||||||
func makeBotPreviewEditorScreen(context: AccountContext, source: Any?, target: Stories.PendingTarget, transitionArguments: (UIView, CGRect, UIImage?)?, transitionOut: @escaping () -> BotPreviewEditorTransitionOut?, externalState: MediaEditorTransitionOutExternalState, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
func makeBotPreviewEditorScreen(context: AccountContext, source: Any?, target: Stories.PendingTarget, transitionArguments: (UIView, CGRect, UIImage?)?, transitionOut: @escaping () -> BotPreviewEditorTransitionOut?, externalState: MediaEditorTransitionOutExternalState, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
||||||
|
|
||||||
|
|||||||
@ -1365,4 +1365,13 @@ public class AttachmentController: ViewController, MinimizableController {
|
|||||||
})
|
})
|
||||||
return disposableSet
|
return disposableSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makeContentSnapshotView() -> UIView? {
|
||||||
|
let snapshotView = self.view.snapshotView(afterScreenUpdates: false)
|
||||||
|
if let contentSnapshotView = self.mainController.makeContentSnapshotView() {
|
||||||
|
contentSnapshotView.frame = contentSnapshotView.frame.offsetBy(dx: 0.0, dy: 64.0 + 56.0)
|
||||||
|
snapshotView?.addSubview(contentSnapshotView)
|
||||||
|
}
|
||||||
|
return snapshotView
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -187,6 +187,8 @@ protocol BrowserContent: UIView {
|
|||||||
func addToRecentlyVisited()
|
func addToRecentlyVisited()
|
||||||
|
|
||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ComponentTransition)
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, transition: ComponentTransition)
|
||||||
|
|
||||||
|
func makeContentSnapshotView() -> UIView?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentScrollingUpdate {
|
struct ContentScrollingUpdate {
|
||||||
|
|||||||
@ -468,4 +468,8 @@ final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate
|
|||||||
|
|
||||||
func addToRecentlyVisited() {
|
func addToRecentlyVisited() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeContentSnapshotView() -> UIView? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1418,4 +1418,8 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
let _ = addRecentlyVisitedLink(engine: self.context.engine, webPage: webPage).startStandalone()
|
let _ = addRecentlyVisitedLink(engine: self.context.engine, webPage: webPage).startStandalone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeContentSnapshotView() -> UIView? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -460,4 +460,8 @@ final class BrowserPdfContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
|
|
||||||
func addToRecentlyVisited() {
|
func addToRecentlyVisited() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeContentSnapshotView() -> UIView? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1170,16 +1170,18 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
|
|
||||||
var openPreviousOnClose = false
|
var openPreviousOnClose = false
|
||||||
|
|
||||||
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
public static let supportedDocumentMimeTypes: [String] = [
|
public static let supportedDocumentMimeTypes: [String] = [
|
||||||
"text/plain",
|
// "text/plain",
|
||||||
"text/rtf",
|
// "text/rtf",
|
||||||
"application/pdf",
|
// "application/pdf",
|
||||||
"application/msword",
|
// "application/msword",
|
||||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
// "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
"application/vnd.ms-excel",
|
// "application/vnd.ms-excel",
|
||||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
// "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
// "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
// "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||||
]
|
]
|
||||||
|
|
||||||
public init(context: AccountContext, subject: Subject) {
|
public init(context: AccountContext, subject: Subject) {
|
||||||
@ -1212,6 +1214,8 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.validLayout = layout
|
||||||
|
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
self.node.containerLayoutUpdated(layout: layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.height, transition: ComponentTransition(transition))
|
self.node.containerLayoutUpdated(layout: layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.height, transition: ComponentTransition(transition))
|
||||||
@ -1221,7 +1225,15 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
self.node.minimize(topEdgeOffset: topEdgeOffset, damping: 180.0, initialVelocity: initialVelocity)
|
self.node.minimize(topEdgeOffset: topEdgeOffset, damping: 180.0, initialVelocity: initialVelocity)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var isMinimized = false
|
public var isMinimized = false {
|
||||||
|
didSet {
|
||||||
|
if let webContent = self.node.content.last as? BrowserWebContent {
|
||||||
|
if !self.isMinimized {
|
||||||
|
webContent.webView.setNeedsLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public var isMinimizable = true
|
public var isMinimizable = true
|
||||||
|
|
||||||
public var minimizedIcon: UIImage? {
|
public var minimizedIcon: UIImage? {
|
||||||
@ -1244,6 +1256,20 @@ public class BrowserScreen: ViewController, MinimizableController {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makeContentSnapshotView() -> UIView? {
|
||||||
|
if let contentSnapshot = self.node.content.last?.makeContentSnapshotView(), let layout = self.validLayout {
|
||||||
|
if let wrapperView = self.view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
contentSnapshot.frame = contentSnapshot.frame.offsetBy(dx: 0.0, dy: self.navigationLayout(layout: layout).navigationFrame.height)
|
||||||
|
wrapperView.addSubview(contentSnapshot)
|
||||||
|
return wrapperView
|
||||||
|
} else {
|
||||||
|
return contentSnapshot
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return self.view.snapshotView(afterScreenUpdates: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class BrowserReferenceContentSource: ContextReferenceContentSource {
|
private final class BrowserReferenceContentSource: ContextReferenceContentSource {
|
||||||
|
|||||||
@ -119,7 +119,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
|
||||||
private let webView: WKWebView
|
let webView: WKWebView
|
||||||
|
|
||||||
private let errorView: ComponentHostView<Empty>
|
private let errorView: ComponentHostView<Empty>
|
||||||
private var currentError: Error?
|
private var currentError: Error?
|
||||||
@ -486,7 +486,9 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
if keyPath == "title" {
|
if keyPath == "title" {
|
||||||
self.updateState { $0.withUpdatedTitle(self.webView.title ?? "") }
|
self.updateState { $0.withUpdatedTitle(self.webView.title ?? "") }
|
||||||
} else if keyPath == "URL" {
|
} else if keyPath == "URL" {
|
||||||
self.updateState { $0.withUpdatedUrl(self.webView.url?.absoluteString ?? "") }
|
if let url = self.webView.url {
|
||||||
|
self.updateState { $0.withUpdatedUrl(url.absoluteString) }
|
||||||
|
}
|
||||||
self.didSetupSearch = false
|
self.didSetupSearch = false
|
||||||
} else if keyPath == "estimatedProgress" {
|
} else if keyPath == "estimatedProgress" {
|
||||||
self.updateState { $0.withUpdatedEstimatedProgress(self.webView.estimatedProgress) }
|
self.updateState { $0.withUpdatedEstimatedProgress(self.webView.estimatedProgress) }
|
||||||
@ -571,18 +573,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||||
if (error as NSError).code != -999 {
|
if [-1003, -1100].contains((error as NSError).code) {
|
||||||
self.currentError = error
|
|
||||||
} else {
|
|
||||||
self.currentError = nil
|
|
||||||
}
|
|
||||||
if let (size, insets, fullInsets) = self.validLayout {
|
|
||||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, transition: .immediate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
||||||
if (error as NSError).code != -999 {
|
|
||||||
self.currentError = error
|
self.currentError = error
|
||||||
} else {
|
} else {
|
||||||
self.currentError = nil
|
self.currentError = nil
|
||||||
@ -752,7 +743,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
var nodeList = document.getElementsByTagName('link');
|
var nodeList = document.getElementsByTagName('link');
|
||||||
for (var i = 0; i < nodeList.length; i++)
|
for (var i = 0; i < nodeList.length; i++)
|
||||||
{
|
{
|
||||||
if((nodeList[i].getAttribute('rel') == 'icon')||(nodeList[i].getAttribute('rel') == 'shortcut icon'))
|
if((nodeList[i].getAttribute('rel') == 'icon')||(nodeList[i].getAttribute('rel') == 'shortcut icon')||(nodeList[i].getAttribute('rel').startsWith('apple-touch-icon')))
|
||||||
{
|
{
|
||||||
const node = nodeList[i];
|
const node = nodeList[i];
|
||||||
favicons.push({
|
favicons.push({
|
||||||
@ -872,6 +863,18 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||||||
func addToRecentlyVisited() {
|
func addToRecentlyVisited() {
|
||||||
self.addToRecentsWhenReady = true
|
self.addToRecentsWhenReady = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeContentSnapshotView() -> UIView? {
|
||||||
|
let configuration = WKSnapshotConfiguration()
|
||||||
|
configuration.rect = CGRect(origin: .zero, size: self.webView.frame.size)
|
||||||
|
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.frame = CGRect(origin: .zero, size: self.webView.frame.size)
|
||||||
|
self.webView.takeSnapshot(with: configuration, completionHandler: { image, _ in
|
||||||
|
imageView.image = image
|
||||||
|
})
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ErrorComponent: CombinedComponent {
|
private final class ErrorComponent: CombinedComponent {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ public extension MediaEditorScreen {
|
|||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
storyItem: EngineStoryItem,
|
storyItem: EngineStoryItem,
|
||||||
videoPlaybackPosition: Double?,
|
videoPlaybackPosition: Double?,
|
||||||
|
cover: Bool,
|
||||||
repost: Bool,
|
repost: Bool,
|
||||||
transitionIn: MediaEditorScreen.TransitionIn,
|
transitionIn: MediaEditorScreen.TransitionIn,
|
||||||
transitionOut: MediaEditorScreen.TransitionOut?,
|
transitionOut: MediaEditorScreen.TransitionOut?,
|
||||||
@ -88,6 +89,7 @@ public extension MediaEditorScreen {
|
|||||||
mode: .storyEditor,
|
mode: .storyEditor,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
isEditing: !repost,
|
isEditing: !repost,
|
||||||
|
isEditingCover: cover,
|
||||||
forwardSource: repost ? (peer, storyItem) : nil,
|
forwardSource: repost ? (peer, storyItem) : nil,
|
||||||
initialCaption: initialCaption,
|
initialCaption: initialCaption,
|
||||||
initialPrivacy: initialPrivacy,
|
initialPrivacy: initialPrivacy,
|
||||||
|
|||||||
@ -18,11 +18,11 @@ private final class MediaCoverScreenComponent: Component {
|
|||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let mediaEditor: MediaEditor
|
let mediaEditor: Signal<MediaEditor?, NoError>
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
mediaEditor: MediaEditor
|
mediaEditor: Signal<MediaEditor?, NoError>
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.mediaEditor = mediaEditor
|
self.mediaEditor = mediaEditor
|
||||||
@ -57,9 +57,17 @@ private final class MediaCoverScreenComponent: Component {
|
|||||||
var playerStateDisposable: Disposable?
|
var playerStateDisposable: Disposable?
|
||||||
var playerState: MediaEditorPlayerState?
|
var playerState: MediaEditorPlayerState?
|
||||||
|
|
||||||
init(mediaEditor: MediaEditor) {
|
private(set) var mediaEditor: MediaEditor?
|
||||||
|
|
||||||
|
init(mediaEditor: Signal<MediaEditor?, NoError>) {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
let _ = (mediaEditor
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] mediaEditor in
|
||||||
|
if let self, let mediaEditor {
|
||||||
|
self.mediaEditor = mediaEditor
|
||||||
|
|
||||||
self.playerStateDisposable = (mediaEditor.playerState(framesCount: 16)
|
self.playerStateDisposable = (mediaEditor.playerState(framesCount: 16)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] playerState in
|
|> deliverOnMainQueue).start(next: { [weak self] playerState in
|
||||||
if let self {
|
if let self {
|
||||||
@ -70,6 +78,8 @@ private final class MediaCoverScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.playerStateDisposable?.dispose()
|
self.playerStateDisposable?.dispose()
|
||||||
@ -258,7 +268,7 @@ private final class MediaCoverScreenComponent: Component {
|
|||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
displaysProgress: false,
|
displaysProgress: false,
|
||||||
action: { [weak controller, weak self] in
|
action: { [weak controller, weak self] in
|
||||||
if let playerState = self?.state?.playerState, let mediaEditor = self?.component?.mediaEditor, let image = mediaEditor.resultImage {
|
if let playerState = self?.state?.playerState, let mediaEditor = self?.state?.mediaEditor, let image = mediaEditor.resultImage {
|
||||||
mediaEditor.setCoverImageTimestamp(playerState.position)
|
mediaEditor.setCoverImageTimestamp(playerState.position)
|
||||||
controller?.completed(playerState.position, image)
|
controller?.completed(playerState.position, image)
|
||||||
}
|
}
|
||||||
@ -318,7 +328,6 @@ private final class MediaCoverScreenComponent: Component {
|
|||||||
if let playerState = state.playerState {
|
if let playerState = state.playerState {
|
||||||
let visibleTracks = playerState.tracks.filter { $0.id == 0 }.map { MediaScrubberComponent.Track($0) }
|
let visibleTracks = playerState.tracks.filter { $0.id == 0 }.map { MediaScrubberComponent.Track($0) }
|
||||||
|
|
||||||
let mediaEditor = component.mediaEditor
|
|
||||||
let scrubberInset: CGFloat = buttonSideInset
|
let scrubberInset: CGFloat = buttonSideInset
|
||||||
let scrubberSize = self.scrubber.update(
|
let scrubberSize = self.scrubber.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
@ -333,13 +342,13 @@ private final class MediaCoverScreenComponent: Component {
|
|||||||
isPlaying: playerState.isPlaying,
|
isPlaying: playerState.isPlaying,
|
||||||
tracks: visibleTracks,
|
tracks: visibleTracks,
|
||||||
portalView: controller.portalView,
|
portalView: controller.portalView,
|
||||||
positionUpdated: { [weak mediaEditor] position, apply in
|
positionUpdated: { [weak state] position, apply in
|
||||||
if let mediaEditor {
|
if let mediaEditor = state?.mediaEditor {
|
||||||
mediaEditor.seek(position, andPlay: false)
|
mediaEditor.seek(position, andPlay: false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
coverPositionUpdated: { [weak mediaEditor] position, tap, commit in
|
coverPositionUpdated: { [weak state] position, tap, commit in
|
||||||
if let mediaEditor {
|
if let mediaEditor = state?.mediaEditor {
|
||||||
if tap {
|
if tap {
|
||||||
mediaEditor.setOnNextDisplay {
|
mediaEditor.setOnNextDisplay {
|
||||||
commit()
|
commit()
|
||||||
@ -436,7 +445,7 @@ final class MediaCoverScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateOutToEditor(completion: @escaping () -> Void) {
|
func animateOutToEditor(completion: @escaping () -> Void) {
|
||||||
if let mediaEditor = self.controller?.mediaEditor {
|
self.controller?.withMediaEditor { mediaEditor in
|
||||||
mediaEditor.play()
|
mediaEditor.play()
|
||||||
}
|
}
|
||||||
if let view = self.componentHost.view as? MediaCoverScreenComponent.View {
|
if let view = self.componentHost.view as? MediaCoverScreenComponent.View {
|
||||||
@ -534,18 +543,26 @@ final class MediaCoverScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate let context: AccountContext
|
fileprivate let context: AccountContext
|
||||||
fileprivate let mediaEditor: MediaEditor
|
fileprivate let mediaEditor: Signal<MediaEditor?, NoError>
|
||||||
fileprivate let previewView: MediaEditorPreviewView
|
fileprivate let previewView: MediaEditorPreviewView
|
||||||
fileprivate let portalView: PortalView
|
fileprivate let portalView: PortalView
|
||||||
|
|
||||||
|
func withMediaEditor(_ f: @escaping (MediaEditor) -> Void) {
|
||||||
|
let _ = (self.mediaEditor
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { mediaEditor in
|
||||||
|
if let mediaEditor {
|
||||||
|
f(mediaEditor)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var completed: (Double, UIImage) -> Void = { _, _ in }
|
var completed: (Double, UIImage) -> Void = { _, _ in }
|
||||||
var dismissed: () -> Void = {}
|
var dismissed: () -> Void = {}
|
||||||
|
|
||||||
private var initialValues: MediaEditorValues
|
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
mediaEditor: MediaEditor,
|
mediaEditor: Signal<MediaEditor?, NoError>,
|
||||||
previewView: MediaEditorPreviewView,
|
previewView: MediaEditorPreviewView,
|
||||||
portalView: PortalView
|
portalView: PortalView
|
||||||
) {
|
) {
|
||||||
@ -553,7 +570,6 @@ final class MediaCoverScreen: ViewController {
|
|||||||
self.mediaEditor = mediaEditor
|
self.mediaEditor = mediaEditor
|
||||||
self.previewView = previewView
|
self.previewView = previewView
|
||||||
self.portalView = portalView
|
self.portalView = portalView
|
||||||
self.initialValues = mediaEditor.values.makeCopy()
|
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
self.navigationPresentation = .flatModal
|
self.navigationPresentation = .flatModal
|
||||||
@ -562,12 +578,14 @@ final class MediaCoverScreen: ViewController {
|
|||||||
|
|
||||||
self.statusBar.statusBarStyle = .White
|
self.statusBar.statusBarStyle = .White
|
||||||
|
|
||||||
|
self.withMediaEditor { mediaEditor in
|
||||||
if let coverImageTimestamp = mediaEditor.values.coverImageTimestamp {
|
if let coverImageTimestamp = mediaEditor.values.coverImageTimestamp {
|
||||||
mediaEditor.seek(coverImageTimestamp, andPlay: false)
|
mediaEditor.seek(coverImageTimestamp, andPlay: false)
|
||||||
} else {
|
} else {
|
||||||
mediaEditor.seek(mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, andPlay: false)
|
mediaEditor.seek(mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, andPlay: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
|||||||
@ -732,7 +732,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
transition = transition.withUserData(nextTransitionUserData)
|
transition = transition.withUserData(nextTransitionUserData)
|
||||||
}
|
}
|
||||||
|
|
||||||
let isEditingStory = controller.isEditingStory
|
let isEditingStory = controller.isEditingStory || controller.isEditingStoryCover
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
if let initialCaption = controller.initialCaption {
|
if let initialCaption = controller.initialCaption {
|
||||||
self.inputPanelExternalState.initialText = initialCaption
|
self.inputPanelExternalState.initialText = initialCaption
|
||||||
@ -2807,6 +2807,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.availableReactions = reactions
|
self.availableReactions = reactions
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if controller.isEditingStoryCover {
|
||||||
|
self.openCoverSelection(immediate: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -4563,44 +4567,31 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
controller.push(linkController)
|
controller.push(linkController)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addInitialLink(_ link: String) {
|
func addInitialLink(_ link: (url: String, name: String?)) {
|
||||||
guard self.context.isPremium else {
|
guard self.context.isPremium else {
|
||||||
return
|
Queue.mainQueue().after(0.3) {
|
||||||
}
|
let context = self.context
|
||||||
let text = link
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
|
let demoController = context.sharedContext.makePremiumDemoController(context: context, subject: .stories, forceDark: true, action: {
|
||||||
var attributes: [MessageAttribute] = []
|
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesLinks, forceDark: true, dismissed: {})
|
||||||
attributes.append(TextEntitiesMessageAttribute(entities: [.init(range: 0 ..< (text as NSString).length, type: .Url)]))
|
replaceImpl?(controller)
|
||||||
|
}, dismissed: {})
|
||||||
// attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !self.positionBelowText, forceLargeMedia: self.largeMedia, isManuallyAdded: false, isSafe: true))
|
replaceImpl = { [weak self, weak demoController] c in
|
||||||
|
demoController?.dismiss(animated: true, completion: {
|
||||||
let effectiveMedia: TelegramMediaWebpage? = nil
|
|
||||||
// if let webpage = self.webpage, case .Loaded = webpage.content {
|
|
||||||
// effectiveMedia = webpage
|
|
||||||
// }
|
|
||||||
|
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1))
|
|
||||||
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: text, attributes: attributes, media: effectiveMedia.flatMap { [$0] } ?? [], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
|
|
||||||
|
|
||||||
let renderer = DrawingMessageRenderer(context: self.context, messages: [message], parentView: self.view, isLink: true)
|
|
||||||
renderer.render(completion: { [weak self] renderResult in
|
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let result = CreateLinkScreen.Result(
|
self.controller?.push(c)
|
||||||
url: link,
|
|
||||||
name: "",
|
|
||||||
webpage: effectiveMedia,
|
|
||||||
positionBelowText: false,
|
|
||||||
largeMedia: nil,
|
|
||||||
image: effectiveMedia != nil ? renderResult.dayImage : nil,
|
|
||||||
nightImage: effectiveMedia != nil ? renderResult.nightImage : nil
|
|
||||||
)
|
|
||||||
|
|
||||||
let entity = DrawingLinkEntity(url: result.url, name: result.name, webpage: result.webpage, positionBelowText: result.positionBelowText, largeMedia: result.largeMedia, style: .white)
|
|
||||||
self.interaction?.insertEntity(entity, position: CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.width / 3.0 * 4.0), select: false)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
self.controller?.push(demoController)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let entity = DrawingLinkEntity(url: link.url, name: link.name ?? "", webpage: nil, positionBelowText: false, largeMedia: nil, style: .white)
|
||||||
|
self.interaction?.insertEntity(entity, position: CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.width / 3.0 * 4.0), select: false)
|
||||||
|
}
|
||||||
|
|
||||||
func addReaction() {
|
func addReaction() {
|
||||||
guard let controller = self.controller else {
|
guard let controller = self.controller else {
|
||||||
@ -4643,7 +4634,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let weatherPromise = Promise<StickerPickerScreen.Weather>()
|
let weatherPromise = Promise<StickerPickerScreen.Weather>()
|
||||||
weatherPromise.set(getWeather(context: self.context))
|
weatherPromise.set(getWeather(context: self.context, load: true))
|
||||||
self.weatherPromise = weatherPromise
|
self.weatherPromise = weatherPromise
|
||||||
|
|
||||||
let _ = (weatherPromise.get()
|
let _ = (weatherPromise.get()
|
||||||
@ -4696,7 +4687,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
guard controller.checkCaptionLimit() else {
|
guard controller.checkCaptionLimit() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if controller.isEditingStory {
|
if controller.isEditingStory || controller.isEditingStoryCover {
|
||||||
controller.requestStoryCompletion(animated: true)
|
controller.requestStoryCompletion(animated: true)
|
||||||
} else {
|
} else {
|
||||||
if controller.checkIfCompletionIsAllowed() {
|
if controller.checkIfCompletionIsAllowed() {
|
||||||
@ -4713,11 +4704,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openCoverSelection() {
|
func openCoverSelection(immediate: Bool) {
|
||||||
guard let mediaEditor = self.mediaEditor else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let portalView = PortalView(matchPosition: false) else {
|
guard let portalView = PortalView(matchPosition: false) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -4732,7 +4719,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
let coverController = MediaCoverScreen(
|
let coverController = MediaCoverScreen(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
mediaEditor: mediaEditor,
|
mediaEditor: self.mediaEditorPromise.get(),
|
||||||
previewView: self.previewView,
|
previewView: self.previewView,
|
||||||
portalView: portalView
|
portalView: portalView
|
||||||
)
|
)
|
||||||
@ -4749,8 +4736,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
self.controller?.present(coverController, in: .current)
|
self.controller?.present(coverController, in: .current)
|
||||||
self.coverScreen = coverController
|
self.coverScreen = coverController
|
||||||
|
|
||||||
|
if immediate {
|
||||||
|
self.isDisplayingTool = .cover
|
||||||
|
self.requestUpdate(transition: .immediate)
|
||||||
|
} else {
|
||||||
self.animateOutToTool(tool: .cover)
|
self.animateOutToTool(tool: .cover)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateModalTransitionFactor(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
func updateModalTransitionFactor(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
guard let layout = self.validLayout, case .compact = layout.metrics.widthClass else {
|
guard let layout = self.validLayout, case .compact = layout.metrics.widthClass else {
|
||||||
@ -4933,6 +4926,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let editorConfiguration = MediaEditorConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
var weatherSignal: Signal<StickerPickerScreen.Weather, NoError>
|
var weatherSignal: Signal<StickerPickerScreen.Weather, NoError>
|
||||||
if hasInteractiveStickers {
|
if hasInteractiveStickers {
|
||||||
let weatherPromise: Promise<StickerPickerScreen.Weather>
|
let weatherPromise: Promise<StickerPickerScreen.Weather>
|
||||||
@ -4940,7 +4935,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
weatherPromise = current
|
weatherPromise = current
|
||||||
} else {
|
} else {
|
||||||
weatherPromise = Promise()
|
weatherPromise = Promise()
|
||||||
weatherPromise.set(getWeather(context: self.context))
|
weatherPromise.set(getWeather(context: self.context, load: editorConfiguration.preloadWeather))
|
||||||
self.weatherPromise = weatherPromise
|
self.weatherPromise = weatherPromise
|
||||||
}
|
}
|
||||||
weatherSignal = weatherPromise.get()
|
weatherSignal = weatherPromise.get()
|
||||||
@ -5028,6 +5023,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
switch result {
|
switch result {
|
||||||
case let .loaded(weather):
|
case let .loaded(weather):
|
||||||
self.addWeather(weather)
|
self.addWeather(weather)
|
||||||
|
case .notPreloaded:
|
||||||
|
weatherPromise.set(getWeather(context: self.context, load: true))
|
||||||
|
let _ = (weatherPromise.get()
|
||||||
|
|> take(1)).start(next: { [weak self] result in
|
||||||
|
if let self, case let .loaded(weather) = result {
|
||||||
|
self.addWeather(weather)
|
||||||
|
}
|
||||||
|
})
|
||||||
case .notDetermined, .notAllowed:
|
case .notDetermined, .notAllowed:
|
||||||
self.presentLocationAccessAlert()
|
self.presentLocationAccessAlert()
|
||||||
default:
|
default:
|
||||||
@ -5223,7 +5226,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.controller?.present(controller, in: .window(.root))
|
self.controller?.present(controller, in: .window(.root))
|
||||||
self.animateOutToTool(tool: .tools)
|
self.animateOutToTool(tool: .tools)
|
||||||
case .cover:
|
case .cover:
|
||||||
self.openCoverSelection()
|
self.openCoverSelection(immediate: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -5571,6 +5574,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let mode: Mode
|
let mode: Mode
|
||||||
let subject: Signal<Subject?, NoError>
|
let subject: Signal<Subject?, NoError>
|
||||||
let isEditingStory: Bool
|
let isEditingStory: Bool
|
||||||
|
let isEditingStoryCover: Bool
|
||||||
fileprivate let customTarget: EnginePeer.Id?
|
fileprivate let customTarget: EnginePeer.Id?
|
||||||
let forwardSource: (EnginePeer, EngineStoryItem)?
|
let forwardSource: (EnginePeer, EngineStoryItem)?
|
||||||
|
|
||||||
@ -5578,7 +5582,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
fileprivate let initialPrivacy: EngineStoryPrivacy?
|
fileprivate let initialPrivacy: EngineStoryPrivacy?
|
||||||
fileprivate let initialMediaAreas: [MediaArea]?
|
fileprivate let initialMediaAreas: [MediaArea]?
|
||||||
fileprivate let initialVideoPosition: Double?
|
fileprivate let initialVideoPosition: Double?
|
||||||
fileprivate let initialLink: String?
|
fileprivate let initialLink: (url: String, name: String?)?
|
||||||
|
|
||||||
fileprivate let transitionIn: TransitionIn?
|
fileprivate let transitionIn: TransitionIn?
|
||||||
fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut?
|
fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut?
|
||||||
@ -5608,12 +5612,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
subject: Signal<Subject?, NoError>,
|
subject: Signal<Subject?, NoError>,
|
||||||
customTarget: EnginePeer.Id? = nil,
|
customTarget: EnginePeer.Id? = nil,
|
||||||
isEditing: Bool = false,
|
isEditing: Bool = false,
|
||||||
|
isEditingCover: Bool = false,
|
||||||
forwardSource: (EnginePeer, EngineStoryItem)? = nil,
|
forwardSource: (EnginePeer, EngineStoryItem)? = nil,
|
||||||
initialCaption: NSAttributedString? = nil,
|
initialCaption: NSAttributedString? = nil,
|
||||||
initialPrivacy: EngineStoryPrivacy? = nil,
|
initialPrivacy: EngineStoryPrivacy? = nil,
|
||||||
initialMediaAreas: [MediaArea]? = nil,
|
initialMediaAreas: [MediaArea]? = nil,
|
||||||
initialVideoPosition: Double? = nil,
|
initialVideoPosition: Double? = nil,
|
||||||
initialLink: String? = nil,
|
initialLink: (url: String, name: String?)? = nil,
|
||||||
transitionIn: TransitionIn?,
|
transitionIn: TransitionIn?,
|
||||||
transitionOut: @escaping (Bool, Bool?) -> TransitionOut?,
|
transitionOut: @escaping (Bool, Bool?) -> TransitionOut?,
|
||||||
completion: @escaping (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
completion: @escaping (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||||
@ -5623,6 +5628,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.customTarget = customTarget
|
self.customTarget = customTarget
|
||||||
self.isEditingStory = isEditing
|
self.isEditingStory = isEditing
|
||||||
|
self.isEditingStoryCover = isEditingCover
|
||||||
self.forwardSource = forwardSource
|
self.forwardSource = forwardSource
|
||||||
self.initialCaption = initialCaption
|
self.initialCaption = initialCaption
|
||||||
self.initialPrivacy = initialPrivacy
|
self.initialPrivacy = initialPrivacy
|
||||||
@ -5799,7 +5805,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var isEmbeddedEditor: Bool {
|
fileprivate var isEmbeddedEditor: Bool {
|
||||||
return self.isEditingStory || self.forwardSource != nil
|
return self.isEditingStory || self.isEditingStoryCover || self.forwardSource != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentCoverImage: UIImage?
|
private var currentCoverImage: UIImage?
|
||||||
@ -5917,7 +5923,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
editCoverImpl = { [weak self, weak controller] in
|
editCoverImpl = { [weak self, weak controller] in
|
||||||
if let self {
|
if let self {
|
||||||
self.node.openCoverSelection()
|
self.node.openCoverSelection(immediate: false)
|
||||||
}
|
}
|
||||||
if let controller {
|
if let controller {
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
@ -6478,7 +6484,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.isEditingStory {
|
if !(self.isEditingStory || self.isEditingStoryCover) {
|
||||||
let privacy = self.state.privacy
|
let privacy = self.state.privacy
|
||||||
let _ = updateMediaEditorStoredStateInteractively(engine: self.context.engine, { current in
|
let _ = updateMediaEditorStoredStateInteractively(engine: self.context.engine, { current in
|
||||||
if let current {
|
if let current {
|
||||||
@ -8283,3 +8289,27 @@ private func stickerFile(resource: TelegramMediaResource, thumbnailResource: Tel
|
|||||||
|
|
||||||
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: isVideo ? "video/webm" : "image/webp", size: size, attributes: fileAttributes)
|
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: isVideo ? "video/webm" : "image/webp", size: size, attributes: fileAttributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct MediaEditorConfiguration {
|
||||||
|
static var defaultValue: MediaEditorConfiguration {
|
||||||
|
return MediaEditorConfiguration(preloadWeather: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
let preloadWeather: Bool
|
||||||
|
|
||||||
|
fileprivate init(preloadWeather: Bool) {
|
||||||
|
self.preloadWeather = preloadWeather
|
||||||
|
}
|
||||||
|
|
||||||
|
static func with(appConfiguration: AppConfiguration) -> MediaEditorConfiguration {
|
||||||
|
if let data = appConfiguration.data {
|
||||||
|
var preloadWeather = false
|
||||||
|
if let value = data["story_weather_preload"] as? Bool {
|
||||||
|
preloadWeather = value
|
||||||
|
}
|
||||||
|
return MediaEditorConfiguration(preloadWeather: preloadWeather)
|
||||||
|
} else {
|
||||||
|
return .defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ private func getWeatherData(context: AccountContext, location: CLLocationCoordin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWeather(context: AccountContext) -> Signal<StickerPickerScreen.Weather, NoError> {
|
func getWeather(context: AccountContext, load: Bool) -> Signal<StickerPickerScreen.Weather, NoError> {
|
||||||
guard let locationManager = context.sharedContext.locationManager else {
|
guard let locationManager = context.sharedContext.locationManager else {
|
||||||
return .single(.none)
|
return .single(.none)
|
||||||
}
|
}
|
||||||
@ -60,6 +60,7 @@ func getWeather(context: AccountContext) -> Signal<StickerPickerScreen.Weather,
|
|||||||
case .denied, .restricted, .unreachable:
|
case .denied, .restricted, .unreachable:
|
||||||
return .single(.notAllowed)
|
return .single(.notAllowed)
|
||||||
case .allowed:
|
case .allowed:
|
||||||
|
if load {
|
||||||
return .single(.fetching)
|
return .single(.fetching)
|
||||||
|> then(
|
|> then(
|
||||||
currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0)
|
currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0)
|
||||||
@ -87,6 +88,9 @@ func getWeather(context: AccountContext) -> Signal<StickerPickerScreen.Weather,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
return .single(.notPreloaded)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -616,8 +616,6 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// self.scrollView.contentOffset = CGPoint(x: 0.0, y: max(0.0, self.scrollView.contentSize.height - self.scrollView.bounds.height))
|
|
||||||
|
|
||||||
if self.items.count == 1, let item = self.items.first {
|
if self.items.count == 1, let item = self.items.first {
|
||||||
if let navigationController = self.navigationController {
|
if let navigationController = self.navigationController {
|
||||||
item.beforeMaximize(navigationController, { [weak self] in
|
item.beforeMaximize(navigationController, { [weak self] in
|
||||||
@ -625,6 +623,12 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let contentOffset = max(0.0, self.scrollView.contentSize.height - self.scrollView.bounds.height)
|
||||||
|
self.scrollView.contentOffset = CGPoint(x: 0.0, y: contentOffset)
|
||||||
|
for itemNode in self.itemNodes.values {
|
||||||
|
itemNode.frame = itemNode.frame.offsetBy(dx: 0.0, dy: contentOffset)
|
||||||
|
}
|
||||||
|
|
||||||
self.isExpanded = true
|
self.isExpanded = true
|
||||||
self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring))
|
self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring))
|
||||||
}
|
}
|
||||||
@ -646,7 +650,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
if scrollView.contentOffset.y < -64.0, let lastItemId = self.items.last?.id, let itemNode = self.itemNodes[lastItemId] {
|
if scrollView.contentOffset.y < -64.0, let lastItemId = self.items.last?.id, let itemNode = self.itemNodes[lastItemId] {
|
||||||
let velocity = scrollView.panGestureRecognizer.velocity(in: self.view).y
|
let velocity = scrollView.panGestureRecognizer.velocity(in: self.view).y
|
||||||
let distance = layout.size.height - self.collapsedHeight(layout: layout) - itemNode.frame.minY
|
let distance = layout.size.height - self.collapsedHeight(layout: layout) - itemNode.frame.minY
|
||||||
let initialVelocity = distance != 0.0 ? abs(velocity / distance) : 0.0
|
let initialVelocity = min(8.0, distance != 0.0 ? abs(velocity / distance) : 0.0)
|
||||||
|
|
||||||
self.isExpanded = false
|
self.isExpanded = false
|
||||||
scrollView.isScrollEnabled = false
|
scrollView.isScrollEnabled = false
|
||||||
@ -747,6 +751,10 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
|
|
||||||
var index = 0
|
var index = 0
|
||||||
let contentHeight = frameForIndex(index: self.items.count - 1, size: layout.size, insets: itemInsets, itemCount: self.items.count, boundingSize: layout.size).midY - 70.0
|
let contentHeight = frameForIndex(index: self.items.count - 1, size: layout.size, insets: itemInsets, itemCount: self.items.count, boundingSize: layout.size).midY - 70.0
|
||||||
|
|
||||||
|
var effectiveScrollBounds = self.scrollView.bounds
|
||||||
|
effectiveScrollBounds.origin.y = max(0.0, min(contentHeight - self.scrollView.bounds.height, effectiveScrollBounds.origin.y))
|
||||||
|
|
||||||
for item in self.items {
|
for item in self.items {
|
||||||
if let currentTransition = self.currentTransition {
|
if let currentTransition = self.currentTransition {
|
||||||
if currentTransition.matches(item: item) {
|
if currentTransition.matches(item: item) {
|
||||||
@ -859,14 +867,14 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
|
|
||||||
if self.isExpanded {
|
if self.isExpanded {
|
||||||
let currentItemFrame = frameForIndex(index: index, size: layout.size, insets: itemInsets, itemCount: self.items.count, boundingSize: layout.size)
|
let currentItemFrame = frameForIndex(index: index, size: layout.size, insets: itemInsets, itemCount: self.items.count, boundingSize: layout.size)
|
||||||
let currentItemTransform = final3dTransform(for: currentItemFrame.minY, size: currentItemFrame.size, contentHeight: contentHeight, itemCount: self.items.count, additionalAngle: self.highlightedItemId == item.id ? 0.04 : nil, scrollBounds: self.scrollView.bounds, insets: itemInsets)
|
let currentItemTransform = final3dTransform(for: currentItemFrame.minY, size: currentItemFrame.size, contentHeight: contentHeight, itemCount: self.items.count, additionalAngle: self.highlightedItemId == item.id ? 0.04 : nil, scrollBounds: effectiveScrollBounds, insets: itemInsets)
|
||||||
|
|
||||||
var effectiveItemFrame = currentItemFrame
|
var effectiveItemFrame = currentItemFrame
|
||||||
var effectiveItemTransform = currentItemTransform
|
let effectiveItemTransform = currentItemTransform
|
||||||
|
|
||||||
if let dismissingItemId = self.dismissingItemId, let deletingIndex = self.items.firstIndex(where: { $0.id == dismissingItemId }), let offset = self.dismissingItemOffset {
|
if let dismissingItemId = self.dismissingItemId, let deletingIndex = self.items.firstIndex(where: { $0.id == dismissingItemId }), let offset = self.dismissingItemOffset {
|
||||||
var targetItemFrame: CGRect?
|
// var targetItemFrame: CGRect?
|
||||||
var targetItemTransform: CATransform3D?
|
// var targetItemTransform: CATransform3D?
|
||||||
if deletingIndex == index {
|
if deletingIndex == index {
|
||||||
let effectiveOffset: CGFloat
|
let effectiveOffset: CGFloat
|
||||||
if offset <= 0.0 {
|
if offset <= 0.0 {
|
||||||
@ -875,25 +883,26 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
effectiveOffset = scrollingRubberBandingOffset(offset: offset, bandingStart: 0.0, range: 20.0)
|
effectiveOffset = scrollingRubberBandingOffset(offset: offset, bandingStart: 0.0, range: 20.0)
|
||||||
}
|
}
|
||||||
effectiveItemFrame = effectiveItemFrame.offsetBy(dx: effectiveOffset, dy: 0.0)
|
effectiveItemFrame = effectiveItemFrame.offsetBy(dx: effectiveOffset, dy: 0.0)
|
||||||
} else if index < deletingIndex {
|
|
||||||
let frame = frameForIndex(index: index, size: layout.size, insets: itemInsets, itemCount: self.items.count - 1, boundingSize: layout.size)
|
|
||||||
let spacing = interitemSpacing(itemCount: self.items.count - 1, boundingSize: layout.size, insets: itemInsets)
|
|
||||||
|
|
||||||
targetItemFrame = frame
|
|
||||||
targetItemTransform = final3dTransform(for: frame.minY, size: layout.size, contentHeight: contentHeight - layout.size.height - spacing, itemCount: self.items.count - 1, scrollBounds: self.scrollView.bounds, insets: itemInsets)
|
|
||||||
} else {
|
|
||||||
let frame = frameForIndex(index: index - 1, size: layout.size, insets: itemInsets, itemCount: self.items.count - 1, boundingSize: layout.size)
|
|
||||||
let spacing = interitemSpacing(itemCount: self.items.count - 1, boundingSize: layout.size, insets: itemInsets)
|
|
||||||
|
|
||||||
targetItemFrame = frame
|
|
||||||
targetItemTransform = final3dTransform(for: frame.minY, size: layout.size, contentHeight: contentHeight - layout.size.height - spacing, itemCount: self.items.count - 1, scrollBounds: self.scrollView.bounds, insets: itemInsets)
|
|
||||||
}
|
}
|
||||||
|
// else if index < deletingIndex {
|
||||||
|
// let frame = frameForIndex(index: index, size: layout.size, insets: itemInsets, itemCount: self.items.count - 1, boundingSize: layout.size)
|
||||||
|
// let spacing = interitemSpacing(itemCount: self.items.count - 1, boundingSize: layout.size, insets: itemInsets)
|
||||||
|
//
|
||||||
|
// targetItemFrame = frame
|
||||||
|
// targetItemTransform = final3dTransform(for: frame.minY, size: layout.size, contentHeight: contentHeight - layout.size.height - spacing, itemCount: self.items.count - 1, scrollBounds: self.scrollView.bounds, insets: itemInsets)
|
||||||
|
// } else {
|
||||||
|
// let frame = frameForIndex(index: index - 1, size: layout.size, insets: itemInsets, itemCount: self.items.count - 1, boundingSize: layout.size)
|
||||||
|
// let spacing = interitemSpacing(itemCount: self.items.count - 1, boundingSize: layout.size, insets: itemInsets)
|
||||||
|
//
|
||||||
|
// targetItemFrame = frame
|
||||||
|
// targetItemTransform = final3dTransform(for: frame.minY, size: layout.size, contentHeight: contentHeight - layout.size.height - spacing, itemCount: self.items.count - 1, scrollBounds: self.scrollView.bounds, insets: itemInsets)
|
||||||
|
// }
|
||||||
|
|
||||||
if let targetItemFrame, let targetItemTransform {
|
// if let targetItemFrame, let targetItemTransform {
|
||||||
let fraction = max(0.0, min(1.0, -1.0 * offset / (layout.size.width * 1.5)))
|
// let fraction = max(0.0, min(1.0, -1.0 * offset / (layout.size.width * 1.5)))
|
||||||
effectiveItemFrame = effectiveItemFrame.interpolate(with: targetItemFrame, fraction: fraction)
|
// effectiveItemFrame = effectiveItemFrame.interpolate(with: targetItemFrame, fraction: fraction)
|
||||||
effectiveItemTransform = effectiveItemTransform.interpolate(with: targetItemTransform, fraction: fraction)
|
// effectiveItemTransform = effectiveItemTransform.interpolate(with: targetItemTransform, fraction: fraction)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
itemFrame = effectiveItemFrame
|
itemFrame = effectiveItemFrame
|
||||||
itemTransform = effectiveItemTransform
|
itemTransform = effectiveItemTransform
|
||||||
@ -904,6 +913,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
var hideTransform = false
|
var hideTransform = false
|
||||||
if let currentTransition = self.currentTransition {
|
if let currentTransition = self.currentTransition {
|
||||||
if case let .maximize(itemId) = currentTransition {
|
if case let .maximize(itemId) = currentTransition {
|
||||||
|
itemOffset += self.scrollView.bounds.origin.y
|
||||||
|
|
||||||
itemOffset += layout.size.height * 0.25
|
itemOffset += layout.size.height * 0.25
|
||||||
if let lastItemNode = self.scrollView.subviews.last?.asyncdisplaykit_node as? ItemNode, lastItemNode.item.id == itemId {
|
if let lastItemNode = self.scrollView.subviews.last?.asyncdisplaykit_node as? ItemNode, lastItemNode.item.id == itemId {
|
||||||
hideTransform = true
|
hideTransform = true
|
||||||
@ -954,7 +965,16 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
|
|
||||||
let contentSize = CGSize(width: layout.size.width, height: contentHeight)
|
let contentSize = CGSize(width: layout.size.width, height: contentHeight)
|
||||||
if self.scrollView.contentSize != contentSize {
|
if self.scrollView.contentSize != contentSize {
|
||||||
|
var contentSizeDelta: CGFloat?
|
||||||
|
if contentSize.height < self.scrollView.contentSize.height, transition.isAnimated {
|
||||||
|
let currentContentOffset = self.scrollView.contentOffset.y
|
||||||
|
let updatedContentOffset = max(0.0, contentSize.height - self.scrollView.bounds.height)
|
||||||
|
contentSizeDelta = currentContentOffset - updatedContentOffset
|
||||||
|
}
|
||||||
self.scrollView.contentSize = contentSize
|
self.scrollView.contentSize = contentSize
|
||||||
|
if let contentSizeDelta {
|
||||||
|
transition.animateBounds(layer: self.scrollView.layer, from: CGRect(origin: CGPoint(x: 0.0, y: self.scrollView.contentOffset.y + contentSizeDelta), size: self.scrollView.bounds.size))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.scrollView.frame != bounds {
|
if self.scrollView.frame != bounds {
|
||||||
self.scrollView.frame = bounds
|
self.scrollView.frame = bounds
|
||||||
|
|||||||
@ -77,8 +77,12 @@ final class MinimizedHeaderNode: ASDisplayNode {
|
|||||||
if titles.count == 1, let title = titles.first {
|
if titles.count == 1, let title = titles.first {
|
||||||
self.title = title
|
self.title = title
|
||||||
} else if let title = titles.last {
|
} else if let title = titles.last {
|
||||||
|
var trimmedTitle = title
|
||||||
|
if trimmedTitle.count > 20 {
|
||||||
|
trimmedTitle = "\(trimmedTitle.prefix(20).trimmingCharacters(in: .whitespacesAndNewlines))\u{2026}"
|
||||||
|
}
|
||||||
let othersString = self.strings.WebApp_MinimizedTitle_Others(Int32(titles.count - 1))
|
let othersString = self.strings.WebApp_MinimizedTitle_Others(Int32(titles.count - 1))
|
||||||
self.title = self.strings.WebApp_MinimizedTitleFormat(title, othersString).string
|
self.title = self.strings.WebApp_MinimizedTitleFormat(trimmedTitle, othersString).string
|
||||||
} else {
|
} else {
|
||||||
self.title = nil
|
self.title = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2541,6 +2541,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
|||||||
peer: peer,
|
peer: peer,
|
||||||
storyItem: item,
|
storyItem: item,
|
||||||
videoPlaybackPosition: nil,
|
videoPlaybackPosition: nil,
|
||||||
|
cover: false,
|
||||||
repost: false,
|
repost: false,
|
||||||
transitionIn: .gallery(MediaEditorScreen.TransitionIn.GalleryTransitionIn(sourceView: self.itemGrid.view, sourceRect: foundItemLayer?.frame ?? .zero, sourceImage: sourceImage)),
|
transitionIn: .gallery(MediaEditorScreen.TransitionIn.GalleryTransitionIn(sourceView: self.itemGrid.view, sourceRect: foundItemLayer?.frame ?? .zero, sourceImage: sourceImage)),
|
||||||
transitionOut: MediaEditorScreen.TransitionOut(destinationView: self.itemGrid.view, destinationRect: foundItemLayer?.frame ?? .zero, destinationCornerRadius: 0.0),
|
transitionOut: MediaEditorScreen.TransitionOut(destinationView: self.itemGrid.view, destinationRect: foundItemLayer?.frame ?? .zero, destinationCornerRadius: 0.0),
|
||||||
|
|||||||
@ -2067,6 +2067,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
case none
|
case none
|
||||||
case notDetermined
|
case notDetermined
|
||||||
case notAllowed
|
case notAllowed
|
||||||
|
case notPreloaded
|
||||||
case fetching
|
case fetching
|
||||||
case loaded(StickerPickerScreen.Weather.LoadedWeather)
|
case loaded(StickerPickerScreen.Weather.LoadedWeather)
|
||||||
}
|
}
|
||||||
@ -2727,7 +2728,7 @@ final class StoryStickersContentView: UIView, EmojiCustomContentView {
|
|||||||
|
|
||||||
let weatherButtonContent: AnyComponent<Empty>
|
let weatherButtonContent: AnyComponent<Empty>
|
||||||
switch self.weather {
|
switch self.weather {
|
||||||
case .notAllowed, .notDetermined:
|
case .notAllowed, .notDetermined, .notPreloaded:
|
||||||
weatherButtonContent = AnyComponent(
|
weatherButtonContent = AnyComponent(
|
||||||
InteractiveStickerButtonContent(
|
InteractiveStickerButtonContent(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
|
|||||||
@ -5340,7 +5340,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let updateDisposable = MetaDisposable()
|
private let updateDisposable = MetaDisposable()
|
||||||
func openStoryEditing(repost: Bool = false) {
|
func openStoryEditing(repost: Bool = false, cover: Bool = false) {
|
||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -5351,14 +5351,25 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
var videoPlaybackPosition: Double?
|
var videoPlaybackPosition: Double?
|
||||||
if let visibleItem = self.visibleItems[component.slice.item.id], let view = visibleItem.view.view as? StoryItemContentComponent.View {
|
if let visibleItem = self.visibleItems[component.slice.item.id], let view = visibleItem.view.view as? StoryItemContentComponent.View {
|
||||||
|
if cover {
|
||||||
|
if case let .file(file) = component.slice.item.storyItem.media {
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .Video(_, _, _, _, coverTime) = attribute {
|
||||||
|
videoPlaybackPosition = coverTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
videoPlaybackPosition = view.videoPlaybackPosition
|
videoPlaybackPosition = view.videoPlaybackPosition
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
guard let controller = MediaEditorScreen.makeEditStoryController(
|
guard let controller = MediaEditorScreen.makeEditStoryController(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
peer: component.slice.effectivePeer,
|
peer: component.slice.effectivePeer,
|
||||||
storyItem: component.slice.item.storyItem,
|
storyItem: component.slice.item.storyItem,
|
||||||
videoPlaybackPosition: videoPlaybackPosition,
|
videoPlaybackPosition: videoPlaybackPosition,
|
||||||
|
cover: cover,
|
||||||
repost: repost,
|
repost: repost,
|
||||||
transitionIn: .noAnimation,
|
transitionIn: .noAnimation,
|
||||||
transitionOut: nil,
|
transitionOut: nil,
|
||||||
@ -6107,6 +6118,19 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
self.openStoryEditing()
|
self.openStoryEditing()
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
if case .file = component.slice.item.storyItem.media {
|
||||||
|
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_EditCover, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { [weak self] _, a in
|
||||||
|
a(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.openStoryEditing(cover: true)
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: component.slice.item.storyItem.isPinned ? component.strings.Story_Context_RemoveFromProfile : component.strings.Story_Context_SaveToProfile, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: component.slice.item.storyItem.isPinned ? component.strings.Story_Context_RemoveFromProfile : component.strings.Story_Context_SaveToProfile, icon: { theme in
|
||||||
@ -6291,6 +6315,19 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
self.openStoryEditing()
|
self.openStoryEditing()
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
if case .file = component.slice.item.storyItem.media {
|
||||||
|
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_EditCover, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { [weak self] _, a in
|
||||||
|
a(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.openStoryEditing(cover: true)
|
||||||
|
})))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !items.isEmpty {
|
if !items.isEmpty {
|
||||||
|
|||||||
@ -128,6 +128,7 @@ import MessageUI
|
|||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
import OwnershipTransferController
|
import OwnershipTransferController
|
||||||
import OldChannelsController
|
import OldChannelsController
|
||||||
|
import BrowserUI
|
||||||
|
|
||||||
public enum ChatControllerPeekActions {
|
public enum ChatControllerPeekActions {
|
||||||
case standard
|
case standard
|
||||||
@ -398,11 +399,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var historyNavigationStack = ChatHistoryNavigationStack()
|
var historyNavigationStack = ChatHistoryNavigationStack()
|
||||||
|
|
||||||
public let canReadHistory = ValuePromise<Bool>(true, ignoreRepeated: true)
|
public let canReadHistory = ValuePromise<Bool>(true, ignoreRepeated: true)
|
||||||
|
public let hasBrowserOrAppInFront = Promise<Bool>(false)
|
||||||
var reminderActivity: NSUserActivity?
|
var reminderActivity: NSUserActivity?
|
||||||
var isReminderActivityEnabled: Bool = false
|
var isReminderActivityEnabled: Bool = false
|
||||||
|
|
||||||
var canReadHistoryValue = false
|
var canReadHistoryValue = false {
|
||||||
|
didSet {
|
||||||
|
self.computedCanReadHistoryPromise.set(self.canReadHistoryValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
var canReadHistoryDisposable: Disposable?
|
var canReadHistoryDisposable: Disposable?
|
||||||
|
var computedCanReadHistoryPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
|
|
||||||
var themeEmoticonAndDarkAppearancePreviewPromise = Promise<(String?, Bool?)>((nil, nil))
|
var themeEmoticonAndDarkAppearancePreviewPromise = Promise<(String?, Bool?)>((nil, nil))
|
||||||
var didSetPresentationData = false
|
var didSetPresentationData = false
|
||||||
@ -6502,8 +6509,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
self.canReadHistoryDisposable = (combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in
|
|
||||||
return a && b
|
|
||||||
|
self.canReadHistoryDisposable = (combineLatest(
|
||||||
|
context.sharedContext.applicationBindings.applicationInForeground,
|
||||||
|
self.canReadHistory.get(),
|
||||||
|
self.hasBrowserOrAppInFront.get()
|
||||||
|
) |> map { inForeground, globallyEnabled, hasBrowserOrWebAppInFront in
|
||||||
|
return inForeground && globallyEnabled && !hasBrowserOrWebAppInFront
|
||||||
} |> deliverOnMainQueue).startStrict(next: { [weak self] value in
|
} |> deliverOnMainQueue).startStrict(next: { [weak self] value in
|
||||||
if let strongSelf = self, strongSelf.canReadHistoryValue != value {
|
if let strongSelf = self, strongSelf.canReadHistoryValue != value {
|
||||||
strongSelf.canReadHistoryValue = value
|
strongSelf.canReadHistoryValue = value
|
||||||
@ -7119,6 +7132,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hasBrowserOrWebAppInFront: Signal<Bool, NoError> = .single([])
|
||||||
|
|> then(
|
||||||
|
self.effectiveNavigationController?.viewControllersSignal ?? .single([])
|
||||||
|
)
|
||||||
|
|> map { controllers in
|
||||||
|
if controllers.last is BrowserScreen || controllers.last is AttachmentController {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.hasBrowserOrAppInFront.set(hasBrowserOrWebAppInFront)
|
||||||
}
|
}
|
||||||
|
|
||||||
var returnInputViewFocus = false
|
var returnInputViewFocus = false
|
||||||
@ -7129,9 +7155,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.didAppear = true
|
self.didAppear = true
|
||||||
|
|
||||||
self.chatDisplayNode.historyNode.experimentalSnapScrollToItem = false
|
self.chatDisplayNode.historyNode.experimentalSnapScrollToItem = false
|
||||||
self.chatDisplayNode.historyNode.canReadHistory.set(combineLatest(self.context.sharedContext.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in
|
self.chatDisplayNode.historyNode.canReadHistory.set(self.computedCanReadHistoryPromise.get())
|
||||||
return a && b
|
|
||||||
})
|
|
||||||
|
|
||||||
self.chatDisplayNode.loadInputPanels(theme: self.presentationInterfaceState.theme, strings: self.presentationInterfaceState.strings, fontSize: self.presentationInterfaceState.fontSize)
|
self.chatDisplayNode.loadInputPanels(theme: self.presentationInterfaceState.theme, strings: self.presentationInterfaceState.strings, fontSize: self.presentationInterfaceState.fontSize)
|
||||||
|
|
||||||
|
|||||||
@ -2654,7 +2654,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return editorController
|
return editorController
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: String?, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController {
|
public func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: (url: String, name: String?)?, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController {
|
||||||
let subject: Signal<MediaEditorScreen.Subject?, NoError>
|
let subject: Signal<MediaEditorScreen.Subject?, NoError>
|
||||||
if let image = source as? UIImage {
|
if let image = source as? UIImage {
|
||||||
subject = .single(.image(image, PixelDimensions(image.size), nil, .bottomRight))
|
subject = .single(.image(image, PixelDimensions(image.size), nil, .bottomRight))
|
||||||
|
|||||||
@ -674,7 +674,17 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
media = .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers, coverTime: values.coverImageTimestamp)
|
|
||||||
|
var coverTime: Double?
|
||||||
|
if let coverImageTimestamp = values.coverImageTimestamp {
|
||||||
|
if let trimRange = values.videoTrimRange {
|
||||||
|
coverTime = coverImageTimestamp - trimRange.lowerBound
|
||||||
|
} else {
|
||||||
|
coverTime = coverImageTimestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
media = .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers, coverTime: coverTime)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -1104,7 +1104,18 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
case "web_app_share_to_story":
|
case "web_app_share_to_story":
|
||||||
if let json = json, let mediaUrl = json["media_url"] as? String {
|
if let json = json, let mediaUrl = json["media_url"] as? String {
|
||||||
let text = json["text"] as? String
|
let text = json["text"] as? String
|
||||||
let link = json["widget_link"] as? String
|
let link = json["widget_link"] as? [String: Any]
|
||||||
|
|
||||||
|
var linkUrl: String?
|
||||||
|
var linkName: String?
|
||||||
|
if let link {
|
||||||
|
if let url = link["url"] as? String {
|
||||||
|
linkUrl = url
|
||||||
|
if let name = link["name"] as? String {
|
||||||
|
linkName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum FetchResult {
|
enum FetchResult {
|
||||||
case result(Data)
|
case result(Data)
|
||||||
@ -1112,7 +1123,6 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controller = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: {
|
let controller = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: {
|
||||||
|
|
||||||
}))
|
}))
|
||||||
self.controller?.present(controller, in: .window(.root))
|
self.controller?.present(controller, in: .window(.root))
|
||||||
|
|
||||||
@ -1154,7 +1164,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
isPeerArchived: false,
|
isPeerArchived: false,
|
||||||
transitionOut: nil
|
transitionOut: nil
|
||||||
)
|
)
|
||||||
let controller = self.context.sharedContext.makeStoryMediaEditorScreen(context: self.context, source: source, text: text, link: link, completion: { result, commit in
|
let controller = self.context.sharedContext.makeStoryMediaEditorScreen(context: self.context, source: source, text: text, link: linkUrl.flatMap { ($0, linkName) }, completion: { result, commit in
|
||||||
// let targetPeerId: EnginePeer.Id
|
// let targetPeerId: EnginePeer.Id
|
||||||
let target: Stories.PendingTarget
|
let target: Stories.PendingTarget
|
||||||
// if let sendAsPeerId = result.options.sendAsPeerId {
|
// if let sendAsPeerId = result.options.sendAsPeerId {
|
||||||
@ -2115,7 +2125,9 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.controllerNode.isContainerPanningUpdated(isPanning)
|
self.controllerNode.isContainerPanningUpdated(isPanning)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var validLayout: ContainerViewLayout?
|
||||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.validLayout = layout
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||||
@ -2171,6 +2183,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.controllerNode.webView?.hideScrollIndicators()
|
self.controllerNode.webView?.hideScrollIndicators()
|
||||||
} else {
|
} else {
|
||||||
self.requestLayout(transition: .immediate)
|
self.requestLayout(transition: .immediate)
|
||||||
|
self.controllerNode.webView?.setNeedsLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2205,6 +2218,22 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
public var minimizedIcon: UIImage? {
|
public var minimizedIcon: UIImage? {
|
||||||
return self.controllerNode.icon
|
return self.controllerNode.icon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makeContentSnapshotView() -> UIView? {
|
||||||
|
guard let webView = self.controllerNode.webView, let _ = self.validLayout else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let configuration = WKSnapshotConfiguration()
|
||||||
|
configuration.rect = CGRect(origin: .zero, size: webView.frame.size)
|
||||||
|
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.frame = CGRect(origin: .zero, size: webView.frame.size)
|
||||||
|
webView.takeSnapshot(with: configuration, completionHandler: { image, _ in
|
||||||
|
imageView.image = image
|
||||||
|
})
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class WebAppPickerContext: AttachmentMediaPickerContext {
|
final class WebAppPickerContext: AttachmentMediaPickerContext {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user