mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
140bd3c526
@ -11599,7 +11599,7 @@ Sorry for the inconvenience.";
|
|||||||
"ChatbotSetup.Recipients.IncludedSectionFooter" = "Select chats or entire chat categories which the bot **WILL** have access to.";
|
"ChatbotSetup.Recipients.IncludedSectionFooter" = "Select chats or entire chat categories which the bot **WILL** have access to.";
|
||||||
|
|
||||||
"ChatbotSetup.PermissionsSectionHeader" = "BOT PERMISSIONS";
|
"ChatbotSetup.PermissionsSectionHeader" = "BOT PERMISSIONS";
|
||||||
"ChatbotSetup.PermissionsSectionFooter" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot.";
|
"ChatbotSetup.PermissionsSectionFooter" = "The bot can only reply on your behalf in chats that were active during the last 24h.";
|
||||||
"ChatbotSetup.Permission.ReplyToMessages" = "Reply to Messages";
|
"ChatbotSetup.Permission.ReplyToMessages" = "Reply to Messages";
|
||||||
|
|
||||||
"ChatbotSetup.BotAddAction" = "ADD";
|
"ChatbotSetup.BotAddAction" = "ADD";
|
||||||
|
@ -60,7 +60,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.anchor = anchor
|
self.anchor = anchor
|
||||||
self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: false, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [])
|
self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [])
|
||||||
let _ = self.updateFromView()
|
let _ = self.updateFromView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,19 +167,34 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: Bool, u
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let enableSetting: Signal<Bool, NoError> = context.engine.data.subscribe(
|
||||||
|
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId),
|
||||||
|
TelegramEngine.EngineData.Item.Configuration.App()
|
||||||
|
)
|
||||||
|
|> map { accountPeer, appConfig -> Bool in
|
||||||
|
if let accountPeer, accountPeer.isPremium {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if let data = appConfig.data, let setting = data["new_noncontact_peers_require_premium_without_ownpremium"] as? Bool {
|
||||||
|
if setting {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|
||||||
let signal = combineLatest(queue: .mainQueue(),
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
context.sharedContext.presentationData,
|
context.sharedContext.presentationData,
|
||||||
context.engine.data.subscribe(
|
statePromise.get(),
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)
|
enableSetting
|
||||||
),
|
|
||||||
statePromise.get()
|
|
||||||
)
|
)
|
||||||
|> map { presentationData, accountPeer, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, enableSetting -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
let rightNavigationButton: ItemListNavigationButton? = nil
|
let rightNavigationButton: ItemListNavigationButton? = nil
|
||||||
|
|
||||||
let title: ItemListControllerTitle = .text(presentationData.strings.Privacy_Messages_Title)
|
let title: ItemListControllerTitle = .text(presentationData.strings.Privacy_Messages_Title)
|
||||||
|
|
||||||
let entries: [GlobalAutoremoveEntry] = incomingMessagePrivacyScreenEntries(presentationData: presentationData, state: state, isPremium: accountPeer?.isPremium ?? false)
|
let entries: [GlobalAutoremoveEntry] = incomingMessagePrivacyScreenEntries(presentationData: presentationData, state: state, isPremium: enableSetting)
|
||||||
|
|
||||||
let animateChanges = false
|
let animateChanges = false
|
||||||
|
|
||||||
|
@ -376,6 +376,11 @@ final class ChatHistoryPreloadManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func update(indices: [(ChatHistoryPreloadIndex, Bool, Bool)], additionalPeerIds: Set<PeerId>) {
|
private func update(indices: [(ChatHistoryPreloadIndex, Bool, Bool)], additionalPeerIds: Set<PeerId>) {
|
||||||
|
/*#if DEBUG
|
||||||
|
var indices = indices
|
||||||
|
indices.removeAll()
|
||||||
|
#endif*/
|
||||||
|
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
var validEntityIds = Set(indices.map { $0.0.entity })
|
var validEntityIds = Set(indices.map { $0.0.entity })
|
||||||
for peerId in additionalPeerIds {
|
for peerId in additionalPeerIds {
|
||||||
@ -431,7 +436,7 @@ final class ChatHistoryPreloadManager {
|
|||||||
let key: PostboxViewKey
|
let key: PostboxViewKey
|
||||||
switch index.entity {
|
switch index.entity {
|
||||||
case let .peer(peerId, threadId):
|
case let .peer(peerId, threadId):
|
||||||
key = .messageOfInterestHole(location: .peer(peerId: peerId, threadId: threadId), namespace: Namespaces.Message.Cloud, count: 70)
|
key = .messageOfInterestHole(location: .peer(peerId: peerId, threadId: threadId), namespace: Namespaces.Message.Cloud, count: 50)
|
||||||
}
|
}
|
||||||
view.disposable.set((self.postbox.combinedView(keys: [key])
|
view.disposable.set((self.postbox.combinedView(keys: [key])
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] next in
|
|> deliverOn(self.queue)).start(next: { [weak self] next in
|
||||||
|
@ -47,6 +47,7 @@ public final class EmojiActionIconComponent: Component {
|
|||||||
let size = CGSize(width: 24.0, height: 24.0)
|
let size = CGSize(width: 24.0, height: 24.0)
|
||||||
|
|
||||||
if let fileId = component.fileId {
|
if let fileId = component.fileId {
|
||||||
|
var iconSize = size
|
||||||
let icon: ComponentView<Empty>
|
let icon: ComponentView<Empty>
|
||||||
if let current = self.icon {
|
if let current = self.icon {
|
||||||
icon = current
|
icon = current
|
||||||
@ -56,6 +57,9 @@ public final class EmojiActionIconComponent: Component {
|
|||||||
}
|
}
|
||||||
let content: EmojiStatusComponent.AnimationContent
|
let content: EmojiStatusComponent.AnimationContent
|
||||||
if let file = component.file {
|
if let file = component.file {
|
||||||
|
if let dimensions = file.dimensions {
|
||||||
|
iconSize = dimensions.cgSize.aspectFitted(size)
|
||||||
|
}
|
||||||
content = .file(file: file)
|
content = .file(file: file)
|
||||||
} else {
|
} else {
|
||||||
content = .customEmoji(fileId: fileId)
|
content = .customEmoji(fileId: fileId)
|
||||||
@ -68,7 +72,7 @@ public final class EmojiActionIconComponent: Component {
|
|||||||
animationRenderer: component.context.animationRenderer,
|
animationRenderer: component.context.animationRenderer,
|
||||||
content: .animation(
|
content: .animation(
|
||||||
content: content,
|
content: content,
|
||||||
size: size,
|
size: iconSize,
|
||||||
placeholderColor: .lightGray,
|
placeholderColor: .lightGray,
|
||||||
themeColor: component.color,
|
themeColor: component.color,
|
||||||
loopMode: .forever
|
loopMode: .forever
|
||||||
@ -77,9 +81,9 @@ public final class EmojiActionIconComponent: Component {
|
|||||||
action: nil
|
action: nil
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: size
|
containerSize: iconSize
|
||||||
)
|
)
|
||||||
let iconFrame = CGRect(origin: CGPoint(), size: size)
|
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) * 0.5), y: floor((size.height - iconSize.height) * 0.5)), size: iconSize)
|
||||||
if let iconView = icon.view {
|
if let iconView = icon.view {
|
||||||
if iconView.superview == nil {
|
if iconView.superview == nil {
|
||||||
self.addSubview(iconView)
|
self.addSubview(iconView)
|
||||||
|
@ -30,6 +30,12 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum EmptyLineHandling {
|
||||||
|
case allowed
|
||||||
|
case oneConsecutive
|
||||||
|
case notAllowed
|
||||||
|
}
|
||||||
|
|
||||||
public let externalState: ExternalState?
|
public let externalState: ExternalState?
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
@ -39,10 +45,12 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
public let placeholder: String
|
public let placeholder: String
|
||||||
public let autocapitalizationType: UITextAutocapitalizationType
|
public let autocapitalizationType: UITextAutocapitalizationType
|
||||||
public let autocorrectionType: UITextAutocorrectionType
|
public let autocorrectionType: UITextAutocorrectionType
|
||||||
|
public let returnKeyType: UIReturnKeyType
|
||||||
public let characterLimit: Int?
|
public let characterLimit: Int?
|
||||||
public let displayCharacterLimit: Bool
|
public let displayCharacterLimit: Bool
|
||||||
public let allowEmptyLines: Bool
|
public let emptyLineHandling: EmptyLineHandling
|
||||||
public let updated: ((String) -> Void)?
|
public let updated: ((String) -> Void)?
|
||||||
|
public let returnKeyAction: (() -> Void)?
|
||||||
public let textUpdateTransition: Transition
|
public let textUpdateTransition: Transition
|
||||||
public let tag: AnyObject?
|
public let tag: AnyObject?
|
||||||
|
|
||||||
@ -56,10 +64,12 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
placeholder: String,
|
placeholder: String,
|
||||||
autocapitalizationType: UITextAutocapitalizationType = .sentences,
|
autocapitalizationType: UITextAutocapitalizationType = .sentences,
|
||||||
autocorrectionType: UITextAutocorrectionType = .default,
|
autocorrectionType: UITextAutocorrectionType = .default,
|
||||||
|
returnKeyType: UIReturnKeyType = .default,
|
||||||
characterLimit: Int? = nil,
|
characterLimit: Int? = nil,
|
||||||
displayCharacterLimit: Bool = false,
|
displayCharacterLimit: Bool = false,
|
||||||
allowEmptyLines: Bool = true,
|
emptyLineHandling: EmptyLineHandling = .allowed,
|
||||||
updated: ((String) -> Void)?,
|
updated: ((String) -> Void)?,
|
||||||
|
returnKeyAction: (() -> Void)? = nil,
|
||||||
textUpdateTransition: Transition = .immediate,
|
textUpdateTransition: Transition = .immediate,
|
||||||
tag: AnyObject? = nil
|
tag: AnyObject? = nil
|
||||||
) {
|
) {
|
||||||
@ -72,10 +82,12 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
self.placeholder = placeholder
|
self.placeholder = placeholder
|
||||||
self.autocapitalizationType = autocapitalizationType
|
self.autocapitalizationType = autocapitalizationType
|
||||||
self.autocorrectionType = autocorrectionType
|
self.autocorrectionType = autocorrectionType
|
||||||
|
self.returnKeyType = returnKeyType
|
||||||
self.characterLimit = characterLimit
|
self.characterLimit = characterLimit
|
||||||
self.displayCharacterLimit = displayCharacterLimit
|
self.displayCharacterLimit = displayCharacterLimit
|
||||||
self.allowEmptyLines = allowEmptyLines
|
self.emptyLineHandling = emptyLineHandling
|
||||||
self.updated = updated
|
self.updated = updated
|
||||||
|
self.returnKeyAction = returnKeyAction
|
||||||
self.textUpdateTransition = textUpdateTransition
|
self.textUpdateTransition = textUpdateTransition
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
}
|
}
|
||||||
@ -108,13 +120,16 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
if lhs.autocorrectionType != rhs.autocorrectionType {
|
if lhs.autocorrectionType != rhs.autocorrectionType {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.returnKeyType != rhs.returnKeyType {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.characterLimit != rhs.characterLimit {
|
if lhs.characterLimit != rhs.characterLimit {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.displayCharacterLimit != rhs.displayCharacterLimit {
|
if lhs.displayCharacterLimit != rhs.displayCharacterLimit {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.allowEmptyLines != rhs.allowEmptyLines {
|
if lhs.emptyLineHandling != rhs.emptyLineHandling {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (lhs.updated == nil) != (rhs.updated == nil) {
|
if (lhs.updated == nil) != (rhs.updated == nil) {
|
||||||
@ -190,6 +205,12 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func activateInput() {
|
||||||
|
if let textFieldView = self.textField.view as? TextFieldComponent.View {
|
||||||
|
textFieldView.activateInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: ListMultilineTextFieldItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
func update(component: ListMultilineTextFieldItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
self.isUpdating = true
|
self.isUpdating = true
|
||||||
defer {
|
defer {
|
||||||
@ -225,6 +246,16 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
self.measureTextLimitLabel = nil
|
self.measureTextLimitLabel = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mappedEmptyLineHandling: TextFieldComponent.EmptyLineHandling
|
||||||
|
switch component.emptyLineHandling {
|
||||||
|
case .allowed:
|
||||||
|
mappedEmptyLineHandling = .allowed
|
||||||
|
case .oneConsecutive:
|
||||||
|
mappedEmptyLineHandling = .oneConsecutive
|
||||||
|
case .notAllowed:
|
||||||
|
mappedEmptyLineHandling = .notAllowed
|
||||||
|
}
|
||||||
|
|
||||||
let textFieldSize = self.textField.update(
|
let textFieldSize = self.textField.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(TextFieldComponent(
|
component: AnyComponent(TextFieldComponent(
|
||||||
@ -242,13 +273,20 @@ public final class ListMultilineTextFieldItemComponent: Component {
|
|||||||
},
|
},
|
||||||
isOneLineWhenUnfocused: false,
|
isOneLineWhenUnfocused: false,
|
||||||
characterLimit: component.characterLimit,
|
characterLimit: component.characterLimit,
|
||||||
allowEmptyLines: component.allowEmptyLines,
|
emptyLineHandling: mappedEmptyLineHandling,
|
||||||
formatMenuAvailability: .none,
|
formatMenuAvailability: .none,
|
||||||
|
returnKeyType: component.returnKeyType,
|
||||||
lockedFormatAction: {
|
lockedFormatAction: {
|
||||||
},
|
},
|
||||||
present: { _ in
|
present: { _ in
|
||||||
},
|
},
|
||||||
paste: { _ in
|
paste: { _ in
|
||||||
|
},
|
||||||
|
returnKeyAction: { [weak self] in
|
||||||
|
guard let self, let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
component.returnKeyAction?()
|
||||||
}
|
}
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
|
@ -321,7 +321,12 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
self.scrollContentView.addSubview(avatarsNode.view)
|
self.scrollContentView.addSubview(avatarsNode.view)
|
||||||
}
|
}
|
||||||
|
|
||||||
let avatarPeers = component.peers.map(\.peer)
|
let avatarPeers: [EnginePeer]
|
||||||
|
if !premiumRestrictedUsers.isEmpty {
|
||||||
|
avatarPeers = premiumRestrictedUsers.map(\.peer)
|
||||||
|
} else {
|
||||||
|
avatarPeers = component.peers.map(\.peer)
|
||||||
|
}
|
||||||
let avatarsContent = self.avatarsContext.update(peers: avatarPeers.count <= 3 ? avatarPeers : Array(avatarPeers.prefix(upTo: 3)), animated: false)
|
let avatarsContent = self.avatarsContext.update(peers: avatarPeers.count <= 3 ? avatarPeers : Array(avatarPeers.prefix(upTo: 3)), animated: false)
|
||||||
let avatarsSize = avatarsNode.update(
|
let avatarsSize = avatarsNode.update(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
|
@ -77,6 +77,7 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
private let introSection = ComponentView<Empty>()
|
private let introSection = ComponentView<Empty>()
|
||||||
private let deleteSection = ComponentView<Empty>()
|
private let deleteSection = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private var ignoreScrolling: Bool = false
|
||||||
private var isUpdating: Bool = false
|
private var isUpdating: Bool = false
|
||||||
|
|
||||||
private var component: BusinessIntroSetupScreenComponent?
|
private var component: BusinessIntroSetupScreenComponent?
|
||||||
@ -161,7 +162,9 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
self.updateScrolling(transition: .immediate)
|
if !self.ignoreScrolling {
|
||||||
|
self.updateScrolling(transition: .immediate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var scrolledUp = true
|
private var scrolledUp = true
|
||||||
@ -765,11 +768,21 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
placeholder: "Enter Title",
|
placeholder: "Enter Title",
|
||||||
autocapitalizationType: .none,
|
autocapitalizationType: .none,
|
||||||
autocorrectionType: .no,
|
autocorrectionType: .no,
|
||||||
|
returnKeyType: .next,
|
||||||
characterLimit: 32,
|
characterLimit: 32,
|
||||||
displayCharacterLimit: true,
|
displayCharacterLimit: true,
|
||||||
allowEmptyLines: false,
|
emptyLineHandling: .notAllowed,
|
||||||
updated: { _ in
|
updated: { _ in
|
||||||
},
|
},
|
||||||
|
returnKeyAction: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let titleView = self.introSection.findTaggedView(tag: self.textInputTag) as? ListMultilineTextFieldItemComponent.View {
|
||||||
|
titleView.activateInput()
|
||||||
|
}
|
||||||
|
},
|
||||||
textUpdateTransition: .spring(duration: 0.4),
|
textUpdateTransition: .spring(duration: 0.4),
|
||||||
tag: self.titleInputTag
|
tag: self.titleInputTag
|
||||||
))))
|
))))
|
||||||
@ -786,11 +799,20 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
placeholder: "Enter Message",
|
placeholder: "Enter Message",
|
||||||
autocapitalizationType: .none,
|
autocapitalizationType: .none,
|
||||||
autocorrectionType: .no,
|
autocorrectionType: .no,
|
||||||
|
returnKeyType: .done,
|
||||||
characterLimit: 70,
|
characterLimit: 70,
|
||||||
displayCharacterLimit: true,
|
displayCharacterLimit: true,
|
||||||
allowEmptyLines: false,
|
emptyLineHandling: .notAllowed,
|
||||||
updated: { _ in
|
updated: { _ in
|
||||||
},
|
},
|
||||||
|
returnKeyAction: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let titleView = self.introSection.findTaggedView(tag: self.textInputTag) as? ListMultilineTextFieldItemComponent.View {
|
||||||
|
titleView.endEditing(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
textUpdateTransition: .spring(duration: 0.4),
|
textUpdateTransition: .spring(duration: 0.4),
|
||||||
tag: self.textInputTag
|
tag: self.textInputTag
|
||||||
))))
|
))))
|
||||||
@ -1083,6 +1105,7 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
|
|
||||||
let previousBounds = self.scrollView.bounds
|
let previousBounds = self.scrollView.bounds
|
||||||
|
|
||||||
|
self.ignoreScrolling = true
|
||||||
let contentSize = CGSize(width: availableSize.width, height: contentHeight)
|
let contentSize = CGSize(width: availableSize.width, height: contentHeight)
|
||||||
if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) {
|
if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) {
|
||||||
self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize)
|
self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize)
|
||||||
@ -1123,6 +1146,7 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.topOverscrollLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -3000.0), size: CGSize(width: availableSize.width, height: 3000.0))
|
self.topOverscrollLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -3000.0), size: CGSize(width: availableSize.width, height: 3000.0))
|
||||||
|
self.ignoreScrolling = false
|
||||||
|
|
||||||
self.updateScrolling(transition: transition)
|
self.updateScrolling(transition: transition)
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ final class BusinessLocationSetupScreenComponent: Component {
|
|||||||
autocapitalizationType: .none,
|
autocapitalizationType: .none,
|
||||||
autocorrectionType: .no,
|
autocorrectionType: .no,
|
||||||
characterLimit: 256,
|
characterLimit: 256,
|
||||||
allowEmptyLines: false,
|
emptyLineHandling: .oneConsecutive,
|
||||||
updated: { _ in
|
updated: { _ in
|
||||||
},
|
},
|
||||||
textUpdateTransition: .spring(duration: 0.4),
|
textUpdateTransition: .spring(duration: 0.4),
|
||||||
|
@ -295,7 +295,7 @@ final class ChatbotSetupScreenComponent: Component {
|
|||||||
break
|
break
|
||||||
case let .result(peer):
|
case let .result(peer):
|
||||||
let previousState = self.botResolutionState?.state
|
let previousState = self.botResolutionState?.state
|
||||||
if let peer {
|
if let peer, case let .user(user) = peer, user.botInfo != nil {
|
||||||
self.botResolutionState?.state = .found(peer: peer, isInstalled: false)
|
self.botResolutionState?.state = .found(peer: peer, isInstalled: false)
|
||||||
} else {
|
} else {
|
||||||
self.botResolutionState?.state = .notFound
|
self.botResolutionState?.state = .notFound
|
||||||
|
@ -89,6 +89,12 @@ public final class TextFieldComponent: Component {
|
|||||||
case none
|
case none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum EmptyLineHandling {
|
||||||
|
case allowed
|
||||||
|
case oneConsecutive
|
||||||
|
case notAllowed
|
||||||
|
}
|
||||||
|
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
public let strings: PresentationStrings
|
public let strings: PresentationStrings
|
||||||
@ -101,11 +107,13 @@ public final class TextFieldComponent: Component {
|
|||||||
public let resetText: NSAttributedString?
|
public let resetText: NSAttributedString?
|
||||||
public let isOneLineWhenUnfocused: Bool
|
public let isOneLineWhenUnfocused: Bool
|
||||||
public let characterLimit: Int?
|
public let characterLimit: Int?
|
||||||
public let allowEmptyLines: Bool
|
public let emptyLineHandling: EmptyLineHandling
|
||||||
public let formatMenuAvailability: FormatMenuAvailability
|
public let formatMenuAvailability: FormatMenuAvailability
|
||||||
|
public let returnKeyType: UIReturnKeyType
|
||||||
public let lockedFormatAction: () -> Void
|
public let lockedFormatAction: () -> Void
|
||||||
public let present: (ViewController) -> Void
|
public let present: (ViewController) -> Void
|
||||||
public let paste: (PasteData) -> Void
|
public let paste: (PasteData) -> Void
|
||||||
|
public let returnKeyAction: (() -> Void)?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -120,11 +128,13 @@ public final class TextFieldComponent: Component {
|
|||||||
resetText: NSAttributedString?,
|
resetText: NSAttributedString?,
|
||||||
isOneLineWhenUnfocused: Bool,
|
isOneLineWhenUnfocused: Bool,
|
||||||
characterLimit: Int? = nil,
|
characterLimit: Int? = nil,
|
||||||
allowEmptyLines: Bool = true,
|
emptyLineHandling: EmptyLineHandling = .allowed,
|
||||||
formatMenuAvailability: FormatMenuAvailability,
|
formatMenuAvailability: FormatMenuAvailability,
|
||||||
|
returnKeyType: UIReturnKeyType = .default,
|
||||||
lockedFormatAction: @escaping () -> Void,
|
lockedFormatAction: @escaping () -> Void,
|
||||||
present: @escaping (ViewController) -> Void,
|
present: @escaping (ViewController) -> Void,
|
||||||
paste: @escaping (PasteData) -> Void
|
paste: @escaping (PasteData) -> Void,
|
||||||
|
returnKeyAction: (() -> Void)? = nil
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
@ -138,11 +148,13 @@ public final class TextFieldComponent: Component {
|
|||||||
self.resetText = resetText
|
self.resetText = resetText
|
||||||
self.isOneLineWhenUnfocused = isOneLineWhenUnfocused
|
self.isOneLineWhenUnfocused = isOneLineWhenUnfocused
|
||||||
self.characterLimit = characterLimit
|
self.characterLimit = characterLimit
|
||||||
self.allowEmptyLines = allowEmptyLines
|
self.emptyLineHandling = emptyLineHandling
|
||||||
self.formatMenuAvailability = formatMenuAvailability
|
self.formatMenuAvailability = formatMenuAvailability
|
||||||
|
self.returnKeyType = returnKeyType
|
||||||
self.lockedFormatAction = lockedFormatAction
|
self.lockedFormatAction = lockedFormatAction
|
||||||
self.present = present
|
self.present = present
|
||||||
self.paste = paste
|
self.paste = paste
|
||||||
|
self.returnKeyAction = returnKeyAction
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: TextFieldComponent, rhs: TextFieldComponent) -> Bool {
|
public static func ==(lhs: TextFieldComponent, rhs: TextFieldComponent) -> Bool {
|
||||||
@ -182,12 +194,15 @@ public final class TextFieldComponent: Component {
|
|||||||
if lhs.characterLimit != rhs.characterLimit {
|
if lhs.characterLimit != rhs.characterLimit {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.allowEmptyLines != rhs.allowEmptyLines {
|
if lhs.emptyLineHandling != rhs.emptyLineHandling {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.formatMenuAvailability != rhs.formatMenuAvailability {
|
if lhs.formatMenuAvailability != rhs.formatMenuAvailability {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.returnKeyType != rhs.returnKeyType {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,6 +417,13 @@ public final class TextFieldComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func chatInputTextNodeShouldReturn() -> Bool {
|
public func chatInputTextNodeShouldReturn() -> Bool {
|
||||||
|
guard let component = self.component else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if let returnKeyAction = component.returnKeyAction {
|
||||||
|
returnKeyAction()
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,6 +581,7 @@ public final class TextFieldComponent: Component {
|
|||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let characterLimit = component.characterLimit {
|
if let characterLimit = component.characterLimit {
|
||||||
let string = self.inputState.inputText.string as NSString
|
let string = self.inputState.inputText.string as NSString
|
||||||
let updatedString = string.replacingCharacters(in: range, with: text)
|
let updatedString = string.replacingCharacters(in: range, with: text)
|
||||||
@ -566,12 +589,26 @@ public final class TextFieldComponent: Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !component.allowEmptyLines {
|
switch component.emptyLineHandling {
|
||||||
|
case .allowed:
|
||||||
|
break
|
||||||
|
case .oneConsecutive:
|
||||||
let string = self.inputState.inputText.string as NSString
|
let string = self.inputState.inputText.string as NSString
|
||||||
let updatedString = string.replacingCharacters(in: range, with: text)
|
let updatedString = string.replacingCharacters(in: range, with: text)
|
||||||
if updatedString.range(of: "\n\n") != nil {
|
if updatedString.range(of: "\n\n") != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case .notAllowed:
|
||||||
|
if (range.length == 0 && text == "\n"), let returnKeyAction = component.returnKeyAction {
|
||||||
|
returnKeyAction()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let string = self.inputState.inputText.string as NSString
|
||||||
|
let updatedString = string.replacingCharacters(in: range, with: text)
|
||||||
|
if updatedString.range(of: "\n") != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -1066,6 +1103,10 @@ public final class TextFieldComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.textView.returnKeyType != component.returnKeyType {
|
||||||
|
self.textView.returnKeyType = component.returnKeyType
|
||||||
|
}
|
||||||
|
|
||||||
if let initialText = component.externalState.initialText {
|
if let initialText = component.externalState.initialText {
|
||||||
component.externalState.initialText = nil
|
component.externalState.initialText = nil
|
||||||
self.updateInputState { _ in
|
self.updateInputState { _ in
|
||||||
|
@ -17,7 +17,7 @@ extension ChatControllerImpl {
|
|||||||
guard let self, let itemNode else {
|
guard let self, let itemNode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if value >= 1 {
|
if value >= 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ extension ChatControllerImpl {
|
|||||||
let location = CGPoint(x: bounds.midX, y: bounds.minY - 11.0)
|
let location = CGPoint(x: bounds.midX, y: bounds.minY - 11.0)
|
||||||
|
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
let tooltipController = TooltipController(content: .text("Only you can see that this message was sent by the bot."), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, balancedTextLayout: true, timeout: 3.5, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true)
|
let tooltipController = TooltipController(content: .text("Only you can see that this\nmessage was sent by the bot."), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, balancedTextLayout: true, timeout: 3.5, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true)
|
||||||
self.checksTooltipController = tooltipController
|
self.checksTooltipController = tooltipController
|
||||||
tooltipController.dismissed = { [weak self, weak tooltipController] _ in
|
tooltipController.dismissed = { [weak self, weak tooltipController] _ in
|
||||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.checksTooltipController === tooltipController {
|
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.checksTooltipController === tooltipController {
|
||||||
@ -57,6 +57,11 @@ extension ChatControllerImpl {
|
|||||||
return (self.chatDisplayNode, CGRect(origin: location, size: CGSize()))
|
return (self.chatDisplayNode, CGRect(origin: location, size: CGSize()))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
if "".isEmpty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
#endif
|
||||||
let _ = ApplicationSpecificNotice.incrementBusinessBotMessageTooltip(accountManager: self.context.sharedContext.accountManager).startStandalone()
|
let _ = ApplicationSpecificNotice.incrementBusinessBotMessageTooltip(accountManager: self.context.sharedContext.accountManager).startStandalone()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -691,6 +691,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
convertedUrl = "https://t.me/giftcode/\(slug)"
|
convertedUrl = "https://t.me/giftcode/\(slug)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if parsedUrl.host == "message" {
|
||||||
|
if let components = URLComponents(string: "/?" + query) {
|
||||||
|
var parameter: String?
|
||||||
|
if let queryItems = components.queryItems {
|
||||||
|
for queryItem in queryItems {
|
||||||
|
if let value = queryItem.value {
|
||||||
|
if queryItem.name == "slug" {
|
||||||
|
parameter = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let parameter {
|
||||||
|
convertedUrl = "https://t.me/m/\(parameter)"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsedUrl.host == "resolve" {
|
if parsedUrl.host == "resolve" {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user