mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
e32b2d3d09
@ -90,6 +90,7 @@ public protocol AttachmentContainable: ViewController {
|
|||||||
var requestAttachmentMenuExpansion: () -> Void { get set }
|
var requestAttachmentMenuExpansion: () -> Void { get set }
|
||||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void { get set }
|
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void { get set }
|
||||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set }
|
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set }
|
||||||
|
var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void { get set }
|
||||||
var cancelPanGesture: () -> Void { get set }
|
var cancelPanGesture: () -> Void { get set }
|
||||||
var isContainerPanning: () -> Bool { get set }
|
var isContainerPanning: () -> Bool { get set }
|
||||||
var isContainerExpanded: () -> Bool { get set }
|
var isContainerExpanded: () -> Bool { get set }
|
||||||
@ -564,6 +565,11 @@ public class AttachmentController: ViewController {
|
|||||||
strongSelf.panel.updateBackgroundAlpha(alpha, transition: transition)
|
strongSelf.panel.updateBackgroundAlpha(alpha, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
controller.updateTabBarVisibility = { [weak self, weak controller] isVisible, transition in
|
||||||
|
if let strongSelf = self, strongSelf.currentControllers.contains(where: { $0 === controller }) {
|
||||||
|
strongSelf.updateIsPanelVisible(isVisible, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
controller.cancelPanGesture = { [weak self] in
|
controller.cancelPanGesture = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -748,6 +754,18 @@ public class AttachmentController: ViewController {
|
|||||||
|
|
||||||
private var hasButton = false
|
private var hasButton = false
|
||||||
|
|
||||||
|
private var isPanelVisible: Bool = true
|
||||||
|
|
||||||
|
private func updateIsPanelVisible(_ isVisible: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
|
if self.isPanelVisible == isVisible {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isPanelVisible = isVisible
|
||||||
|
if let layout = self.validLayout {
|
||||||
|
self.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = layout
|
self.validLayout = layout
|
||||||
|
|
||||||
@ -837,6 +855,9 @@ public class AttachmentController: ViewController {
|
|||||||
if let controller = self.controller, controller.buttons.count > 1 || controller.hasTextInput {
|
if let controller = self.controller, controller.buttons.count > 1 || controller.hasTextInput {
|
||||||
hasPanel = true
|
hasPanel = true
|
||||||
}
|
}
|
||||||
|
if !self.isPanelVisible {
|
||||||
|
hasPanel = false
|
||||||
|
}
|
||||||
|
|
||||||
let isEffecitvelyCollapsedUpdated = (self.selectionCount > 0) != (self.panel.isSelecting)
|
let isEffecitvelyCollapsedUpdated = (self.selectionCount > 0) != (self.panel.isSelecting)
|
||||||
var panelHeight = self.panel.update(layout: containerLayout, buttons: self.controller?.buttons ?? [], isSelecting: self.selectionCount > 0, elevateProgress: !hasPanel && !hasButton, transition: transition)
|
var panelHeight = self.panel.update(layout: containerLayout, buttons: self.controller?.buttons ?? [], isSelecting: self.selectionCount > 0, elevateProgress: !hasPanel && !hasButton, transition: transition)
|
||||||
|
@ -33,17 +33,20 @@ final class ComposePollScreenComponent: Component {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let peer: EnginePeer
|
let peer: EnginePeer
|
||||||
let isQuiz: Bool?
|
let isQuiz: Bool?
|
||||||
|
let initialData: ComposePollScreen.InitialData
|
||||||
let completion: (ComposedPoll) -> Void
|
let completion: (ComposedPoll) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
isQuiz: Bool?,
|
isQuiz: Bool?,
|
||||||
|
initialData: ComposePollScreen.InitialData,
|
||||||
completion: @escaping (ComposedPoll) -> Void
|
completion: @escaping (ComposedPoll) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.isQuiz = isQuiz
|
self.isQuiz = isQuiz
|
||||||
|
self.initialData = initialData
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +72,8 @@ final class ComposePollScreenComponent: Component {
|
|||||||
private let quizAnswerSection = ComponentView<Empty>()
|
private let quizAnswerSection = ComponentView<Empty>()
|
||||||
|
|
||||||
private let pollOptionsSectionHeader = ComponentView<Empty>()
|
private let pollOptionsSectionHeader = ComponentView<Empty>()
|
||||||
private let pollOptionsSectionFooter = ComponentView<Empty>()
|
private let pollOptionsSectionFooterContainer = UIView()
|
||||||
|
private var pollOptionsSectionFooter = ComponentView<Empty>()
|
||||||
private var pollOptionsSectionContainer: ListSectionContentView
|
private var pollOptionsSectionContainer: ListSectionContentView
|
||||||
|
|
||||||
private let pollSettingsSection = ComponentView<Empty>()
|
private let pollSettingsSection = ComponentView<Empty>()
|
||||||
@ -78,6 +82,8 @@ final class ComposePollScreenComponent: Component {
|
|||||||
private var reactionSelectionControl: ComponentView<Empty>?
|
private var reactionSelectionControl: ComponentView<Empty>?
|
||||||
|
|
||||||
private var isUpdating: Bool = false
|
private var isUpdating: Bool = false
|
||||||
|
private var ignoreScrolling: Bool = false
|
||||||
|
private var previousHadInputHeight: Bool = false
|
||||||
|
|
||||||
private var component: ComposePollScreenComponent?
|
private var component: ComposePollScreenComponent?
|
||||||
private(set) weak var state: EmptyComponentState?
|
private(set) weak var state: EmptyComponentState?
|
||||||
@ -92,6 +98,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
|
|
||||||
private var nextPollOptionId: Int = 0
|
private var nextPollOptionId: Int = 0
|
||||||
private var pollOptions: [PollOption] = []
|
private var pollOptions: [PollOption] = []
|
||||||
|
private var currentPollOptionsLimitReached: Bool = false
|
||||||
|
|
||||||
private var isAnonymous: Bool = true
|
private var isAnonymous: Bool = true
|
||||||
private var isMultiAnswer: Bool = false
|
private var isMultiAnswer: Bool = false
|
||||||
@ -106,6 +113,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
private var inputMediaInteraction: ChatEntityKeyboardInputNode.Interaction?
|
private var inputMediaInteraction: ChatEntityKeyboardInputNode.Interaction?
|
||||||
private var inputMediaNode: ChatEntityKeyboardInputNode?
|
private var inputMediaNode: ChatEntityKeyboardInputNode?
|
||||||
private var inputMediaNodeBackground = SimpleLayer()
|
private var inputMediaNodeBackground = SimpleLayer()
|
||||||
|
private var inputMediaNodeTargetTag: AnyObject?
|
||||||
|
|
||||||
private let inputMediaNodeDataPromise = Promise<ChatEntityKeyboardInputNode.InputData>()
|
private let inputMediaNodeDataPromise = Promise<ChatEntityKeyboardInputNode.InputData>()
|
||||||
|
|
||||||
@ -208,6 +216,8 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let usedCustomEmojiFiles: [Int64: TelegramMediaFile] = [:]
|
||||||
|
|
||||||
return ComposedPoll(
|
return ComposedPoll(
|
||||||
publicity: self.isAnonymous ? .anonymous : .public,
|
publicity: self.isAnonymous ? .anonymous : .public,
|
||||||
kind: mappedKind,
|
kind: mappedKind,
|
||||||
@ -222,7 +232,8 @@ final class ComposePollScreenComponent: Component {
|
|||||||
return TelegramMediaPollResults.Solution(text: mappedSolution, entities: [])
|
return TelegramMediaPollResults.Solution(text: mappedSolution, entities: [])
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
deadlineTimeout: nil
|
deadlineTimeout: nil,
|
||||||
|
usedCustomEmojiFiles: usedCustomEmojiFiles
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +248,9 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
self.updateScrolling(transition: .immediate)
|
if !self.ignoreScrolling {
|
||||||
|
self.updateScrolling(transition: .immediate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateScrolling(transition: Transition) {
|
private func updateScrolling(transition: Transition) {
|
||||||
@ -279,6 +292,10 @@ final class ComposePollScreenComponent: Component {
|
|||||||
|
|
||||||
var height: CGFloat = 0.0
|
var height: CGFloat = 0.0
|
||||||
if case .emoji = self.currentInputMode, let inputData = self.inputMediaNodeData {
|
if case .emoji = self.currentInputMode, let inputData = self.inputMediaNodeData {
|
||||||
|
if let updatedTag = self.collectTextInputStates().first(where: { $1.isEditing })?.view.currentTag {
|
||||||
|
self.inputMediaNodeTargetTag = updatedTag
|
||||||
|
}
|
||||||
|
|
||||||
let inputMediaNode: ChatEntityKeyboardInputNode
|
let inputMediaNode: ChatEntityKeyboardInputNode
|
||||||
var inputMediaNodeTransition = transition
|
var inputMediaNodeTransition = transition
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
@ -293,6 +310,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
updatedInputData: self.inputMediaNodeDataPromise.get(),
|
updatedInputData: self.inputMediaNodeDataPromise.get(),
|
||||||
defaultToEmojiTab: true,
|
defaultToEmojiTab: true,
|
||||||
opaqueTopPanelBackground: false,
|
opaqueTopPanelBackground: false,
|
||||||
|
useOpaqueTheme: true,
|
||||||
interaction: self.inputMediaInteraction,
|
interaction: self.inputMediaInteraction,
|
||||||
chatPeerId: nil,
|
chatPeerId: nil,
|
||||||
stateContext: self.inputMediaNodeStateContext
|
stateContext: self.inputMediaNodeStateContext
|
||||||
@ -351,7 +369,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if animateIn {
|
if animateIn {
|
||||||
var targetFrame = inputMediaNode.frame
|
var targetFrame = inputNodeFrame
|
||||||
targetFrame.origin.y = availableSize.height
|
targetFrame.origin.y = availableSize.height
|
||||||
inputMediaNodeTransition.setFrame(layer: inputMediaNode.layer, frame: targetFrame)
|
inputMediaNodeTransition.setFrame(layer: inputMediaNode.layer, frame: targetFrame)
|
||||||
|
|
||||||
@ -367,39 +385,42 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
height = heightAndOverflow.0
|
height = heightAndOverflow.0
|
||||||
} else if let inputMediaNode = self.inputMediaNode {
|
} else {
|
||||||
self.inputMediaNode = nil
|
self.inputMediaNodeTargetTag = nil
|
||||||
|
|
||||||
var targetFrame = inputMediaNode.frame
|
if let inputMediaNode = self.inputMediaNode {
|
||||||
targetFrame.origin.y = availableSize.height
|
self.inputMediaNode = nil
|
||||||
transition.setFrame(view: inputMediaNode.view, frame: targetFrame, completion: { [weak inputMediaNode] _ in
|
var targetFrame = inputMediaNode.frame
|
||||||
if let inputMediaNode {
|
targetFrame.origin.y = availableSize.height
|
||||||
|
transition.setFrame(view: inputMediaNode.view, frame: targetFrame, completion: { [weak inputMediaNode] _ in
|
||||||
|
if let inputMediaNode {
|
||||||
|
Queue.mainQueue().after(0.3) {
|
||||||
|
inputMediaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false, completion: { [weak inputMediaNode] _ in
|
||||||
|
inputMediaNode?.view.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
transition.setFrame(layer: self.inputMediaNodeBackground, frame: targetFrame, completion: { [weak self] _ in
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
inputMediaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false, completion: { [weak inputMediaNode] _ in
|
guard let self else {
|
||||||
inputMediaNode?.view.removeFromSuperview()
|
return
|
||||||
})
|
}
|
||||||
|
if self.currentInputMode == .keyboard {
|
||||||
|
self.inputMediaNodeBackground.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false, completion: { [weak self] finished in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if finished {
|
||||||
|
self.inputMediaNodeBackground.removeFromSuperlayer()
|
||||||
|
}
|
||||||
|
self.inputMediaNodeBackground.removeAllAnimations()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
transition.setFrame(layer: self.inputMediaNodeBackground, frame: targetFrame, completion: { [weak self] _ in
|
|
||||||
Queue.mainQueue().after(0.3) {
|
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.currentInputMode == .keyboard {
|
|
||||||
self.inputMediaNodeBackground.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false, completion: { [weak self] finished in
|
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if finished {
|
|
||||||
self.inputMediaNodeBackground.removeFromSuperlayer()
|
|
||||||
}
|
|
||||||
self.inputMediaNodeBackground.removeAllAnimations()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if needsInputActivation {
|
/*if needsInputActivation {
|
||||||
@ -409,9 +430,12 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/*if let controller = self.environment?.controller() as? ComposePollScreen {
|
if let controller = self.environment?.controller() as? ComposePollScreen {
|
||||||
controller.updateTabBarAlpha(self.inputMediaNode == nil ? 1.0 : 0.0, transition.containedViewLayoutTransition)
|
let isTabBarVisible = self.inputMediaNode == nil
|
||||||
}*/
|
DispatchQueue.main.async { [weak controller] in
|
||||||
|
controller?.updateTabBarVisibility(isTabBarVisible, transition.containedViewLayoutTransition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
@ -436,6 +460,11 @@ final class ComposePollScreenComponent: Component {
|
|||||||
self.isUpdating = false
|
self.isUpdating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var alphaTransition = transition
|
||||||
|
if !transition.animation.isImmediate {
|
||||||
|
alphaTransition = alphaTransition.withAnimation(.curve(duration: 0.25, curve: .easeInOut))
|
||||||
|
}
|
||||||
|
|
||||||
let environment = environment[EnvironmentType.self].value
|
let environment = environment[EnvironmentType.self].value
|
||||||
let themeUpdated = self.environment?.theme !== environment.theme
|
let themeUpdated = self.environment?.theme !== environment.theme
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
@ -491,7 +520,7 @@ final class ComposePollScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.currentInputMode = .keyboard
|
self.currentInputMode = .keyboard
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .spring(duration: 0.4))
|
||||||
},
|
},
|
||||||
dismissTextInput: {
|
dismissTextInput: {
|
||||||
},
|
},
|
||||||
@ -500,23 +529,45 @@ final class ComposePollScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var found = false
|
||||||
for (textInputView, externalState) in self.collectTextInputStates() {
|
for (textInputView, externalState) in self.collectTextInputStates() {
|
||||||
if externalState.isEditing {
|
if externalState.isEditing {
|
||||||
textInputView.insertText(text: text)
|
textInputView.insertText(text: text)
|
||||||
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !found, let inputMediaNodeTargetTag = self.inputMediaNodeTargetTag {
|
||||||
|
for (textInputView, _) in self.collectTextInputStates() {
|
||||||
|
if textInputView.currentTag === inputMediaNodeTargetTag {
|
||||||
|
textInputView.insertText(text: text)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
backwardsDeleteText: { [weak self] in
|
backwardsDeleteText: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var found = false
|
||||||
for (textInputView, externalState) in self.collectTextInputStates() {
|
for (textInputView, externalState) in self.collectTextInputStates() {
|
||||||
if externalState.isEditing {
|
if externalState.isEditing {
|
||||||
textInputView.backwardsDeleteText()
|
textInputView.backwardsDeleteText()
|
||||||
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !found, let inputMediaNodeTargetTag = self.inputMediaNodeTargetTag {
|
||||||
|
for (textInputView, _) in self.collectTextInputStates() {
|
||||||
|
if textInputView.currentTag === inputMediaNodeTargetTag {
|
||||||
|
textInputView.backwardsDeleteText()
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
openStickerEditor: {
|
openStickerEditor: {
|
||||||
},
|
},
|
||||||
@ -576,7 +627,8 @@ final class ComposePollScreenComponent: Component {
|
|||||||
resetText: self.resetPollText.flatMap { resetText in
|
resetText: self.resetPollText.flatMap { resetText in
|
||||||
return ListComposePollOptionComponent.ResetText(value: resetText)
|
return ListComposePollOptionComponent.ResetText(value: resetText)
|
||||||
},
|
},
|
||||||
characterLimit: 256,
|
assumeIsEditing: self.inputMediaNodeTargetTag === self.pollTextFieldTag,
|
||||||
|
characterLimit: component.initialData.maxPollTextLength,
|
||||||
returnKeyAction: { [weak self] in
|
returnKeyAction: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -670,7 +722,8 @@ final class ComposePollScreenComponent: Component {
|
|||||||
resetText: pollOption.resetText.flatMap { resetText in
|
resetText: pollOption.resetText.flatMap { resetText in
|
||||||
return ListComposePollOptionComponent.ResetText(value: resetText)
|
return ListComposePollOptionComponent.ResetText(value: resetText)
|
||||||
},
|
},
|
||||||
characterLimit: 256,
|
assumeIsEditing: self.inputMediaNodeTargetTag === pollOption.textFieldTag,
|
||||||
|
characterLimit: component.initialData.maxPollOptionLength,
|
||||||
returnKeyAction: { [weak self] in
|
returnKeyAction: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -692,7 +745,11 @@ final class ComposePollScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let index = self.pollOptions.firstIndex(where: { $0.id == optionId }) {
|
if let index = self.pollOptions.firstIndex(where: { $0.id == optionId }) {
|
||||||
if index != 0 {
|
if index == 0 {
|
||||||
|
if let textInputView = self.pollTextSection.findTaggedView(tag: self.pollTextFieldTag) as? ListComposePollOptionComponent.View {
|
||||||
|
textInputView.activateInput()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if let pollOptionView = self.pollOptionsSectionContainer.itemViews[self.pollOptions[index - 1].id] {
|
if let pollOptionView = self.pollOptionsSectionContainer.itemViews[self.pollOptions[index - 1].id] {
|
||||||
if let pollOptionComponentView = pollOptionView.contents.view as? ListComposePollOptionComponent.View {
|
if let pollOptionComponentView = pollOptionView.contents.view as? ListComposePollOptionComponent.View {
|
||||||
pollOptionComponentView.activateInput()
|
pollOptionComponentView.activateInput()
|
||||||
@ -832,14 +889,31 @@ final class ComposePollScreenComponent: Component {
|
|||||||
contentHeight += pollOptionsSectionUpdateResult.size.height
|
contentHeight += pollOptionsSectionUpdateResult.size.height
|
||||||
|
|
||||||
contentHeight += 7.0
|
contentHeight += 7.0
|
||||||
var pollOptionsFooterItems: [AnimatedTextComponent.Item] = []
|
|
||||||
if self.pollOptions.count >= 10, !"".isEmpty {
|
let pollOptionsLimitReached = self.pollOptions.count >= 10
|
||||||
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
var animatePollOptionsFooterIn = false
|
||||||
id: 3,
|
var pollOptionsFooterTransition = transition
|
||||||
isUnbreakable: true,
|
if self.currentPollOptionsLimitReached != pollOptionsLimitReached {
|
||||||
content: .text("You have added the maximum number of options.")
|
self.currentPollOptionsLimitReached = pollOptionsLimitReached
|
||||||
|
if let pollOptionsSectionFooterView = self.pollOptionsSectionFooter.view {
|
||||||
|
animatePollOptionsFooterIn = true
|
||||||
|
pollOptionsFooterTransition = pollOptionsFooterTransition.withAnimation(.none)
|
||||||
|
alphaTransition.setAlpha(view: pollOptionsSectionFooterView, alpha: 0.0, completion: { [weak pollOptionsSectionFooterView] _ in
|
||||||
|
pollOptionsSectionFooterView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
self.pollOptionsSectionFooter = ComponentView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pollOptionsComponent: AnyComponent<Empty>
|
||||||
|
if pollOptionsLimitReached {
|
||||||
|
pollOptionsFooterTransition = pollOptionsFooterTransition.withAnimation(.none)
|
||||||
|
pollOptionsComponent = AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: "You have added the maximum number of options.", font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
var pollOptionsFooterItems: [AnimatedTextComponent.Item] = []
|
||||||
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
pollOptionsFooterItems.append(AnimatedTextComponent.Item(
|
||||||
id: 0,
|
id: 0,
|
||||||
isUnbreakable: true,
|
isUnbreakable: true,
|
||||||
@ -855,25 +929,36 @@ final class ComposePollScreenComponent: Component {
|
|||||||
isUnbreakable: true,
|
isUnbreakable: true,
|
||||||
content: .text(" more options.")
|
content: .text(" more options.")
|
||||||
))
|
))
|
||||||
}
|
pollOptionsComponent = AnyComponent(AnimatedTextComponent(
|
||||||
let pollOptionsSectionFooterSize = self.pollOptionsSectionFooter.update(
|
|
||||||
transition: transition,
|
|
||||||
component: AnyComponent(AnimatedTextComponent(
|
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
color: environment.theme.list.freeTextColor,
|
color: environment.theme.list.freeTextColor,
|
||||||
items: pollOptionsFooterItems
|
items: pollOptionsFooterItems
|
||||||
)),
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
let pollOptionsSectionFooterSize = self.pollOptionsSectionFooter.update(
|
||||||
|
transition: pollOptionsFooterTransition,
|
||||||
|
component: pollOptionsComponent,
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - sectionHeaderSideInset * 2.0, height: 1000.0)
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - sectionHeaderSideInset * 2.0, height: 1000.0)
|
||||||
)
|
)
|
||||||
let pollOptionsSectionFooterFrame = CGRect(origin: CGPoint(x: sideInset + sectionHeaderSideInset, y: contentHeight), size: pollOptionsSectionFooterSize)
|
let pollOptionsSectionFooterFrame = CGRect(origin: CGPoint(x: sideInset + sectionHeaderSideInset, y: contentHeight), size: pollOptionsSectionFooterSize)
|
||||||
|
|
||||||
|
if self.pollOptionsSectionFooterContainer.superview == nil {
|
||||||
|
self.scrollView.addSubview(self.pollOptionsSectionFooterContainer)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: self.pollOptionsSectionFooterContainer, frame: pollOptionsSectionFooterFrame)
|
||||||
|
|
||||||
if let pollOptionsSectionFooterView = self.pollOptionsSectionFooter.view {
|
if let pollOptionsSectionFooterView = self.pollOptionsSectionFooter.view {
|
||||||
if pollOptionsSectionFooterView.superview == nil {
|
if pollOptionsSectionFooterView.superview == nil {
|
||||||
pollOptionsSectionFooterView.layer.anchorPoint = CGPoint()
|
pollOptionsSectionFooterView.layer.anchorPoint = CGPoint()
|
||||||
self.scrollView.addSubview(pollOptionsSectionFooterView)
|
self.pollOptionsSectionFooterContainer.addSubview(pollOptionsSectionFooterView)
|
||||||
}
|
}
|
||||||
transition.setPosition(view: pollOptionsSectionFooterView, position: pollOptionsSectionFooterFrame.origin)
|
pollOptionsFooterTransition.setPosition(view: pollOptionsSectionFooterView, position: CGPoint())
|
||||||
pollOptionsSectionFooterView.bounds = CGRect(origin: CGPoint(), size: pollOptionsSectionFooterFrame.size)
|
pollOptionsSectionFooterView.bounds = CGRect(origin: CGPoint(), size: pollOptionsSectionFooterFrame.size)
|
||||||
|
if animatePollOptionsFooterIn && !transition.animation.isImmediate {
|
||||||
|
alphaTransition.animateAlpha(view: pollOptionsSectionFooterView, from: 0.0, to: 1.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
contentHeight += pollOptionsSectionFooterSize.height
|
contentHeight += pollOptionsSectionFooterSize.height
|
||||||
contentHeight += sectionSpacing
|
contentHeight += sectionSpacing
|
||||||
@ -1049,6 +1134,9 @@ final class ComposePollScreenComponent: Component {
|
|||||||
deviceMetrics: environment.deviceMetrics,
|
deviceMetrics: environment.deviceMetrics,
|
||||||
transition: transition
|
transition: transition
|
||||||
)
|
)
|
||||||
|
if self.inputMediaNode == nil {
|
||||||
|
inputHeight = environment.inputHeight
|
||||||
|
}
|
||||||
|
|
||||||
let textInputStates = self.collectTextInputStates()
|
let textInputStates = self.collectTextInputStates()
|
||||||
|
|
||||||
@ -1188,14 +1276,47 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let combinedBottomInset: CGFloat
|
||||||
if isEditing {
|
if isEditing {
|
||||||
contentHeight += bottomInset + 8.0
|
combinedBottomInset = bottomInset + 8.0 + inputHeight
|
||||||
contentHeight += inputHeight
|
|
||||||
} else {
|
} else {
|
||||||
contentHeight += bottomInset
|
combinedBottomInset = bottomInset + environment.safeInsets.bottom
|
||||||
contentHeight += environment.safeInsets.bottom
|
|
||||||
}
|
}
|
||||||
|
contentHeight += combinedBottomInset
|
||||||
|
|
||||||
|
var recenterOnTag: AnyObject?
|
||||||
|
if let hint = transition.userData(TextFieldComponent.AnimationHint.self), let targetView = hint.view {
|
||||||
|
var matches = false
|
||||||
|
switch hint.kind {
|
||||||
|
case .textChanged:
|
||||||
|
matches = true
|
||||||
|
case let .textFocusChanged(isFocused):
|
||||||
|
if isFocused {
|
||||||
|
matches = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches {
|
||||||
|
for (textView, _) in self.collectTextInputStates() {
|
||||||
|
if targetView.isDescendant(of: textView) {
|
||||||
|
recenterOnTag = textView.currentTag
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if recenterOnTag == nil && self.previousHadInputHeight != (inputHeight > 0.0) {
|
||||||
|
for (textView, state) in self.collectTextInputStates() {
|
||||||
|
if state.isEditing {
|
||||||
|
recenterOnTag = textView.currentTag
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.previousHadInputHeight = (inputHeight > 0.0)
|
||||||
|
|
||||||
|
self.ignoreScrolling = true
|
||||||
|
let previousBounds = self.scrollView.bounds
|
||||||
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)
|
||||||
@ -1208,6 +1329,31 @@ final class ComposePollScreenComponent: Component {
|
|||||||
self.scrollView.scrollIndicatorInsets = scrollInsets
|
self.scrollView.scrollIndicatorInsets = scrollInsets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let recenterOnTag {
|
||||||
|
if let targetView = self.collectTextInputStates().first(where: { $0.view.currentTag === recenterOnTag })?.view {
|
||||||
|
let caretRect = targetView.convert(targetView.bounds, to: self.scrollView)
|
||||||
|
var scrollViewBounds = self.scrollView.bounds
|
||||||
|
let minButtonDistance: CGFloat = 16.0
|
||||||
|
if -scrollViewBounds.minY + caretRect.maxY > availableSize.height - combinedBottomInset - minButtonDistance {
|
||||||
|
scrollViewBounds.origin.y = -(availableSize.height - combinedBottomInset - minButtonDistance - caretRect.maxY)
|
||||||
|
if scrollViewBounds.origin.y < 0.0 {
|
||||||
|
scrollViewBounds.origin.y = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.scrollView.bounds != scrollViewBounds {
|
||||||
|
self.scrollView.bounds = scrollViewBounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !previousBounds.isEmpty, !transition.animation.isImmediate {
|
||||||
|
let bounds = self.scrollView.bounds
|
||||||
|
if bounds.maxY != previousBounds.maxY {
|
||||||
|
let offsetY = previousBounds.maxY - bounds.maxY
|
||||||
|
transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: offsetY), to: CGPoint(), additive: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.ignoreScrolling = false
|
||||||
|
|
||||||
self.updateScrolling(transition: transition)
|
self.updateScrolling(transition: transition)
|
||||||
|
|
||||||
if isEditing {
|
if isEditing {
|
||||||
@ -1239,6 +1385,19 @@ final class ComposePollScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class ComposePollScreen: ViewControllerComponentContainer, AttachmentContainable {
|
public class ComposePollScreen: ViewControllerComponentContainer, AttachmentContainable {
|
||||||
|
public final class InitialData {
|
||||||
|
fileprivate let maxPollTextLength: Int
|
||||||
|
fileprivate let maxPollOptionLength: Int
|
||||||
|
|
||||||
|
fileprivate init(
|
||||||
|
maxPollTextLength: Int,
|
||||||
|
maxPollOptionLength: Int
|
||||||
|
) {
|
||||||
|
self.maxPollTextLength = maxPollTextLength
|
||||||
|
self.maxPollOptionLength = maxPollOptionLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let completion: (ComposedPoll) -> Void
|
private let completion: (ComposedPoll) -> Void
|
||||||
private var isDismissed: Bool = false
|
private var isDismissed: Bool = false
|
||||||
@ -1251,6 +1410,8 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
|
|||||||
}
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
}
|
}
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
|
}
|
||||||
public var cancelPanGesture: () -> Void = {
|
public var cancelPanGesture: () -> Void = {
|
||||||
}
|
}
|
||||||
public var isContainerPanning: () -> Bool = {
|
public var isContainerPanning: () -> Bool = {
|
||||||
@ -1272,6 +1433,7 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
|
initialData: InitialData,
|
||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
isQuiz: Bool?,
|
isQuiz: Bool?,
|
||||||
completion: @escaping (ComposedPoll) -> Void
|
completion: @escaping (ComposedPoll) -> Void
|
||||||
@ -1283,6 +1445,7 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
|
|||||||
context: context,
|
context: context,
|
||||||
peer: peer,
|
peer: peer,
|
||||||
isQuiz: isQuiz,
|
isQuiz: isQuiz,
|
||||||
|
initialData: initialData,
|
||||||
completion: completion
|
completion: completion
|
||||||
), navigationBarAppearance: .default, theme: .default)
|
), navigationBarAppearance: .default, theme: .default)
|
||||||
|
|
||||||
@ -1319,6 +1482,13 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
|
|||||||
deinit {
|
deinit {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func initialData(context: AccountContext) -> InitialData {
|
||||||
|
return InitialData(
|
||||||
|
maxPollTextLength: Int(context.userLimits.maxCaptionLength),
|
||||||
|
maxPollOptionLength: 100
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func cancelPressed() {
|
@objc private func cancelPressed() {
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
}
|
}
|
||||||
|
@ -506,6 +506,7 @@ public final class ComposedPoll {
|
|||||||
public let correctAnswers: [Data]?
|
public let correctAnswers: [Data]?
|
||||||
public let results: TelegramMediaPollResults
|
public let results: TelegramMediaPollResults
|
||||||
public let deadlineTimeout: Int32?
|
public let deadlineTimeout: Int32?
|
||||||
|
public let usedCustomEmojiFiles: [Int64: TelegramMediaFile]
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
publicity: TelegramMediaPollPublicity,
|
publicity: TelegramMediaPollPublicity,
|
||||||
@ -514,7 +515,8 @@ public final class ComposedPoll {
|
|||||||
options: [TelegramMediaPollOption],
|
options: [TelegramMediaPollOption],
|
||||||
correctAnswers: [Data]?,
|
correctAnswers: [Data]?,
|
||||||
results: TelegramMediaPollResults,
|
results: TelegramMediaPollResults,
|
||||||
deadlineTimeout: Int32?
|
deadlineTimeout: Int32?,
|
||||||
|
usedCustomEmojiFiles: [Int64: TelegramMediaFile]
|
||||||
) {
|
) {
|
||||||
self.publicity = publicity
|
self.publicity = publicity
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
@ -523,6 +525,7 @@ public final class ComposedPoll {
|
|||||||
self.correctAnswers = correctAnswers
|
self.correctAnswers = correctAnswers
|
||||||
self.results = results
|
self.results = results
|
||||||
self.deadlineTimeout = deadlineTimeout
|
self.deadlineTimeout = deadlineTimeout
|
||||||
|
self.usedCustomEmojiFiles = usedCustomEmojiFiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,6 +564,7 @@ public class CreatePollControllerImpl: ItemListController, AttachmentContainable
|
|||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
@ -609,15 +613,16 @@ public class CreatePollControllerImpl: ItemListController, AttachmentContainable
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> ViewController {
|
public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> ViewController {
|
||||||
if "".isEmpty {
|
return ComposePollScreen(
|
||||||
return ComposePollScreen(
|
context: context,
|
||||||
context: context,
|
initialData: ComposePollScreen.initialData(context: context),
|
||||||
peer: peer,
|
peer: peer,
|
||||||
isQuiz: isQuiz,
|
isQuiz: isQuiz,
|
||||||
completion: completion
|
completion: completion
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func legacyCreatePollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool?, completion: @escaping (ComposedPoll) -> Void) -> ViewController {
|
||||||
var initialState = CreatePollControllerState()
|
var initialState = CreatePollControllerState()
|
||||||
if let isQuiz = isQuiz {
|
if let isQuiz = isQuiz {
|
||||||
initialState.isQuiz = isQuiz
|
initialState.isQuiz = isQuiz
|
||||||
@ -973,7 +978,8 @@ public func createPollController(context: AccountContext, updatedPresentationDat
|
|||||||
options: options,
|
options: options,
|
||||||
correctAnswers: correctAnswers,
|
correctAnswers: correctAnswers,
|
||||||
results: TelegramMediaPollResults(voters: nil, totalVoters: nil, recentVoters: [], solution: resolvedSolution),
|
results: TelegramMediaPollResults(voters: nil, totalVoters: nil, recentVoters: [], solution: resolvedSolution),
|
||||||
deadlineTimeout: deadlineTimeout
|
deadlineTimeout: deadlineTimeout,
|
||||||
|
usedCustomEmojiFiles: [:]
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ public final class ListComposePollOptionComponent: Component {
|
|||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
public let strings: PresentationStrings
|
public let strings: PresentationStrings
|
||||||
public let resetText: ResetText?
|
public let resetText: ResetText?
|
||||||
|
public let assumeIsEditing: Bool
|
||||||
public let characterLimit: Int?
|
public let characterLimit: Int?
|
||||||
public let returnKeyAction: (() -> Void)?
|
public let returnKeyAction: (() -> Void)?
|
||||||
public let backspaceKeyAction: (() -> Void)?
|
public let backspaceKeyAction: (() -> Void)?
|
||||||
@ -87,6 +88,7 @@ public final class ListComposePollOptionComponent: Component {
|
|||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
resetText: ResetText? = nil,
|
resetText: ResetText? = nil,
|
||||||
|
assumeIsEditing: Bool = false,
|
||||||
characterLimit: Int,
|
characterLimit: Int,
|
||||||
returnKeyAction: (() -> Void)?,
|
returnKeyAction: (() -> Void)?,
|
||||||
backspaceKeyAction: (() -> Void)?,
|
backspaceKeyAction: (() -> Void)?,
|
||||||
@ -100,6 +102,7 @@ public final class ListComposePollOptionComponent: Component {
|
|||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.resetText = resetText
|
self.resetText = resetText
|
||||||
|
self.assumeIsEditing = assumeIsEditing
|
||||||
self.characterLimit = characterLimit
|
self.characterLimit = characterLimit
|
||||||
self.returnKeyAction = returnKeyAction
|
self.returnKeyAction = returnKeyAction
|
||||||
self.backspaceKeyAction = backspaceKeyAction
|
self.backspaceKeyAction = backspaceKeyAction
|
||||||
@ -125,6 +128,9 @@ public final class ListComposePollOptionComponent: Component {
|
|||||||
if lhs.resetText != rhs.resetText {
|
if lhs.resetText != rhs.resetText {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.assumeIsEditing != rhs.assumeIsEditing {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.characterLimit != rhs.characterLimit {
|
if lhs.characterLimit != rhs.characterLimit {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -236,6 +242,10 @@ public final class ListComposePollOptionComponent: Component {
|
|||||||
return self.textField.view as? TextFieldComponent.View
|
return self.textField.view as? TextFieldComponent.View
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var currentTag: AnyObject? {
|
||||||
|
return self.component?.tag
|
||||||
|
}
|
||||||
|
|
||||||
public var customUpdateIsHighlighted: ((Bool) -> Void)?
|
public var customUpdateIsHighlighted: ((Bool) -> Void)?
|
||||||
public private(set) var separatorInset: CGFloat = 0.0
|
public private(set) var separatorInset: CGFloat = 0.0
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab
|
|||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
@ -153,6 +153,7 @@ public final class MediaGroupsScreen: ViewController, AttachmentContainable {
|
|||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
@ -203,6 +203,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
public var requestAttachmentMenuExpansion: () -> Void = { }
|
public var requestAttachmentMenuExpansion: () -> Void = { }
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
@ -1297,6 +1297,7 @@ open class PremiumGiftScreen: ViewControllerComponentContainer {
|
|||||||
public var animationColor: UIColor?
|
public var animationColor: UIColor?
|
||||||
|
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
|
||||||
public let mainButtonStatePromise = Promise<AttachmentMainButtonState?>(nil)
|
public let mainButtonStatePromise = Promise<AttachmentMainButtonState?>(nil)
|
||||||
private let mainButtonActionSlot = ActionSlot<Void>()
|
private let mainButtonActionSlot = ActionSlot<Void>()
|
||||||
|
@ -710,8 +710,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[236446268] = { return Api.PhotoSize.parse_photoSizeEmpty($0) }
|
dict[236446268] = { return Api.PhotoSize.parse_photoSizeEmpty($0) }
|
||||||
dict[-96535659] = { return Api.PhotoSize.parse_photoSizeProgressive($0) }
|
dict[-96535659] = { return Api.PhotoSize.parse_photoSizeProgressive($0) }
|
||||||
dict[-525288402] = { return Api.PhotoSize.parse_photoStrippedSize($0) }
|
dict[-525288402] = { return Api.PhotoSize.parse_photoStrippedSize($0) }
|
||||||
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
|
dict[1484026161] = { return Api.Poll.parse_poll($0) }
|
||||||
dict[1823064809] = { return Api.PollAnswer.parse_pollAnswer($0) }
|
dict[-15277366] = { return Api.PollAnswer.parse_pollAnswer($0) }
|
||||||
dict[997055186] = { return Api.PollAnswerVoters.parse_pollAnswerVoters($0) }
|
dict[997055186] = { return Api.PollAnswerVoters.parse_pollAnswerVoters($0) }
|
||||||
dict[2061444128] = { return Api.PollResults.parse_pollResults($0) }
|
dict[2061444128] = { return Api.PollResults.parse_pollResults($0) }
|
||||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||||
@ -1358,7 +1358,7 @@ public extension Api {
|
|||||||
return parser(reader)
|
return parser(reader)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found")
|
telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,17 +436,17 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum Poll: TypeConstructorDescription {
|
enum Poll: TypeConstructorDescription {
|
||||||
case poll(id: Int64, flags: Int32, question: String, answers: [Api.PollAnswer], closePeriod: Int32?, closeDate: Int32?)
|
case poll(id: Int64, flags: Int32, question: Api.TextWithEntities, answers: [Api.PollAnswer], closePeriod: Int32?, closeDate: Int32?)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .poll(let id, let flags, let question, let answers, let closePeriod, let closeDate):
|
case .poll(let id, let flags, let question, let answers, let closePeriod, let closeDate):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-2032041631)
|
buffer.appendInt32(1484026161)
|
||||||
}
|
}
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeString(question, buffer: buffer, boxed: false)
|
question.serialize(buffer, true)
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
buffer.appendInt32(Int32(answers.count))
|
buffer.appendInt32(Int32(answers.count))
|
||||||
for item in answers {
|
for item in answers {
|
||||||
@ -470,8 +470,10 @@ public extension Api {
|
|||||||
_1 = reader.readInt64()
|
_1 = reader.readInt64()
|
||||||
var _2: Int32?
|
var _2: Int32?
|
||||||
_2 = reader.readInt32()
|
_2 = reader.readInt32()
|
||||||
var _3: String?
|
var _3: Api.TextWithEntities?
|
||||||
_3 = parseString(reader)
|
if let signature = reader.readInt32() {
|
||||||
|
_3 = Api.parse(reader, signature: signature) as? Api.TextWithEntities
|
||||||
|
}
|
||||||
var _4: [Api.PollAnswer]?
|
var _4: [Api.PollAnswer]?
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PollAnswer.self)
|
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PollAnswer.self)
|
||||||
@ -498,15 +500,15 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum PollAnswer: TypeConstructorDescription {
|
enum PollAnswer: TypeConstructorDescription {
|
||||||
case pollAnswer(text: String, option: Buffer)
|
case pollAnswer(text: Api.TextWithEntities, option: Buffer)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .pollAnswer(let text, let option):
|
case .pollAnswer(let text, let option):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1823064809)
|
buffer.appendInt32(-15277366)
|
||||||
}
|
}
|
||||||
serializeString(text, buffer: buffer, boxed: false)
|
text.serialize(buffer, true)
|
||||||
serializeBytes(option, buffer: buffer, boxed: false)
|
serializeBytes(option, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -520,8 +522,10 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func parse_pollAnswer(_ reader: BufferReader) -> PollAnswer? {
|
public static func parse_pollAnswer(_ reader: BufferReader) -> PollAnswer? {
|
||||||
var _1: String?
|
var _1: Api.TextWithEntities?
|
||||||
_1 = parseString(reader)
|
if let signature = reader.readInt32() {
|
||||||
|
_1 = Api.parse(reader, signature: signature) as? Api.TextWithEntities
|
||||||
|
}
|
||||||
var _2: Buffer?
|
var _2: Buffer?
|
||||||
_2 = parseBytes(reader)
|
_2 = parseBytes(reader)
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
|
@ -433,7 +433,16 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
|||||||
} else {
|
} else {
|
||||||
kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
|
kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
|
||||||
}
|
}
|
||||||
return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, textEntities: [], options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0, deadlineTimeout: closePeriod), nil, nil, nil, nil)
|
|
||||||
|
let questionText: String
|
||||||
|
let questionEntities: [MessageTextEntity]
|
||||||
|
switch question {
|
||||||
|
case let .textWithEntities(text, entities):
|
||||||
|
questionText = text
|
||||||
|
questionEntities = messageTextEntitiesFromApiEntities(entities)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: questionText, textEntities: questionEntities, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0, deadlineTimeout: closePeriod), nil, nil, nil, nil)
|
||||||
}
|
}
|
||||||
case let .messageMediaDice(value, emoticon):
|
case let .messageMediaDice(value, emoticon):
|
||||||
return (TelegramMediaDice(emoji: emoticon, value: value), nil, nil, nil, nil)
|
return (TelegramMediaDice(emoji: emoticon, value: value), nil, nil, nil, nil)
|
||||||
|
@ -6,13 +6,21 @@ import TelegramApi
|
|||||||
extension TelegramMediaPollOption {
|
extension TelegramMediaPollOption {
|
||||||
init(apiOption: Api.PollAnswer) {
|
init(apiOption: Api.PollAnswer) {
|
||||||
switch apiOption {
|
switch apiOption {
|
||||||
case let .pollAnswer(text, option):
|
case let .pollAnswer(text, option):
|
||||||
self.init(text: text, entities: [], opaqueIdentifier: option.makeData())
|
let answerText: String
|
||||||
|
let answerEntities: [MessageTextEntity]
|
||||||
|
switch text {
|
||||||
|
case let .textWithEntities(text, entities):
|
||||||
|
answerText = text
|
||||||
|
answerEntities = messageTextEntitiesFromApiEntities(entities)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init(text: answerText, entities: answerEntities, opaqueIdentifier: option.makeData())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiOption: Api.PollAnswer {
|
var apiOption: Api.PollAnswer {
|
||||||
return .pollAnswer(text: self.text, option: Buffer(data: self.opaqueIdentifier))
|
return .pollAnswer(text: .textWithEntities(text: self.text, entities: apiEntitiesFromMessageTextEntities(self.entities, associatedPeers: SimpleDictionary())), option: Buffer(data: self.opaqueIdentifier))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
|
|||||||
mappedSolutionEntities = apiTextAttributeEntities(TextEntitiesMessageAttribute(entities: solution.entities), associatedPeers: SimpleDictionary())
|
mappedSolutionEntities = apiTextAttributeEntities(TextEntitiesMessageAttribute(entities: solution.entities), associatedPeers: SimpleDictionary())
|
||||||
pollMediaFlags |= 1 << 1
|
pollMediaFlags |= 1 << 1
|
||||||
}
|
}
|
||||||
let inputPoll = Api.InputMedia.inputMediaPoll(flags: pollMediaFlags, poll: Api.Poll.poll(id: 0, flags: pollFlags, question: poll.text, answers: poll.options.map({ $0.apiOption }), closePeriod: poll.deadlineTimeout, closeDate: nil), correctAnswers: correctAnswers, solution: mappedSolution, solutionEntities: mappedSolutionEntities)
|
let inputPoll = Api.InputMedia.inputMediaPoll(flags: pollMediaFlags, poll: Api.Poll.poll(id: 0, flags: pollFlags, question: .textWithEntities(text: poll.text, entities: apiEntitiesFromMessageTextEntities(poll.textEntities, associatedPeers: SimpleDictionary())), answers: poll.options.map({ $0.apiOption }), closePeriod: poll.deadlineTimeout, closeDate: nil), correctAnswers: correctAnswers, solution: mappedSolution, solutionEntities: mappedSolutionEntities)
|
||||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputPoll, text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputPoll, text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||||
} else if let media = media as? TelegramMediaDice {
|
} else if let media = media as? TelegramMediaDice {
|
||||||
let inputDice = Api.InputMedia.inputMediaDice(emoticon: media.emoji)
|
let inputDice = Api.InputMedia.inputMediaDice(emoticon: media.emoji)
|
||||||
|
@ -3897,7 +3897,16 @@ func replayFinalState(
|
|||||||
} else {
|
} else {
|
||||||
kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
|
kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
|
||||||
}
|
}
|
||||||
updatedPoll = TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, textEntities: [], options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: poll.results, isClosed: (flags & (1 << 0)) != 0, deadlineTimeout: closePeriod)
|
|
||||||
|
let questionText: String
|
||||||
|
let questionEntities: [MessageTextEntity]
|
||||||
|
switch question {
|
||||||
|
case let .textWithEntities(text, entities):
|
||||||
|
questionText = text
|
||||||
|
questionEntities = messageTextEntitiesFromApiEntities(entities)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedPoll = TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: questionText, textEntities: questionEntities, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: poll.results, isClosed: (flags & (1 << 0)) != 0, deadlineTimeout: closePeriod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updatedPoll = updatedPoll.withUpdatedResults(TelegramMediaPollResults(apiResults: results), min: resultsMin)
|
updatedPoll = updatedPoll.withUpdatedResults(TelegramMediaPollResults(apiResults: results), min: resultsMin)
|
||||||
|
@ -93,6 +93,8 @@ final class AccountTaskManager {
|
|||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .emoji).start())
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .emoji).start())
|
||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .status).start())
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .status).start())
|
||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .avatar).start())
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .avatar).start())
|
||||||
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .chatStickers).start())
|
||||||
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .greetingStickers).start())
|
||||||
tasks.add(managedSynchronizeAttachMenuBots(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, force: true).start())
|
tasks.add(managedSynchronizeAttachMenuBots(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, force: true).start())
|
||||||
tasks.add(managedSynchronizeNotificationSoundList(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
tasks.add(managedSynchronizeNotificationSoundList(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||||
tasks.add(managedChatListFilters(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
|
tasks.add(managedChatListFilters(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
|
||||||
|
@ -8,6 +8,8 @@ public final class EmojiSearchCategories: Equatable, Codable {
|
|||||||
case emoji = 0
|
case emoji = 0
|
||||||
case status = 1
|
case status = 1
|
||||||
case avatar = 2
|
case avatar = 2
|
||||||
|
case chatStickers = 3
|
||||||
|
case greetingStickers = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Group: Codable, Equatable {
|
public struct Group: Codable, Equatable {
|
||||||
@ -128,6 +130,12 @@ func managedSynchronizeEmojiSearchCategories(postbox: Postbox, network: Network,
|
|||||||
|> `catch` { _ -> Signal<Api.messages.EmojiGroups, NoError> in
|
|> `catch` { _ -> Signal<Api.messages.EmojiGroups, NoError> in
|
||||||
return .single(.emojiGroupsNotModified)
|
return .single(.emojiGroupsNotModified)
|
||||||
}
|
}
|
||||||
|
//TODO:localize
|
||||||
|
case .chatStickers, .greetingStickers:
|
||||||
|
signal = network.request(Api.functions.messages.getEmojiGroups(hash: current?.hash ?? 0))
|
||||||
|
|> `catch` { _ -> Signal<Api.messages.EmojiGroups, NoError> in
|
||||||
|
return .single(.emojiGroupsNotModified)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return signal
|
return signal
|
||||||
|
@ -44,7 +44,14 @@ func _internal_requestMessageSelectPollOption(account: Account, messageId: Messa
|
|||||||
} else {
|
} else {
|
||||||
kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
|
kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
|
||||||
}
|
}
|
||||||
resultPoll = TelegramMediaPoll(pollId: pollId, publicity: publicity, kind: kind, text: question, textEntities: [], options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0, deadlineTimeout: closePeriod)
|
let questionText: String
|
||||||
|
let questionEntities: [MessageTextEntity]
|
||||||
|
switch question {
|
||||||
|
case let .textWithEntities(text, entities):
|
||||||
|
questionText = text
|
||||||
|
questionEntities = messageTextEntitiesFromApiEntities(entities)
|
||||||
|
}
|
||||||
|
resultPoll = TelegramMediaPoll(pollId: pollId, publicity: publicity, kind: kind, text: questionText, textEntities: questionEntities, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0, deadlineTimeout: closePeriod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +142,7 @@ func _internal_requestClosePoll(postbox: Postbox, network: Network, stateManager
|
|||||||
pollMediaFlags |= 1 << 1
|
pollMediaFlags |= 1 << 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, media: .inputMediaPoll(flags: pollMediaFlags, poll: .poll(id: poll.pollId.id, flags: pollFlags, question: poll.text, answers: poll.options.map({ $0.apiOption }), closePeriod: poll.deadlineTimeout, closeDate: nil), correctAnswers: correctAnswers, solution: mappedSolution, solutionEntities: mappedSolutionEntities), replyMarkup: nil, entities: nil, scheduleDate: nil, quickReplyShortcutId: nil))
|
return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, media: .inputMediaPoll(flags: pollMediaFlags, poll: .poll(id: poll.pollId.id, flags: pollFlags, question: .textWithEntities(text: poll.text, entities: apiEntitiesFromMessageTextEntities(poll.textEntities, associatedPeers: SimpleDictionary())), answers: poll.options.map({ $0.apiOption }), closePeriod: poll.deadlineTimeout, closeDate: nil), correctAnswers: correctAnswers, solution: mappedSolution, solutionEntities: mappedSolutionEntities), replyMarkup: nil, entities: nil, scheduleDate: nil, quickReplyShortcutId: nil))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
|
@ -966,7 +966,7 @@ private final class AdminUserActionsSheetComponent: Component {
|
|||||||
component: AnyComponent(PlainButtonComponent(
|
component: AnyComponent(PlainButtonComponent(
|
||||||
content: AnyComponent(OptionsSectionFooterComponent(
|
content: AnyComponent(OptionsSectionFooterComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
text: self.isConfigurationExpanded ? partiallyRestrictTitle : fullyBanTitle,
|
text: self.isConfigurationExpanded ? fullyBanTitle : partiallyRestrictTitle,
|
||||||
fontSize: presentationData.listsFontSize.itemListBaseHeaderFontSize,
|
fontSize: presentationData.listsFontSize.itemListBaseHeaderFontSize,
|
||||||
isExpanded: self.isConfigurationExpanded
|
isExpanded: self.isConfigurationExpanded
|
||||||
)),
|
)),
|
||||||
|
@ -1542,8 +1542,7 @@ public final class AvatarEditorScreen: ViewControllerComponentContainer {
|
|||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
forceHasPremium: true,
|
forceHasPremium: true,
|
||||||
searchIsPlaceholderOnly: false,
|
searchIsPlaceholderOnly: false,
|
||||||
isProfilePhotoEmojiSelection: !isGroup,
|
subject: isGroup ? .groupPhotoEmojiSelection : .profilePhotoEmojiSelection
|
||||||
isGroupPhotoEmojiSelection: isGroup
|
|
||||||
)
|
)
|
||||||
|
|
||||||
let signal = combineLatest(queue: .mainQueue(),
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
|
@ -197,7 +197,20 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
|
|
||||||
let stickerItems: Signal<EmojiPagerContentComponent?, NoError>
|
let stickerItems: Signal<EmojiPagerContentComponent?, NoError>
|
||||||
if hasStickers {
|
if hasStickers {
|
||||||
stickerItems = EmojiPagerContentComponent.stickerInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, stickerNamespaces: stickerNamespaces, stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds, chatPeerId: chatPeerId, hasSearch: hasSearch, hasTrending: hasTrending, forceHasPremium: false, hasEdit: hasEdit, hideBackground: hideBackground)
|
stickerItems = EmojiPagerContentComponent.stickerInputData(
|
||||||
|
context: context,
|
||||||
|
animationCache: animationCache,
|
||||||
|
animationRenderer: animationRenderer,
|
||||||
|
stickerNamespaces: stickerNamespaces,
|
||||||
|
stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds,
|
||||||
|
chatPeerId: chatPeerId,
|
||||||
|
hasSearch: hasSearch,
|
||||||
|
hasTrending: hasTrending,
|
||||||
|
forceHasPremium: false,
|
||||||
|
hasEdit: hasEdit,
|
||||||
|
subject: .chatStickers,
|
||||||
|
hideBackground: hideBackground
|
||||||
|
)
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
} else {
|
} else {
|
||||||
stickerItems = .single(nil)
|
stickerItems = .single(nil)
|
||||||
@ -345,6 +358,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
private var inputDataDisposable: Disposable?
|
private var inputDataDisposable: Disposable?
|
||||||
private var hasRecentGifsDisposable: Disposable?
|
private var hasRecentGifsDisposable: Disposable?
|
||||||
private let opaqueTopPanelBackground: Bool
|
private let opaqueTopPanelBackground: Bool
|
||||||
|
private let useOpaqueTheme: Bool
|
||||||
|
|
||||||
private struct EmojiSearchResult {
|
private struct EmojiSearchResult {
|
||||||
var groups: [EmojiPagerContentComponent.ItemGroup]
|
var groups: [EmojiPagerContentComponent.ItemGroup]
|
||||||
@ -451,11 +465,12 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal<InputData, NoError>, defaultToEmojiTab: Bool, opaqueTopPanelBackground: Bool = false, interaction: ChatEntityKeyboardInputNode.Interaction?, chatPeerId: PeerId?, stateContext: StateContext?) {
|
public init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal<InputData, NoError>, defaultToEmojiTab: Bool, opaqueTopPanelBackground: Bool = false, useOpaqueTheme: Bool = false, interaction: ChatEntityKeyboardInputNode.Interaction?, chatPeerId: PeerId?, stateContext: StateContext?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.currentInputData = currentInputData
|
self.currentInputData = currentInputData
|
||||||
self.defaultToEmojiTab = defaultToEmojiTab
|
self.defaultToEmojiTab = defaultToEmojiTab
|
||||||
self.opaqueTopPanelBackground = opaqueTopPanelBackground
|
self.opaqueTopPanelBackground = opaqueTopPanelBackground
|
||||||
|
self.useOpaqueTheme = useOpaqueTheme
|
||||||
self.stateContext = stateContext
|
self.stateContext = stateContext
|
||||||
|
|
||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
@ -1164,7 +1179,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
externalBackground: nil,
|
externalBackground: nil,
|
||||||
externalExpansionView: nil,
|
externalExpansionView: nil,
|
||||||
customContentView: nil,
|
customContentView: nil,
|
||||||
useOpaqueTheme: false,
|
useOpaqueTheme: self.useOpaqueTheme,
|
||||||
hideBackground: false,
|
hideBackground: false,
|
||||||
stateContext: self.stateContext?.emojiState,
|
stateContext: self.stateContext?.emojiState,
|
||||||
addImage: nil
|
addImage: nil
|
||||||
@ -1508,7 +1523,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
externalBackground: nil,
|
externalBackground: nil,
|
||||||
externalExpansionView: nil,
|
externalExpansionView: nil,
|
||||||
customContentView: nil,
|
customContentView: nil,
|
||||||
useOpaqueTheme: false,
|
useOpaqueTheme: self.useOpaqueTheme,
|
||||||
hideBackground: false,
|
hideBackground: false,
|
||||||
stateContext: nil,
|
stateContext: nil,
|
||||||
addImage: nil
|
addImage: nil
|
||||||
|
@ -1589,6 +1589,13 @@ public extension EmojiPagerContentComponent {
|
|||||||
return emojiItems
|
return emojiItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum StickersSubject {
|
||||||
|
case profilePhotoEmojiSelection
|
||||||
|
case groupPhotoEmojiSelection
|
||||||
|
case chatStickers
|
||||||
|
case greetingStickers
|
||||||
|
}
|
||||||
|
|
||||||
static func stickerInputData(
|
static func stickerInputData(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
animationCache: AnimationCache,
|
animationCache: AnimationCache,
|
||||||
@ -1601,8 +1608,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
forceHasPremium: Bool,
|
forceHasPremium: Bool,
|
||||||
hasEdit: Bool = false,
|
hasEdit: Bool = false,
|
||||||
searchIsPlaceholderOnly: Bool = true,
|
searchIsPlaceholderOnly: Bool = true,
|
||||||
isProfilePhotoEmojiSelection: Bool = false,
|
subject: StickersSubject = .chatStickers,
|
||||||
isGroupPhotoEmojiSelection: Bool = false,
|
|
||||||
hideBackground: Bool = false
|
hideBackground: Bool = false
|
||||||
) -> Signal<EmojiPagerContentComponent, NoError> {
|
) -> Signal<EmojiPagerContentComponent, NoError> {
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
@ -1652,12 +1658,17 @@ public extension EmojiPagerContentComponent {
|
|||||||
|
|
||||||
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
|
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
let searchCategories: Signal<EmojiSearchCategories?, NoError>
|
let searchCategories: Signal<EmojiSearchCategories?, NoError>
|
||||||
if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection {
|
switch subject {
|
||||||
|
case .groupPhotoEmojiSelection, .profilePhotoEmojiSelection:
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
|
||||||
} else {
|
case .chatStickers:
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji)
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .chatStickers)
|
||||||
|
case .greetingStickers:
|
||||||
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .greetingStickers)
|
||||||
}
|
}
|
||||||
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: stickerOrderedItemListCollectionIds, namespaces: stickerNamespaces, aroundIndex: nil, count: 10000000),
|
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: stickerOrderedItemListCollectionIds, namespaces: stickerNamespaces, aroundIndex: nil, count: 10000000),
|
||||||
hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: false),
|
hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: false),
|
||||||
@ -2091,6 +2102,14 @@ public extension EmojiPagerContentComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let warpContentsOnEdges: Bool
|
||||||
|
switch subject {
|
||||||
|
case .profilePhotoEmojiSelection, .groupPhotoEmojiSelection:
|
||||||
|
warpContentsOnEdges = true
|
||||||
|
default:
|
||||||
|
warpContentsOnEdges = false
|
||||||
|
}
|
||||||
|
|
||||||
return EmojiPagerContentComponent(
|
return EmojiPagerContentComponent(
|
||||||
id: isMasks ? "masks" : "stickers",
|
id: isMasks ? "masks" : "stickers",
|
||||||
context: context,
|
context: context,
|
||||||
@ -2103,7 +2122,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
itemLayoutType: .detailed,
|
itemLayoutType: .detailed,
|
||||||
itemContentUniqueId: nil,
|
itemContentUniqueId: nil,
|
||||||
searchState: .empty(hasResults: false),
|
searchState: .empty(hasResults: false),
|
||||||
warpContentsOnEdges: isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection,
|
warpContentsOnEdges: warpContentsOnEdges,
|
||||||
hideBackground: hideBackground,
|
hideBackground: hideBackground,
|
||||||
displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil,
|
displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil,
|
||||||
searchCategories: searchCategories,
|
searchCategories: searchCategories,
|
||||||
|
@ -468,7 +468,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
self.currentInputMode = .text
|
self.currentInputMode = .text
|
||||||
if hasFirstResponder(self) {
|
if hasFirstResponder(self) {
|
||||||
if let view = self.inputPanel.view as? MessageInputPanelComponent.View {
|
if let view = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||||
self.nextTransitionUserData = TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged)
|
self.nextTransitionUserData = TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged(isFocused: false))
|
||||||
if view.isActive {
|
if view.isActive {
|
||||||
view.deactivateInput(force: true)
|
view.deactivateInput(force: true)
|
||||||
} else {
|
} else {
|
||||||
@ -476,7 +476,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged)))
|
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged(isFocused: false))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1305,6 +1305,8 @@ public final class QuickReplySetupScreen: ViewControllerComponentContainer, Atta
|
|||||||
}
|
}
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
}
|
}
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in
|
||||||
|
}
|
||||||
public var cancelPanGesture: () -> Void = {
|
public var cancelPanGesture: () -> Void = {
|
||||||
}
|
}
|
||||||
public var isContainerPanning: () -> Bool = {
|
public var isContainerPanning: () -> Bool = {
|
||||||
|
@ -229,7 +229,8 @@ final class BusinessIntroSetupScreenComponent: Component {
|
|||||||
hasSearch: true,
|
hasSearch: true,
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
forceHasPremium: true,
|
forceHasPremium: true,
|
||||||
searchIsPlaceholderOnly: false
|
searchIsPlaceholderOnly: false,
|
||||||
|
subject: .greetingStickers
|
||||||
)
|
)
|
||||||
self.stickerContentDisposable = (stickerContent
|
self.stickerContentDisposable = (stickerContent
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] stickerContent in
|
|> deliverOnMainQueue).start(next: { [weak self] stickerContent in
|
||||||
|
@ -338,6 +338,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
|
|||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
@ -905,7 +905,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if hasFirstResponder(self) {
|
if hasFirstResponder(self) {
|
||||||
view.deactivateInput()
|
view.deactivateInput()
|
||||||
} else {
|
} else {
|
||||||
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged)))
|
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged(isFocused: false))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,9 +69,9 @@ public final class TextFieldComponent: Component {
|
|||||||
|
|
||||||
|
|
||||||
public final class AnimationHint {
|
public final class AnimationHint {
|
||||||
public enum Kind {
|
public enum Kind: Equatable {
|
||||||
case textChanged
|
case textChanged
|
||||||
case textFocusChanged
|
case textFocusChanged(isFocused: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
public weak var view: View?
|
public weak var view: View?
|
||||||
@ -105,6 +105,7 @@ public final class TextFieldComponent: Component {
|
|||||||
public let hideKeyboard: Bool
|
public let hideKeyboard: Bool
|
||||||
public let customInputView: UIView?
|
public let customInputView: UIView?
|
||||||
public let resetText: NSAttributedString?
|
public let resetText: NSAttributedString?
|
||||||
|
public let assumeIsEditing: Bool
|
||||||
public let isOneLineWhenUnfocused: Bool
|
public let isOneLineWhenUnfocused: Bool
|
||||||
public let characterLimit: Int?
|
public let characterLimit: Int?
|
||||||
public let emptyLineHandling: EmptyLineHandling
|
public let emptyLineHandling: EmptyLineHandling
|
||||||
@ -127,6 +128,7 @@ public final class TextFieldComponent: Component {
|
|||||||
hideKeyboard: Bool,
|
hideKeyboard: Bool,
|
||||||
customInputView: UIView?,
|
customInputView: UIView?,
|
||||||
resetText: NSAttributedString?,
|
resetText: NSAttributedString?,
|
||||||
|
assumeIsEditing: Bool = false,
|
||||||
isOneLineWhenUnfocused: Bool,
|
isOneLineWhenUnfocused: Bool,
|
||||||
characterLimit: Int? = nil,
|
characterLimit: Int? = nil,
|
||||||
emptyLineHandling: EmptyLineHandling = .allowed,
|
emptyLineHandling: EmptyLineHandling = .allowed,
|
||||||
@ -148,6 +150,7 @@ public final class TextFieldComponent: Component {
|
|||||||
self.hideKeyboard = hideKeyboard
|
self.hideKeyboard = hideKeyboard
|
||||||
self.customInputView = customInputView
|
self.customInputView = customInputView
|
||||||
self.resetText = resetText
|
self.resetText = resetText
|
||||||
|
self.assumeIsEditing = assumeIsEditing
|
||||||
self.isOneLineWhenUnfocused = isOneLineWhenUnfocused
|
self.isOneLineWhenUnfocused = isOneLineWhenUnfocused
|
||||||
self.characterLimit = characterLimit
|
self.characterLimit = characterLimit
|
||||||
self.emptyLineHandling = emptyLineHandling
|
self.emptyLineHandling = emptyLineHandling
|
||||||
@ -191,6 +194,9 @@ public final class TextFieldComponent: Component {
|
|||||||
if lhs.resetText != rhs.resetText {
|
if lhs.resetText != rhs.resetText {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.assumeIsEditing != rhs.assumeIsEditing {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.isOneLineWhenUnfocused != rhs.isOneLineWhenUnfocused {
|
if lhs.isOneLineWhenUnfocused != rhs.isOneLineWhenUnfocused {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -450,7 +456,7 @@ public final class TextFieldComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !self.isUpdating {
|
if !self.isUpdating {
|
||||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(view: self, kind: .textFocusChanged)))
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(view: self, kind: .textFocusChanged(isFocused: true))))
|
||||||
}
|
}
|
||||||
if component.isOneLineWhenUnfocused {
|
if component.isOneLineWhenUnfocused {
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
@ -461,7 +467,7 @@ public final class TextFieldComponent: Component {
|
|||||||
|
|
||||||
public func chatInputTextNodeDidFinishEditing() {
|
public func chatInputTextNodeDidFinishEditing() {
|
||||||
if !self.isUpdating {
|
if !self.isUpdating {
|
||||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(view: self, kind: .textFocusChanged)))
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(view: self, kind: .textFocusChanged(isFocused: false))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1141,7 +1147,7 @@ public final class TextFieldComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let wasEditing = component.externalState.isEditing
|
let wasEditing = component.externalState.isEditing
|
||||||
let isEditing = self.textView.isFirstResponder
|
let isEditing = self.textView.isFirstResponder || component.assumeIsEditing
|
||||||
|
|
||||||
var innerTextInsets = component.insets
|
var innerTextInsets = component.insets
|
||||||
innerTextInsets.left = 0.0
|
innerTextInsets.left = 0.0
|
||||||
|
@ -197,6 +197,7 @@ class AttachmentFileControllerImpl: ItemListController, AttachmentFileController
|
|||||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
@ -14,6 +14,7 @@ import TelegramStringFormatting
|
|||||||
import StorageUsageScreen
|
import StorageUsageScreen
|
||||||
import SettingsUI
|
import SettingsUI
|
||||||
import DeleteChatPeerActionSheetItem
|
import DeleteChatPeerActionSheetItem
|
||||||
|
import OverlayStatusController
|
||||||
|
|
||||||
fileprivate struct InitialBannedRights {
|
fileprivate struct InitialBannedRights {
|
||||||
var value: TelegramChatBannedRights?
|
var value: TelegramChatBannedRights?
|
||||||
@ -64,7 +65,7 @@ extension ChatControllerImpl {
|
|||||||
} else {
|
} else {
|
||||||
text.append("**\(result.updateBannedRights.count)** users restricted.")
|
text.append("**\(result.updateBannedRights.count)** users restricted.")
|
||||||
}
|
}
|
||||||
for id in result.banPeers {
|
for (id, _) in result.updateBannedRights {
|
||||||
if let value = initialUserBannedRights[id] {
|
if let value = initialUserBannedRights[id] {
|
||||||
undoRights[id] = value
|
undoRights[id] = value
|
||||||
}
|
}
|
||||||
@ -131,14 +132,40 @@ extension ChatControllerImpl {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if "".isEmpty {
|
var signal = combineLatest(authors.map { author in
|
||||||
self.push(RecentActionsSettingsSheet(context: self.context, adminPeers: authors.map(EnginePeer.init), completion: { _ in }))
|
|
||||||
return
|
|
||||||
}*/
|
|
||||||
|
|
||||||
self.navigationActionDisposable.set((combineLatest(authors.map { author in
|
|
||||||
self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id)
|
self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id)
|
||||||
})
|
})
|
||||||
|
let disposables = MetaDisposable()
|
||||||
|
self.navigationActionDisposable.set(disposables)
|
||||||
|
|
||||||
|
var cancelImpl: (() -> Void)?
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
|
||||||
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||||
|
cancelImpl?()
|
||||||
|
}))
|
||||||
|
self?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
return ActionDisposable { [weak controller] in
|
||||||
|
Queue.mainQueue().async() {
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> runOn(Queue.mainQueue())
|
||||||
|
|> delay(0.3, queue: Queue.mainQueue())
|
||||||
|
let progressDisposable = progressSignal.startStrict()
|
||||||
|
|
||||||
|
signal = signal
|
||||||
|
|> afterDisposed {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
progressDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cancelImpl = {
|
||||||
|
disposables.set(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
disposables.set((signal
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] participants in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] participants in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -195,52 +222,88 @@ extension ChatControllerImpl {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
var signal = self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id)
|
||||||
self.navigationActionDisposable.set((self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id)
|
let disposables = MetaDisposable()
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] participant in
|
self.navigationActionDisposable.set(disposables)
|
||||||
if let strongSelf = self {
|
|
||||||
if "".isEmpty {
|
var cancelImpl: (() -> Void)?
|
||||||
guard let participant else {
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
|
||||||
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||||
|
cancelImpl?()
|
||||||
|
}))
|
||||||
|
self?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
return ActionDisposable { [weak controller] in
|
||||||
|
Queue.mainQueue().async() {
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> runOn(Queue.mainQueue())
|
||||||
|
|> delay(0.3, queue: Queue.mainQueue())
|
||||||
|
let progressDisposable = progressSignal.startStrict()
|
||||||
|
|
||||||
|
signal = signal
|
||||||
|
|> afterDisposed {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
progressDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cancelImpl = {
|
||||||
|
disposables.set(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
disposables.set((signal
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] participant in
|
||||||
|
guard let self, let participant else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (self.context.engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
||||||
|
TelegramEngine.EngineData.Item.Peer.Peer(id: author.id)
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] chatPeer, authorPeer in
|
||||||
|
guard let self, let chatPeer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let authorPeer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var initialUserBannedRights: [EnginePeer.Id: InitialBannedRights] = [:]
|
||||||
|
switch participant {
|
||||||
|
case .creator:
|
||||||
|
break
|
||||||
|
case let .member(_, _, _, banInfo, _):
|
||||||
|
if let banInfo {
|
||||||
|
initialUserBannedRights[participant.peerId] = InitialBannedRights(value: banInfo.rights)
|
||||||
|
} else {
|
||||||
|
initialUserBannedRights[participant.peerId] = InitialBannedRights(value: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.push(AdminUserActionsSheet(
|
||||||
|
context: self.context,
|
||||||
|
chatPeer: chatPeer,
|
||||||
|
peers: [RenderedChannelParticipant(
|
||||||
|
participant: participant,
|
||||||
|
peer: authorPeer._asPeer()
|
||||||
|
)],
|
||||||
|
messageCount: messageIds.count,
|
||||||
|
completion: { [weak self] result in
|
||||||
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (strongSelf.context.engine.data.get(
|
self.applyAdminUserActionsResult(messageIds: messageIds, result: result, initialUserBannedRights: initialUserBannedRights)
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
}
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: author.id)
|
))
|
||||||
)
|
})
|
||||||
|> deliverOnMainQueue).startStandalone(next: { chatPeer, authorPeer in
|
}))
|
||||||
guard let self, let chatPeer else {
|
|
||||||
return
|
/*do {
|
||||||
}
|
self.navigationActionDisposable.set((self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id)
|
||||||
guard let authorPeer else {
|
|> deliverOnMainQueue).startStrict(next: {
|
||||||
return
|
if let strongSelf = self {
|
||||||
}
|
if "".isEmpty {
|
||||||
var initialUserBannedRights: [EnginePeer.Id: InitialBannedRights] = [:]
|
|
||||||
switch participant {
|
|
||||||
case .creator:
|
|
||||||
break
|
|
||||||
case let .member(_, _, _, banInfo, _):
|
|
||||||
if let banInfo {
|
|
||||||
initialUserBannedRights[participant.peerId] = InitialBannedRights(value: banInfo.rights)
|
|
||||||
} else {
|
|
||||||
initialUserBannedRights[participant.peerId] = InitialBannedRights(value: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.push(AdminUserActionsSheet(
|
|
||||||
context: self.context,
|
|
||||||
chatPeer: chatPeer,
|
|
||||||
peers: [RenderedChannelParticipant(
|
|
||||||
participant: participant,
|
|
||||||
peer: authorPeer._asPeer()
|
|
||||||
)],
|
|
||||||
messageCount: messageIds.count,
|
|
||||||
completion: { [weak self] result in
|
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.applyAdminUserActionsResult(messageIds: messageIds, result: result, initialUserBannedRights: initialUserBannedRights)
|
|
||||||
}
|
|
||||||
))
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +378,7 @@ extension ChatControllerImpl {
|
|||||||
strongSelf.present(actionSheet, in: .window(.root))
|
strongSelf.present(actionSheet, in: .window(.root))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentDeleteMessageOptions(messageIds: Set<MessageId>, options: ChatAvailableMessageActionOptions, contextController: ContextControllerProtocol?, completion: @escaping (ContextMenuActionResult) -> Void) {
|
func presentDeleteMessageOptions(messageIds: Set<MessageId>, options: ChatAvailableMessageActionOptions, contextController: ContextControllerProtocol?, completion: @escaping (ContextMenuActionResult) -> Void) {
|
||||||
|
@ -79,6 +79,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
var requestAttachmentMenuExpansion: () -> Void = {}
|
var requestAttachmentMenuExpansion: () -> Void = {}
|
||||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
var cancelPanGesture: () -> Void = { }
|
var cancelPanGesture: () -> Void = { }
|
||||||
var isContainerPanning: () -> Bool = { return false }
|
var isContainerPanning: () -> Bool = { return false }
|
||||||
var isContainerExpanded: () -> Bool = { return false }
|
var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
@ -259,6 +259,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
public var requestAttachmentMenuExpansion: () -> Void = { }
|
public var requestAttachmentMenuExpansion: () -> Void = { }
|
||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
|
public var updateTabBarVisibility: (Bool, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
public var isContainerPanning: () -> Bool = { return false }
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
public var isContainerExpanded: () -> Bool = { return false }
|
public var isContainerExpanded: () -> Bool = { return false }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user