Various fixes

This commit is contained in:
Ilya Laktyushin 2023-08-08 22:10:43 +02:00
parent 29707a7264
commit d6923ba451
17 changed files with 172 additions and 120 deletions

View File

@ -9414,6 +9414,7 @@ Sorry for the inconvenience.";
"ChatList.ContextSelectChats" = "Select Chats";
"StoryFeed.TooltipPremiumPosting" = "Posting stories is currently available only\nto subscribers of [Telegram Premium]().";
"StoryFeed.TooltipPremiumPostingLimited" = "This month, posting stories is only available to subscribers of [Telegram Premium]().";
"StoryFeed.TooltipStoryLimitValue_1" = "1 story";
"StoryFeed.TooltipStoryLimitValue_any" = "%d stories";
"StoryFeed.TooltipStoryLimit" = "You can't post more than **%@** stories in **24 hours**.";
@ -9623,7 +9624,7 @@ Sorry for the inconvenience.";
"Story.Editor.ExpirationValue_1" = "1 Hour";
"Story.Editor.ExpirationValue_any" = "%d Hours";
"Story.Editor.TooltipPremiumExpiration" = "Subscribe to **Telegram Premium** to make your stories disappear after 6, 12 or 48 hours.";
"Story.Editor.TooltipPremiumExpiration" = "Subscribe to [Telegram Premium]() to make your stories disappear after 6, 12 or 48 hours.";
"Story.Editor.InputPlaceholderAddCaption" = "Add a caption...";

View File

@ -894,7 +894,7 @@ public protocol SharedAccountContext: AnyObject {
func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?) -> ViewController
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool) -> ViewController
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController
func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController
func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, action: @escaping () -> Void) -> ViewController

View File

@ -2612,7 +2612,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let text: String
if premiumNeeded {
text = self.presentationData.strings.StoryFeed_TooltipPremiumPosting
text = self.presentationData.strings.StoryFeed_TooltipPremiumPostingLimited
} else if reachedCountLimit {
let valueText = self.presentationData.strings.StoryFeed_TooltipStoryLimitValue(Int32(storiesCountLimit))
text = self.presentationData.strings.StoryFeed_TooltipStoryLimit(valueText).string
@ -2634,7 +2634,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
location: .point(location, .top),
shouldDismissOnTouch: { [weak self] point, containerFrame in
if containerFrame.contains(point), premiumNeeded {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: false)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: false, dismissed: nil)
self?.push(controller)
return .dismiss(consume: true)
} else {

View File

@ -1562,7 +1562,7 @@ public final class ChatListNode: ListView {
let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .restorePremium).start()
}
}
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads, forceDark: false)
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads, forceDark: false, dismissed: nil)
self.push?(controller)
}, openChatFolderUpdates: { [weak self] in
guard let self else {

View File

@ -291,8 +291,8 @@ private final class StickerSelectionComponent: Component {
topPanelScrollingOffset: { [weak self] offset, transition in
if let self {
if self.ignoreNextZeroScrollingOffset && offset == 0.0 {
self.ignoreNextZeroScrollingOffset = false
} else {
self.ignoreNextZeroScrollingOffset = false
self.topPanelScrollingOffset = offset
}
}
@ -337,6 +337,12 @@ private final class StickerSelectionComponent: Component {
},
peekBehavior: stickerPeekBehavior
)
searchContainerNode.openGifContextMenu = { [weak self] item, sourceNode, sourceRect, gesture, isSaved in
guard let self, let node = self.component?.getController()?.node else {
return
}
node.openGifContextMenu(file: item.file, contextResult: item.contextResult, sourceView: sourceNode.view, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved)
}
return searchContainerNode
},
contentIdUpdated: { [weak self] id in
@ -522,10 +528,105 @@ public class StickerPickerScreen: ViewController {
self?.controller?.presentLocationPicker()
}
let gifItems: Signal<EntityKeyboardGifContent?, NoError>
if controller.hasGifs {
let hasRecentGifs = context.engine.data.subscribe(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs))
|> map { savedGifs -> Bool in
return !savedGifs.isEmpty
}
self.hasRecentGifsDisposable = (hasRecentGifs
|> deliverOnMainQueue).start(next: { [weak self] hasRecentGifs in
guard let strongSelf = self else {
return
}
if let gifMode = strongSelf.gifMode {
if !hasRecentGifs, case .recent = gifMode {
strongSelf.gifMode = .trending
}
} else {
strongSelf.gifMode = hasRecentGifs ? .recent : .trending
}
})
self.trendingGifsPromise.set(.single(nil))
self.trendingGifsPromise.set(paneGifSearchForQuery(context: context, query: "", offset: nil, incompleteResults: true, delayRequest: false, updateActivity: nil)
|> map { items -> ChatMediaInputGifPaneTrendingState? in
if let items = items {
return ChatMediaInputGifPaneTrendingState(files: items.files, nextOffset: items.nextOffset)
} else {
return nil
}
})
let gifInputInteraction = GifPagerContentComponent.InputInteraction(
performItemAction: { [weak self] item, view, rect in
guard let self else {
return
}
self.controller?.completion(.video(item.file.media))
self.controller?.dismiss(animated: true)
},
openGifContextMenu: { [weak self] item, sourceView, sourceRect, gesture, isSaved in
guard let self else {
return
}
self.openGifContextMenu(file: item.file, contextResult: item.contextResult, sourceView: sourceView, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved)
},
loadMore: { [weak self] token in
guard let strongSelf = self, let gifContext = strongSelf.gifContext else {
return
}
gifContext.loadMore(token: token)
},
openSearch: { [weak self] in
if let self, let componentView = self.hostView.componentView as? StickerSelectionComponent.View {
if let pagerView = componentView.keyboardView.view as? EntityKeyboardComponent.View {
pagerView.openSearch()
}
self.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))
}
},
updateSearchQuery: { [weak self] query in
guard let self else {
return
}
if let query {
self.gifMode = .emojiSearch(query)
} else {
self.gifMode = .recent
}
},
hideBackground: true,
hasSearch: true
)
self.gifInputInteraction = gifInputInteraction
gifItems = .single(EntityKeyboardGifContent(
hasRecentGifs: true,
component: GifPagerContentComponent(
context: context,
inputInteraction: gifInputInteraction,
subject: .recent,
items: [],
isLoading: false,
loadMoreToken: nil,
displaySearchWithPlaceholder: nil,
searchCategories: nil,
searchInitiallyHidden: true,
searchState: .empty(hasResults: false),
hideBackground: true
)
))
} else {
gifItems = .single(nil)
}
let data = combineLatest(
queue: Queue.mainQueue(),
controller.inputData,
.single(nil) |> then(self.gifComponent.get() |> map(Optional.init)),
gifItems |> then(self.gifComponent.get() |> map(Optional.init)),
self.stickerSearchState.get(),
self.emojiSearchState.get()
)
@ -573,80 +674,6 @@ public class StickerPickerScreen: ViewController {
strongSelf.updateContent(inputData)
}
}))
if controller.hasGifs {
let hasRecentGifs = context.engine.data.subscribe(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs))
|> map { savedGifs -> Bool in
return !savedGifs.isEmpty
}
self.hasRecentGifsDisposable = (hasRecentGifs
|> deliverOnMainQueue).start(next: { [weak self] hasRecentGifs in
guard let strongSelf = self else {
return
}
if let gifMode = strongSelf.gifMode {
if !hasRecentGifs, case .recent = gifMode {
strongSelf.gifMode = .trending
}
} else {
strongSelf.gifMode = hasRecentGifs ? .recent : .trending
}
})
}
self.trendingGifsPromise.set(.single(nil))
self.trendingGifsPromise.set(paneGifSearchForQuery(context: context, query: "", offset: nil, incompleteResults: true, delayRequest: false, updateActivity: nil)
|> map { items -> ChatMediaInputGifPaneTrendingState? in
if let items = items {
return ChatMediaInputGifPaneTrendingState(files: items.files, nextOffset: items.nextOffset)
} else {
return nil
}
})
self.gifInputInteraction = GifPagerContentComponent.InputInteraction(
performItemAction: { [weak self] item, view, rect in
guard let self else {
return
}
self.controller?.completion(.video(item.file.media))
self.controller?.dismiss(animated: true)
},
openGifContextMenu: { [weak self] item, sourceView, sourceRect, gesture, isSaved in
guard let self else {
return
}
self.openGifContextMenu(file: item.file, contextResult: item.contextResult, sourceView: sourceView, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved)
},
loadMore: { [weak self] token in
guard let strongSelf = self, let gifContext = strongSelf.gifContext else {
return
}
gifContext.loadMore(token: token)
},
openSearch: { [weak self] in
if let self, let componentView = self.hostView.componentView as? StickerSelectionComponent.View {
if let pagerView = componentView.keyboardView.view as? EntityKeyboardComponent.View {
pagerView.openSearch()
}
self.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))
}
},
updateSearchQuery: { [weak self] query in
guard let self else {
return
}
if let query {
self.gifMode = .emojiSearch(query)
} else {
self.gifMode = .recent
}
},
hideBackground: true,
hasSearch: true
)
}
deinit {
@ -662,7 +689,7 @@ public class StickerPickerScreen: ViewController {
}
}
private func openGifContextMenu(file: FileMediaReference, contextResult: (ChatContextResultCollection, ChatContextResult)?, sourceView: UIView, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) {
fileprivate func openGifContextMenu(file: FileMediaReference, contextResult: (ChatContextResultCollection, ChatContextResult)?, sourceView: UIView, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) {
guard let controller = self.controller else {
return
}
@ -698,14 +725,14 @@ public class StickerPickerScreen: ViewController {
if isSaved {
self.controller?.completion(.video(file.media))
self.controller?.dismiss(animated: true)
} else {
} else if let (_, result) = contextResult {
if case let .internalReference(reference) = result {
if let file = reference.file {
self.controller?.completion(.video(file))
self.controller?.dismiss(animated: true)
}
}
}
// if isSaved {
// let _ = self.interaction?.sendGif(file, sourceView, sourceRect, false, false)
// } else if let (collection, result) = contextResult {
// let _ = self.interaction?.sendBotContextResultAsGif(collection, result, sourceView, sourceRect, false, false)
// }
}
})))
@ -746,7 +773,7 @@ public class StickerPickerScreen: ViewController {
}
controller.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { [weak controller] action in
if case .info = action {
let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: true)
let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: true, dismissed: nil)
controller?.push(premiumController)
return true
}

View File

@ -1353,7 +1353,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
}
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil, timeout: nil), elevatedLayout: true, animateInAsReplacement: false, action: { action in
if case .info = action {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: false)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: false, dismissed: nil)
controllerInteraction?.pushController(controller)
return true
}
@ -1566,7 +1566,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
}
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil, timeout: nil), elevatedLayout: true, animateInAsReplacement: false, action: { action in
if case .info = action {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: false)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: false, dismissed: nil)
controllerInteraction?.pushController(controller)
return true
}

View File

@ -208,7 +208,7 @@ public func archiveSettingsController(context: AccountContext) -> ViewController
guard let controller else {
return
}
let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false)
let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil)
controller.push(premiumController)
}

View File

@ -404,26 +404,29 @@ final class MediaEditorScreenComponent: Component {
private var nextTransitionUserData: Any?
@objc private func deactivateInput() {
guard let view = self.inputPanel.view as? MessageInputPanelComponent.View else {
guard let _ = self.inputPanel.view as? MessageInputPanelComponent.View else {
return
}
if view.canDeactivateInput() {
self.currentInputMode = .text
if hasFirstResponder(self) {
if let view = self.inputPanel.view as? MessageInputPanelComponent.View {
self.nextTransitionUserData = TextFieldComponent.AnimationHint(kind: .textFocusChanged)
if view.isActive {
view.deactivateInput()
} else {
self.endEditing(true)
}
// if view.canDeactivateInput() {
self.currentInputMode = .text
if hasFirstResponder(self) {
if let view = self.inputPanel.view as? MessageInputPanelComponent.View {
self.nextTransitionUserData = TextFieldComponent.AnimationHint(kind: .textFocusChanged)
if view.isActive {
view.deactivateInput()
} else {
self.endEditing(true)
}
} else {
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(kind: .textFocusChanged)))
}
} else {
view.animateError()
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(kind: .textFocusChanged)))
}
// } else {
// if let controller = self.environment?.controller() as? MediaEditorScreen {
// controller.presentCaptionLimitPremiumSuggestion(isPremium: self.sta)
// }
// view.animateError()
// }
}
private var animatingButtons = false
@ -1168,7 +1171,7 @@ final class MediaEditorScreenComponent: Component {
guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else {
return
}
controller.presentCaptionLimitPremiumSuggestion()
controller.presentCaptionLimitPremiumSuggestion(isPremium: self.state?.isPremium ?? false)
},
presentTextFormattingTooltip: { [weak self] in
guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else {
@ -1193,6 +1196,11 @@ final class MediaEditorScreenComponent: Component {
controller.node.interaction?.insertEntity(entity, scale: 2.5)
self.deactivateInput()
}
case .text:
let text = self.getInputText()
if text.length > component.context.userLimits.maxStoryCaptionLength {
controller.presentCaptionLimitPremiumSuggestion(isPremium: self.state?.isPremium ?? false)
}
default:
break
}
@ -2977,7 +2985,15 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if let self {
if let content {
let stickerEntity = DrawingStickerEntity(content: content)
self.interaction?.insertEntity(stickerEntity, scale: 1.33)
let scale: CGFloat
if case .image = content {
scale = 2.5
} else if case .video = content {
scale = 2.5
} else {
scale = 1.33
}
self.interaction?.insertEntity(stickerEntity, scale: scale)
self.hasAnyChanges = true
self.controller?.isSavingAvailable = true
@ -3641,7 +3657,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let controller = UndoOverlayController(presentationData: presentationData, content: .autoDelete(isOn: true, title: nil, text: text, customUndoText: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { [weak self] action in
if case .info = action, let self {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: nil)
self.push(controller)
}
return false }
@ -3649,7 +3665,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.present(controller, in: .current)
}
fileprivate func presentCaptionLimitPremiumSuggestion() {
fileprivate func presentCaptionLimitPremiumSuggestion(isPremium: Bool) {
self.dismissAllTooltips()
let context = self.context
@ -3660,7 +3676,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let controller = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_read", scale: 0.25, colors: [:], title: title, text: text, customUndoText: nil, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { [weak self] action in
if case .info = action, let self {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: {
})
self.push(controller)
}
return false }
@ -3678,7 +3696,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let controller = UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: text), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { [weak self] action in
if case .info = action, let self {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: nil)
self.push(controller)
}
return false }
@ -3960,6 +3978,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}
if mediaEditor.resultIsVideo {
self.saveDraft(id: randomId)
var firstFrame: Signal<(UIImage?, UIImage?), NoError>
let firstFrameTime = CMTime(seconds: mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, preferredTimescale: CMTimeScale(60))

View File

@ -765,7 +765,7 @@ public final class MessageInputPanelComponent: Component {
}
}
if let maxLength = component.maxLength, maxLength - self.textFieldExternalState.textLength < 5 {
if let maxLength = component.maxLength, maxLength - self.textFieldExternalState.textLength < 5 && isEditing {
let remainingLength = max(-999, maxLength - self.textFieldExternalState.textLength)
let counterSize = self.counter.update(
transition: .immediate,

View File

@ -277,7 +277,7 @@ final class StickersResultPanelComponent: Component {
guard let self, let component = self.component else {
return
}
let controller = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .stickers, forceDark: false)
let controller = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .stickers, forceDark: false, dismissed: nil)
component.present(controller)
}))
} else {

View File

@ -2226,6 +2226,8 @@ public final class StoryItemSetContainerComponent: Component {
self.sendMessageContext.enqueueGifData(view: self, data: data)
case let .sticker(image, isMemoji):
self.sendMessageContext.enqueueStickerImage(view: self, image: image, isMemoji: isMemoji)
case .text:
break
}
},
audioRecorder: self.sendMessageContext.audioRecorderValue,

View File

@ -60,6 +60,7 @@ public final class TextFieldComponent: Component {
case images([UIImage])
case video(Data)
case gif(Data)
case text
}
@ -348,6 +349,7 @@ public final class TextFieldComponent: Component {
}
}
component.paste(.text)
return true
}

View File

@ -368,7 +368,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if case .undo = action {
var replaceImpl: ((ViewController) -> Void)?
let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in

View File

@ -1548,7 +1548,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
let context = item.context
var replaceImpl: ((ViewController) -> Void)?
let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in

View File

@ -201,7 +201,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
let context = strongSelf.context
var replaceImpl: ((ViewController) -> Void)?
let controller = context.sharedContext.makePremiumLimitController(context: context, subject: .folders, count: strongSelf.tabContainerNode?.filtersCount ?? 0, action: {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folders, forceDark: false)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folders, forceDark: false, dismissed: nil)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in

View File

@ -293,7 +293,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
}
var replaceImpl: ((ViewController) -> Void)?
let controller = context.sharedContext.makePremiumLimitController(context: context, subject: .folders, count: strongSelf.controller?.tabContainerNode?.filtersCount ?? 0, action: {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folders, forceDark: false)
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folders, forceDark: false, dismissed: nil)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in

View File

@ -1732,7 +1732,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return archiveSettingsController(context: context)
}
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool) -> ViewController {
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController {
let mappedSource: PremiumSource
switch source {
case .settings: