mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Various improvements
This commit is contained in:
@@ -277,6 +277,7 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
private var panelNode: ChatTextInputPanelNode?
|
||||
|
||||
private var interfaceInteraction: ChatPanelInterfaceInteraction?
|
||||
private var hasPendingInputTextRefresh: Bool = false
|
||||
|
||||
private var component: ChatTextInputPanelComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
@@ -406,7 +407,10 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
if let component = self.component {
|
||||
let currentMode = inputModeFromComponent(component)
|
||||
let (updatedTextInputState, updatedMode) = f(component.externalState.textInputState, currentMode)
|
||||
if component.externalState.textInputState != updatedTextInputState {
|
||||
component.externalState.textInputState = updatedTextInputState
|
||||
self.hasPendingInputTextRefresh = true
|
||||
}
|
||||
if !self.isUpdating {
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
@@ -944,7 +948,10 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
component.externalState.resetInputState = nil
|
||||
let _ = resetInputState
|
||||
panelNode.text = ""
|
||||
} else if self.hasPendingInputTextRefresh {
|
||||
panelNode.updateInputTextState(component.externalState.textInputState)
|
||||
}
|
||||
self.hasPendingInputTextRefresh = false
|
||||
|
||||
let panelHeight = panelNode.updateLayout(
|
||||
width: availableSize.width,
|
||||
|
||||
@@ -529,6 +529,42 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
||||
}
|
||||
}
|
||||
|
||||
public func updateInputTextState(_ state: ChatTextInputState) {
|
||||
if self.ignoreInputStateUpdates {
|
||||
return
|
||||
}
|
||||
if state.inputText.length != 0 && self.textInputNode == nil {
|
||||
self.loadTextInputNode()
|
||||
}
|
||||
|
||||
if let textInputNode = self.textInputNode, let _ = self.presentationInterfaceState, let context = self.context {
|
||||
self.updatingInputState = true
|
||||
|
||||
var textColor: UIColor = .black
|
||||
var accentTextColor: UIColor = .blue
|
||||
var baseFontSize: CGFloat = 17.0
|
||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||
textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor
|
||||
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
||||
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||
}
|
||||
textInputNode.attributedText = textAttributedStringForStateText(context: context, stateText: state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||
})
|
||||
textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count)
|
||||
|
||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||
refreshChatTextInputAttributes(context: context, textView: textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||
})
|
||||
}
|
||||
|
||||
self.updatingInputState = false
|
||||
self.updateTextNodeText(animated: false)
|
||||
self.updateSpoiler()
|
||||
}
|
||||
}
|
||||
|
||||
public func updateKeepSendButtonEnabled(keepSendButtonEnabled: Bool, extendedSearchLayout: Bool, animated: Bool) {
|
||||
if keepSendButtonEnabled != self.keepSendButtonEnabled || extendedSearchLayout != self.extendedSearchLayout {
|
||||
self.keepSendButtonEnabled = keepSendButtonEnabled
|
||||
|
||||
@@ -1007,6 +1007,9 @@ private final class StoryContainerScreenComponent: Component {
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.isAnimatingOut = false
|
||||
self.didAnimateOut = false
|
||||
|
||||
if let component = self.component {
|
||||
component.focusedItemPromise.set(self.focusedItem.get())
|
||||
}
|
||||
@@ -2158,6 +2161,40 @@ public class StoryContainerScreen: ViewControllerComponentContainer {
|
||||
self.dismiss(completion: completion)
|
||||
}
|
||||
|
||||
func dismissForPictureInPicture() {
|
||||
if !self.isDismissed {
|
||||
self.isDismissed = true
|
||||
self.didAnimateIn = false
|
||||
|
||||
/*if let componentView = self.node.hostView.componentView as? StoryContainerScreenComponent.View {
|
||||
componentView.endEditing(true)
|
||||
|
||||
componentView.animateOut(completion: { [weak self] in
|
||||
self?.dismiss(animated: false)
|
||||
})
|
||||
} else {
|
||||
self.dismiss(animated: false)
|
||||
}*/
|
||||
self.dismiss(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func restoreForPictureInPicture(navigationController: NavigationController, completion: @escaping () -> Void) {
|
||||
if self.isDismissed {
|
||||
self.isDismissed = false
|
||||
|
||||
navigationController.pushViewController(self, animated: false, completion: completion)
|
||||
|
||||
if !self.didAnimateIn {
|
||||
self.didAnimateIn = true
|
||||
|
||||
if let componentView = self.node.hostView.componentView as? StoryContainerScreenComponent.View {
|
||||
componentView.animateIn()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
if !self.isDismissed {
|
||||
self.isDismissed = true
|
||||
|
||||
@@ -166,6 +166,10 @@ final class StoryItemContentComponent: Component {
|
||||
private var liveCallStateDisposable: Disposable?
|
||||
private var liveCallStatsDisposable: Disposable?
|
||||
private var mediaStream: ComponentView<Empty>?
|
||||
private let activatePictureInPictureAction = ActionSlot<Action<Void>>()
|
||||
private let deactivatePictureInPictureAction = ActionSlot<Void>()
|
||||
private var restorePictureInPicture: ((@escaping () -> Void) -> Void)?
|
||||
private var dismissWhileInPictureInPicture: (() -> Void)?
|
||||
private var loadingEffectView: StoryItemLoadingEffectView?
|
||||
private var loadingEffectAppearanceTimer: SwiftSignalKit.Timer?
|
||||
|
||||
@@ -534,7 +538,7 @@ final class StoryItemContentComponent: Component {
|
||||
if let mediaStreamCall = self.mediaStreamCall {
|
||||
//print("call progressMode: \(self.progressMode)")
|
||||
var canPlay = true
|
||||
if case .pause = self.progressMode.mode, (!self.progressMode.isCentral || !self.hierarchyTrackingLayer.isInHierarchy) {
|
||||
if case .pause = self.progressMode.mode, (!self.progressMode.isCentral || (!self.hierarchyTrackingLayer.isInHierarchy && self.restorePictureInPicture == nil)) {
|
||||
canPlay = false
|
||||
}
|
||||
if !canPlay {
|
||||
@@ -783,6 +787,22 @@ final class StoryItemContentComponent: Component {
|
||||
self.isSeeking = false
|
||||
}
|
||||
|
||||
func beginPictureInPicture(dismissController: @escaping () -> (restore: (@escaping () -> Void) -> Void, dismissWhilePictureInPicture: () -> Void)) {
|
||||
self.activatePictureInPictureAction.invoke(Action { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var restorePictureInPictureImpl: ((restore: (@escaping () -> Void) -> Void, dismissWhilePictureInPicture: () -> Void))?
|
||||
self.restorePictureInPicture = { f in
|
||||
restorePictureInPictureImpl?.restore(f)
|
||||
}
|
||||
self.dismissWhileInPictureInPicture = {
|
||||
restorePictureInPictureImpl?.dismissWhilePictureInPicture()
|
||||
}
|
||||
restorePictureInPictureImpl = dismissController()
|
||||
})
|
||||
}
|
||||
|
||||
func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
@@ -1000,13 +1020,30 @@ final class StoryItemContentComponent: Component {
|
||||
isFullscreen: false,
|
||||
videoLoading: false,
|
||||
callPeer: nil,
|
||||
enablePictureInPicture: false,
|
||||
activatePictureInPicture: ActionSlot(),
|
||||
deactivatePictureInPicture: ActionSlot(),
|
||||
bringBackControllerForPictureInPictureDeactivation: { f in
|
||||
enablePictureInPicture: true,
|
||||
activatePictureInPicture: self.activatePictureInPictureAction,
|
||||
deactivatePictureInPicture: self.deactivatePictureInPictureAction,
|
||||
bringBackControllerForPictureInPictureDeactivation: { [weak self] f in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.dismissWhileInPictureInPicture = nil
|
||||
if let restorePictureInPicture = self.restorePictureInPicture {
|
||||
self.restorePictureInPicture = nil
|
||||
restorePictureInPicture(f)
|
||||
} else {
|
||||
f()
|
||||
}
|
||||
},
|
||||
pictureInPictureClosed: {
|
||||
pictureInPictureClosed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.restorePictureInPicture = nil
|
||||
if let dismissWhileInPictureInPicture = self.dismissWhileInPictureInPicture {
|
||||
self.dismissWhileInPictureInPicture = nil
|
||||
dismissWhileInPictureInPicture()
|
||||
}
|
||||
},
|
||||
onVideoSizeRetrieved: { _ in
|
||||
},
|
||||
|
||||
@@ -1426,7 +1426,10 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
if self.verticalPanState != nil {
|
||||
return .pause
|
||||
}
|
||||
if self.inputPanelExternalState.isEditing || component.isProgressPaused || self.sendMessageContext.actionSheet != nil || self.sendMessageContext.isViewingAttachedStickers || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.viewListDisplayState != .hidden {
|
||||
if self.contextController != nil {
|
||||
return .blurred
|
||||
}
|
||||
if self.inputPanelExternalState.isEditing || component.isProgressPaused || self.sendMessageContext.actionSheet != nil || self.sendMessageContext.isViewingAttachedStickers || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.viewListDisplayState != .hidden {
|
||||
return .pause
|
||||
}
|
||||
if let reactionContextNode = self.reactionContextNode, reactionContextNode.isReactionSearchActive {
|
||||
@@ -6505,7 +6508,19 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.openItemPrivacySettings()
|
||||
})))
|
||||
|
||||
if !isLiveStream {
|
||||
if isLiveStream {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Minimize", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Call/pip"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.beginPictureInPicture()
|
||||
})))
|
||||
} else {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_Edit, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
@@ -6827,7 +6842,19 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
})))
|
||||
}
|
||||
|
||||
if !isLiveStream {
|
||||
if isLiveStream {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Minimize", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Call/pip"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.beginPictureInPicture()
|
||||
})))
|
||||
} else {
|
||||
let saveText: String = component.strings.Story_Context_SaveToGallery
|
||||
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)
|
||||
@@ -7300,7 +7327,19 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
})))
|
||||
}
|
||||
|
||||
if !component.slice.item.storyItem.isForwardingDisabled && !isLiveStream {
|
||||
if isLiveStream {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Minimize", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Call/pip"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.beginPictureInPicture()
|
||||
})))
|
||||
} else if !component.slice.item.storyItem.isForwardingDisabled {
|
||||
let saveText: String = component.strings.Story_Context_SaveToGallery
|
||||
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: accountUser.isPremium ? "Chat/Context Menu/Download" : "Chat/Context Menu/DownloadLocked"), color: theme.contextMenu.primaryColor)
|
||||
@@ -7425,6 +7464,33 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
})
|
||||
}
|
||||
|
||||
private func beginPictureInPicture() {
|
||||
guard let component = self.component, let visibleItem = self.visibleItems[component.slice.item.id] else {
|
||||
return
|
||||
}
|
||||
guard let itemView = visibleItem.view.view as? StoryItemContentComponent.View else {
|
||||
return
|
||||
}
|
||||
itemView.beginPictureInPicture(dismissController: { [weak self] in
|
||||
guard let self, let component = self.component, let controller = component.controller() as? StoryContainerScreen, let navigationController = controller.navigationController as? NavigationController else {
|
||||
return ({ completion in
|
||||
completion()
|
||||
}, {})
|
||||
}
|
||||
|
||||
controller.dismissForPictureInPicture()
|
||||
|
||||
return ({ [weak navigationController] completion in
|
||||
guard let navigationController else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
controller.restoreForPictureInPicture(navigationController: navigationController, completion: completion)
|
||||
}, {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
private func presentAddStoryFolder(addItems: [EngineStoryItem] = []) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user