[WIP] Stories

This commit is contained in:
Ali 2023-06-10 19:35:23 +04:00
parent 62f92ee5af
commit bf5a8709cc
11 changed files with 113 additions and 148 deletions

View File

@ -222,3 +222,8 @@ public func listViewAnimationDurationAndCurve(transition: ContainedViewLayoutTra
} }
} }
} }
public func scrollingRubberBandingOffset(offset: CGFloat, bandingStart: CGFloat, range: CGFloat, coefficient: CGFloat = 0.4) -> CGFloat {
let bandedOffset = offset - bandingStart
return bandingStart + (1.0 - (1.0 / ((bandedOffset * coefficient / range) + 1.0))) * range
}

View File

@ -741,13 +741,13 @@ final class MediaEditorScreenComponent: Component {
stopAndPreviewMediaRecording: nil, stopAndPreviewMediaRecording: nil,
discardMediaRecordingPreview: nil, discardMediaRecordingPreview: nil,
attachmentAction: nil, attachmentAction: nil,
reactionAction: nil,
timeoutAction: { [weak self] view in timeoutAction: { [weak self] view in
guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else { guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else {
return return
} }
controller.presentTimeoutSetup(sourceView: view) controller.presentTimeoutSetup(sourceView: view)
}, },
forwardAction: nil,
audioRecorder: nil, audioRecorder: nil,
videoRecordingStatus: nil, videoRecordingStatus: nil,
isRecordingLocked: false, isRecordingLocked: false,

View File

@ -260,8 +260,8 @@ final class StoryPreviewComponent: Component {
stopAndPreviewMediaRecording: nil, stopAndPreviewMediaRecording: nil,
discardMediaRecordingPreview: nil, discardMediaRecordingPreview: nil,
attachmentAction: { }, attachmentAction: { },
reactionAction: { _ in },
timeoutAction: nil, timeoutAction: nil,
forwardAction: nil,
audioRecorder: nil, audioRecorder: nil,
videoRecordingStatus: nil, videoRecordingStatus: nil,
isRecordingLocked: false, isRecordingLocked: false,

View File

@ -15,6 +15,8 @@ private extension MessageInputActionButtonComponent.Mode {
return "Chat/Context Menu/Delete" return "Chat/Context Menu/Delete"
case .attach: case .attach:
return "Chat/Input/Text/IconAttachment" return "Chat/Input/Text/IconAttachment"
case .forward:
return "Chat/Input/Text/IconForward"
default: default:
return nil return nil
} }
@ -30,6 +32,7 @@ public final class MessageInputActionButtonComponent: Component {
case videoInput case videoInput
case delete case delete
case attach case attach
case forward
} }
public enum Action { public enum Action {
@ -270,7 +273,7 @@ public final class MessageInputActionButtonComponent: Component {
switch component.mode { switch component.mode {
case .none: case .none:
break break
case .send, .apply, .attach, .delete: case .send, .apply, .attach, .delete, .forward:
sendAlpha = 1.0 sendAlpha = 1.0
case .videoInput, .voiceInput: case .videoInput, .voiceInput:
microphoneAlpha = 1.0 microphoneAlpha = 1.0
@ -300,7 +303,7 @@ public final class MessageInputActionButtonComponent: Component {
if previousComponent?.mode != component.mode { if previousComponent?.mode != component.mode {
switch component.mode { switch component.mode {
case .none, .send, .apply, .voiceInput, .attach, .delete: case .none, .send, .apply, .voiceInput, .attach, .delete, .forward:
micButton.updateMode(mode: .audio, animated: !transition.animation.isImmediate) micButton.updateMode(mode: .audio, animated: !transition.animation.isImmediate)
case .videoInput: case .videoInput:
micButton.updateMode(mode: .video, animated: !transition.animation.isImmediate) micButton.updateMode(mode: .video, animated: !transition.animation.isImmediate)

View File

@ -37,8 +37,8 @@ public final class MessageInputPanelComponent: Component {
public let stopAndPreviewMediaRecording: (() -> Void)? public let stopAndPreviewMediaRecording: (() -> Void)?
public let discardMediaRecordingPreview: (() -> Void)? public let discardMediaRecordingPreview: (() -> Void)?
public let attachmentAction: (() -> Void)? public let attachmentAction: (() -> Void)?
public let reactionAction: ((UIView) -> Void)?
public let timeoutAction: ((UIView) -> Void)? public let timeoutAction: ((UIView) -> Void)?
public let forwardAction: (() -> Void)?
public let audioRecorder: ManagedAudioRecorder? public let audioRecorder: ManagedAudioRecorder?
public let videoRecordingStatus: InstantVideoControllerRecordingStatus? public let videoRecordingStatus: InstantVideoControllerRecordingStatus?
public let isRecordingLocked: Bool public let isRecordingLocked: Bool
@ -64,8 +64,8 @@ public final class MessageInputPanelComponent: Component {
stopAndPreviewMediaRecording: (() -> Void)?, stopAndPreviewMediaRecording: (() -> Void)?,
discardMediaRecordingPreview: (() -> Void)?, discardMediaRecordingPreview: (() -> Void)?,
attachmentAction: (() -> Void)?, attachmentAction: (() -> Void)?,
reactionAction: ((UIView) -> Void)?,
timeoutAction: ((UIView) -> Void)?, timeoutAction: ((UIView) -> Void)?,
forwardAction: (() -> Void)?,
audioRecorder: ManagedAudioRecorder?, audioRecorder: ManagedAudioRecorder?,
videoRecordingStatus: InstantVideoControllerRecordingStatus?, videoRecordingStatus: InstantVideoControllerRecordingStatus?,
isRecordingLocked: Bool, isRecordingLocked: Bool,
@ -90,8 +90,8 @@ public final class MessageInputPanelComponent: Component {
self.stopAndPreviewMediaRecording = stopAndPreviewMediaRecording self.stopAndPreviewMediaRecording = stopAndPreviewMediaRecording
self.discardMediaRecordingPreview = discardMediaRecordingPreview self.discardMediaRecordingPreview = discardMediaRecordingPreview
self.attachmentAction = attachmentAction self.attachmentAction = attachmentAction
self.reactionAction = reactionAction
self.timeoutAction = timeoutAction self.timeoutAction = timeoutAction
self.forwardAction = forwardAction
self.audioRecorder = audioRecorder self.audioRecorder = audioRecorder
self.videoRecordingStatus = videoRecordingStatus self.videoRecordingStatus = videoRecordingStatus
self.isRecordingLocked = isRecordingLocked self.isRecordingLocked = isRecordingLocked
@ -152,6 +152,9 @@ public final class MessageInputPanelComponent: Component {
if lhs.bottomInset != rhs.bottomInset { if lhs.bottomInset != rhs.bottomInset {
return false return false
} }
if (lhs.forwardAction == nil) != (rhs.forwardAction == nil) {
return false
}
return true return true
} }
@ -238,6 +241,12 @@ public final class MessageInputPanelComponent: Component {
} }
} }
public func activateInput() {
if let textFieldView = self.textField.view as? TextFieldComponent.View {
textFieldView.activateInput()
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event) let result = super.hitTest(point, with: event)
@ -520,7 +529,13 @@ public final class MessageInputPanelComponent: Component {
if hasMediaEditing { if hasMediaEditing {
inputActionButtonMode = .send inputActionButtonMode = .send
} else { } else {
inputActionButtonMode = self.textFieldExternalState.hasText ? .send : (self.currentMediaInputIsVoice ? .voiceInput : .videoInput) if self.textFieldExternalState.hasText {
inputActionButtonMode = .send
} else if !self.textFieldExternalState.isEditing && component.forwardAction != nil {
inputActionButtonMode = .forward
} else {
inputActionButtonMode = self.currentMediaInputIsVoice ? .voiceInput : .videoInput
}
} }
} }
let inputActionButtonSize = self.inputActionButton.update( let inputActionButtonSize = self.inputActionButton.update(
@ -546,10 +561,14 @@ public final class MessageInputPanelComponent: Component {
} }
case .apply: case .apply:
if case .up = action { if case .up = action {
self.component?.sendMessageAction() component.sendMessageAction()
} }
case .voiceInput, .videoInput: case .voiceInput, .videoInput:
component.setMediaRecordingActive?(action == .down, mode == .videoInput, sendAction) component.setMediaRecordingActive?(action == .down, mode == .videoInput, sendAction)
case .forward:
if case .up = action {
component.forwardAction?()
}
default: default:
break break
} }
@ -639,39 +658,6 @@ public final class MessageInputPanelComponent: Component {
} }
} }
if let reactionAction = component.reactionAction {
let reactionButtonSize = self.reactionButton.update(
transition: transition,
component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
name: "Chat/Input/Text/AccessoryIconReaction",
tintColor: .white
)),
action: { [weak self] in
guard let self, let reactionButtonView = self.reactionButton.view else {
return
}
reactionAction(reactionButtonView)
}
).minSize(CGSize(width: 32.0, height: 32.0))),
environment: {},
containerSize: CGSize(width: 32.0, height: 32.0)
)
if let reactionButtonView = self.reactionButton.view {
if reactionButtonView.superview == nil {
self.addSubview(reactionButtonView)
}
let reactionIconFrame = CGRect(origin: CGPoint(x: fieldIconNextX - reactionButtonSize.width, y: fieldBackgroundFrame.minY + 1.0 + floor((fieldBackgroundFrame.height - reactionButtonSize.height) * 0.5)), size: reactionButtonSize)
transition.setPosition(view: reactionButtonView, position: reactionIconFrame.center)
transition.setBounds(view: reactionButtonView, bounds: CGRect(origin: CGPoint(), size: reactionIconFrame.size))
transition.setAlpha(view: reactionButtonView, alpha: (self.textFieldExternalState.hasText || hasMediaRecording || hasMediaEditing || self.textFieldExternalState.isEditing) ? 0.0 : 1.0)
transition.setScale(view: reactionButtonView, scale: (self.textFieldExternalState.hasText || hasMediaRecording || hasMediaEditing || self.textFieldExternalState.isEditing) ? 0.1 : 1.0)
fieldIconNextX -= reactionButtonSize.width + 2.0
}
}
if let timeoutAction = component.timeoutAction, let timeoutValue = component.timeoutValue { if let timeoutAction = component.timeoutAction, let timeoutValue = component.timeoutValue {
func generateIcon(value: String) -> UIImage? { func generateIcon(value: String) -> UIImage? {
let image = UIImage(bundleImageName: "Media Editor/Timeout")! let image = UIImage(bundleImageName: "Media Editor/Timeout")!

View File

@ -165,7 +165,7 @@ private final class StoryContainerScreenComponent: Component {
private var visibleItemSetViews: [EnginePeer.Id: ItemSetView] = [:] private var visibleItemSetViews: [EnginePeer.Id: ItemSetView] = [:]
private var itemSetPanState: ItemSetPanState? private var itemSetPanState: ItemSetPanState?
private var dismissPanState: ItemSetPanState? private var verticalPanState: ItemSetPanState?
private var isHoldingTouch: Bool = false private var isHoldingTouch: Bool = false
private var isAnimatingOut: Bool = false private var isAnimatingOut: Bool = false
@ -345,21 +345,27 @@ private final class StoryContainerScreenComponent: Component {
@objc private func dismissPanGesture(_ recognizer: UIPanGestureRecognizer) { @objc private func dismissPanGesture(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state { switch recognizer.state {
case .began: case .began:
self.dismissPanState = ItemSetPanState(fraction: 0.0, didBegin: true) self.verticalPanState = ItemSetPanState(fraction: 0.0, didBegin: true)
self.state?.updated(transition: .immediate) self.state?.updated(transition: .immediate)
case .changed: case .changed:
let translation = recognizer.translation(in: self) let translation = recognizer.translation(in: self)
self.dismissPanState = ItemSetPanState(fraction: max(0.0, min(1.0, translation.y / self.bounds.height)), didBegin: true) self.verticalPanState = ItemSetPanState(fraction: max(-1.0, min(1.0, translation.y / self.bounds.height)), didBegin: true)
self.state?.updated(transition: .immediate) self.state?.updated(transition: .immediate)
case .cancelled, .ended: case .cancelled, .ended:
let translation = recognizer.translation(in: self) let translation = recognizer.translation(in: self)
let velocity = recognizer.velocity(in: self) let velocity = recognizer.velocity(in: self)
self.dismissPanState = nil self.verticalPanState = nil
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring))) self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
if translation.y > 100.0 || velocity.y > 10.0 { if translation.y > 100.0 || velocity.y > 10.0 {
self.environment?.controller()?.dismiss() self.environment?.controller()?.dismiss()
} else if translation.y < -100.0 || velocity.y < -40.0 {
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] {
if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
itemSetComponentView.activateInput()
}
}
} }
default: default:
break break
@ -470,7 +476,7 @@ private final class StoryContainerScreenComponent: Component {
focusedItemPromise.set(.single(nil)) focusedItemPromise.set(.single(nil))
}) })
} else { } else {
self.dismissPanState = ItemSetPanState(fraction: 1.0, didBegin: true) self.verticalPanState = ItemSetPanState(fraction: 1.0, didBegin: true)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.2, curve: .easeInOut))) self.state?.updated(transition: Transition(animation: .curve(duration: 0.2, curve: .easeInOut)))
let focusedItemPromise = self.component?.focusedItemPromise let focusedItemPromise = self.component?.focusedItemPromise
@ -570,7 +576,7 @@ private final class StoryContainerScreenComponent: Component {
if self.itemSetPanState != nil { if self.itemSetPanState != nil {
isProgressPaused = true isProgressPaused = true
} }
if self.dismissPanState != nil { if self.verticalPanState != nil {
isProgressPaused = true isProgressPaused = true
} }
if self.isAnimatingOut { if self.isAnimatingOut {
@ -583,10 +589,14 @@ private final class StoryContainerScreenComponent: Component {
var dismissPanOffset: CGFloat = 0.0 var dismissPanOffset: CGFloat = 0.0
var dismissPanScale: CGFloat = 1.0 var dismissPanScale: CGFloat = 1.0
var dismissAlphaScale: CGFloat = 1.0 var dismissAlphaScale: CGFloat = 1.0
if let dismissPanState = self.dismissPanState { var verticalPanFraction: CGFloat = 0.0
dismissPanOffset = dismissPanState.fraction * availableSize.height if let verticalPanState = self.verticalPanState {
dismissPanScale = 1.0 * (1.0 - dismissPanState.fraction) + 0.6 * dismissPanState.fraction let dismissFraction = max(0.0, verticalPanState.fraction)
dismissAlphaScale = 1.0 * (1.0 - dismissPanState.fraction) + 0.2 * dismissPanState.fraction verticalPanFraction = max(0.0, min(1.0, -verticalPanState.fraction))
dismissPanOffset = dismissFraction * availableSize.height
dismissPanScale = 1.0 * (1.0 - dismissFraction) + 0.6 * dismissFraction
dismissAlphaScale = 1.0 * (1.0 - dismissFraction) + 0.2 * dismissFraction
} }
transition.setAlpha(layer: self.backgroundLayer, alpha: max(0.5, dismissAlphaScale)) transition.setAlpha(layer: self.backgroundLayer, alpha: max(0.5, dismissAlphaScale))
@ -685,6 +695,7 @@ private final class StoryContainerScreenComponent: Component {
hideUI: i == focusedIndex && self.itemSetPanState?.didBegin == false, hideUI: i == focusedIndex && self.itemSetPanState?.didBegin == false,
visibilityFraction: 1.0 - abs(panFraction + cubeAdditionalRotationFraction), visibilityFraction: 1.0 - abs(panFraction + cubeAdditionalRotationFraction),
isPanning: self.itemSetPanState?.didBegin == true, isPanning: self.itemSetPanState?.didBegin == true,
verticalPanFraction: verticalPanFraction,
presentController: { [weak self] c in presentController: { [weak self] c in
guard let self, let environment = self.environment else { guard let self, let environment = self.environment else {
return return

View File

@ -47,6 +47,7 @@ public final class StoryItemSetContainerComponent: Component {
public let hideUI: Bool public let hideUI: Bool
public let visibilityFraction: CGFloat public let visibilityFraction: CGFloat
public let isPanning: Bool public let isPanning: Bool
public let verticalPanFraction: CGFloat
public let presentController: (ViewController) -> Void public let presentController: (ViewController) -> Void
public let close: () -> Void public let close: () -> Void
public let navigate: (NavigationDirection) -> Void public let navigate: (NavigationDirection) -> Void
@ -68,6 +69,7 @@ public final class StoryItemSetContainerComponent: Component {
hideUI: Bool, hideUI: Bool,
visibilityFraction: CGFloat, visibilityFraction: CGFloat,
isPanning: Bool, isPanning: Bool,
verticalPanFraction: CGFloat,
presentController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController) -> Void,
close: @escaping () -> Void, close: @escaping () -> Void,
navigate: @escaping (NavigationDirection) -> Void, navigate: @escaping (NavigationDirection) -> Void,
@ -88,6 +90,7 @@ public final class StoryItemSetContainerComponent: Component {
self.hideUI = hideUI self.hideUI = hideUI
self.visibilityFraction = visibilityFraction self.visibilityFraction = visibilityFraction
self.isPanning = isPanning self.isPanning = isPanning
self.verticalPanFraction = verticalPanFraction
self.presentController = presentController self.presentController = presentController
self.close = close self.close = close
self.navigate = navigate self.navigate = navigate
@ -133,6 +136,9 @@ public final class StoryItemSetContainerComponent: Component {
if lhs.isPanning != rhs.isPanning { if lhs.isPanning != rhs.isPanning {
return false return false
} }
if lhs.verticalPanFraction != rhs.verticalPanFraction {
return false
}
return true return true
} }
@ -205,7 +211,6 @@ public final class StoryItemSetContainerComponent: Component {
let closeButtonIconView: UIImageView let closeButtonIconView: UIImageView
let navigationStrip = ComponentView<MediaNavigationStripComponent.EnvironmentType>() let navigationStrip = ComponentView<MediaNavigationStripComponent.EnvironmentType>()
let inlineActions = ComponentView<Empty>()
var centerInfoItem: InfoItem? var centerInfoItem: InfoItem?
var rightInfoItem: InfoItem? var rightInfoItem: InfoItem?
@ -228,7 +233,6 @@ public final class StoryItemSetContainerComponent: Component {
var preloadContexts: [AnyHashable: Disposable] = [:] var preloadContexts: [AnyHashable: Disposable] = [:]
var displayReactions: Bool = false
var reactionItems: [ReactionItem]? var reactionItems: [ReactionItem]?
var reactionContextNode: ReactionContextNode? var reactionContextNode: ReactionContextNode?
weak var disappearingReactionContextNode: ReactionContextNode? weak var disappearingReactionContextNode: ReactionContextNode?
@ -423,11 +427,7 @@ public final class StoryItemSetContainerComponent: Component {
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state, let component = self.component, let itemLayout = self.itemLayout { if case .ended = recognizer.state, let component = self.component, let itemLayout = self.itemLayout {
if hasFirstResponder(self) { if hasFirstResponder(self) {
self.displayReactions = false
self.endEditing(true) self.endEditing(true)
} else if self.displayReactions {
self.displayReactions = false
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
} else if self.displayViewList { } else if self.displayViewList {
self.displayViewList = false self.displayViewList = false
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
@ -476,7 +476,7 @@ public final class StoryItemSetContainerComponent: Component {
guard let component = self.component else { guard let component = self.component else {
return false return false
} }
if self.inputPanelExternalState.isEditing || component.isProgressPaused || self.displayReactions || self.actionSheet != nil || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.displayViewList { if self.inputPanelExternalState.isEditing || component.isProgressPaused || self.actionSheet != nil || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.displayViewList {
return true return true
} }
if let captionItem = self.captionItem, captionItem.externalState.expandFraction > 0.0 { if let captionItem = self.captionItem, captionItem.externalState.expandFraction > 0.0 {
@ -576,6 +576,12 @@ public final class StoryItemSetContainerComponent: Component {
} }
} }
func activateInput() {
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
inputPanelView.activateInput()
}
}
func animateIn(transitionIn: StoryContainerScreen.TransitionIn) { func animateIn(transitionIn: StoryContainerScreen.TransitionIn) {
self.closeButton.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2, delay: 0.12, timingFunction: kCAMediaTimingFunctionSpring) self.closeButton.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2, delay: 0.12, timingFunction: kCAMediaTimingFunctionSpring)
@ -835,9 +841,6 @@ public final class StoryItemSetContainerComponent: Component {
} }
self.reactionItems = reactionItems self.reactionItems = reactionItems
if self.displayReactions {
self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut)))
}
}) })
} }
@ -966,29 +969,13 @@ public final class StoryItemSetContainerComponent: Component {
} }
self.sendMessageContext.presentAttachmentMenu(view: self, subject: .default) self.sendMessageContext.presentAttachmentMenu(view: self, subject: .default)
}, },
reactionAction: { [weak self] sourceView in timeoutAction: nil,
guard let self, let component = self.component else { forwardAction: component.slice.item.storyItem.isPublic ? { [weak self] in
guard let self else {
return return
} }
self.sendMessageContext.performShareAction(view: self)
let _ = (allowedStoryReactions(context: component.context) } : nil,
|> deliverOnMainQueue).start(next: { [weak self] reactionItems in
guard let self, let component = self.component else {
return
}
component.controller()?.forEachController { c in
if let c = c as? UndoOverlayController {
c.dismiss()
}
return true
}
self.displayReactions = !self.displayReactions
self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut)))
})
},
timeoutAction: nil,
audioRecorder: self.sendMessageContext.audioRecorderValue, audioRecorder: self.sendMessageContext.audioRecorderValue,
videoRecordingStatus: self.sendMessageContext.videoRecorderValue?.audioStatus, videoRecordingStatus: self.sendMessageContext.videoRecorderValue?.audioStatus,
isRecordingLocked: self.sendMessageContext.isMediaRecordingLocked, isRecordingLocked: self.sendMessageContext.isMediaRecordingLocked,
@ -1457,7 +1444,14 @@ public final class StoryItemSetContainerComponent: Component {
if inputPanelView.superview == nil { if inputPanelView.superview == nil {
self.addSubview(inputPanelView) self.addSubview(inputPanelView)
} }
inputPanelTransition.setFrame(view: inputPanelView, frame: inputPanelFrame)
var inputPanelOffset: CGFloat = 0.0
if focusedItem?.isMy == false && !self.inputPanelExternalState.isEditing {
let bandingOffset = scrollingRubberBandingOffset(offset: component.verticalPanFraction * availableSize.height, bandingStart: 0.0, range: 10.0)
inputPanelOffset = -max(0.0, min(10.0, bandingOffset))
}
inputPanelTransition.setFrame(view: inputPanelView, frame: inputPanelFrame.offsetBy(dx: 0.0, dy: inputPanelOffset))
transition.setAlpha(view: inputPanelView, alpha: inputPanelAlpha) transition.setAlpha(view: inputPanelView, alpha: inputPanelAlpha)
} }
@ -1503,7 +1497,7 @@ public final class StoryItemSetContainerComponent: Component {
let reactionsAnchorRect = CGRect(origin: CGPoint(x: inputPanelFrame.maxX - 40.0, y: inputPanelFrame.minY + 9.0), size: CGSize(width: 32.0, height: 32.0)).insetBy(dx: -4.0, dy: -4.0) let reactionsAnchorRect = CGRect(origin: CGPoint(x: inputPanelFrame.maxX - 40.0, y: inputPanelFrame.minY + 9.0), size: CGSize(width: 32.0, height: 32.0)).insetBy(dx: -4.0, dy: -4.0)
var effectiveDisplayReactions = self.displayReactions var effectiveDisplayReactions = false
if self.inputPanelExternalState.isEditing && !self.inputPanelExternalState.hasText { if self.inputPanelExternalState.isEditing && !self.inputPanelExternalState.hasText {
effectiveDisplayReactions = true effectiveDisplayReactions = true
} }
@ -1619,7 +1613,6 @@ public final class StoryItemSetContainerComponent: Component {
} }
}) })
self.displayReactions = false
if hasFirstResponder(self) { if hasFirstResponder(self) {
self.endEditing(true) self.endEditing(true)
} }
@ -1781,50 +1774,6 @@ public final class StoryItemSetContainerComponent: Component {
transition.setFrame(view: navigationStripView, frame: CGRect(origin: CGPoint(x: navigationStripSideInset, y: navigationStripTopInset), size: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0))) transition.setFrame(view: navigationStripView, frame: CGRect(origin: CGPoint(x: navigationStripSideInset, y: navigationStripTopInset), size: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0)))
transition.setAlpha(view: navigationStripView, alpha: (component.hideUI || self.displayViewList) ? 0.0 : 1.0) transition.setAlpha(view: navigationStripView, alpha: (component.hideUI || self.displayViewList) ? 0.0 : 1.0)
} }
var items: [StoryActionsComponent.Item] = []
let _ = focusedItem
items.append(StoryActionsComponent.Item(
kind: .share,
isActivated: false
))
let inlineActionsSize = self.inlineActions.update(
transition: transition,
component: AnyComponent(StoryActionsComponent(
items: items,
action: { [weak self] item in
guard let self else {
return
}
self.sendMessageContext.performInlineAction(view: self, item: item)
}
)),
environment: {},
containerSize: contentFrame.size
)
if let inlineActionsView = self.inlineActions.view {
if inlineActionsView.superview == nil {
self.contentContainerView.addSubview(inlineActionsView)
}
transition.setFrame(view: inlineActionsView, frame: CGRect(origin: CGPoint(x: contentFrame.width - 10.0 - inlineActionsSize.width, y: contentFrame.height - 10.0 - inlineActionsSize.height), size: inlineActionsSize))
var inlineActionsAlpha: CGFloat = inputPanelIsOverlay ? 0.0 : 1.0
if self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil {
inlineActionsAlpha = 0.0
}
if self.displayReactions {
inlineActionsAlpha = 0.0
}
if component.hideUI || self.displayViewList {
inlineActionsAlpha = 0.0
}
if !component.slice.item.storyItem.isPublic {
inlineActionsAlpha = 0.0
}
transition.setAlpha(view: inlineActionsView, alpha: inlineActionsAlpha)
}
} }
component.externalState.derivedMediaSize = contentFrame.size component.externalState.derivedMediaSize = contentFrame.size

View File

@ -272,7 +272,7 @@ final class StoryItemSetContainerSendMessage {
} }
} }
func performInlineAction(view: StoryItemSetContainerComponent.View, item: StoryActionsComponent.Item) { func performShareAction(view: StoryItemSetContainerComponent.View) {
guard let component = view.component else { guard let component = view.component else {
return return
} }
@ -284,23 +284,18 @@ final class StoryItemSetContainerSendMessage {
return return
} }
switch item.kind { /*let linkPromise = Promise<String?, NoError>()
case .like: linkPromise.set(component.context.engine.messages.exportStoryLink(peerId: peerId, id: focusedItem.storyItem.id))*/
return
case .share: let shareController = ShareController(
/*let linkPromise = Promise<String?, NoError>() context: component.context,
linkPromise.set(component.context.engine.messages.exportStoryLink(peerId: peerId, id: focusedItem.storyItem.id))*/ subject: .media(AnyMediaReference.standalone(media: TelegramMediaStory(storyId: StoryId(peerId: peerId, id: focusedItem.storyItem.id)))),
externalShare: false,
let shareController = ShareController( immediateExternalShare: false,
context: component.context, updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }),
subject: .media(AnyMediaReference.standalone(media: TelegramMediaStory(storyId: StoryId(peerId: peerId, id: focusedItem.storyItem.id)))), component.context.sharedContext.presentationData)
externalShare: false, )
immediateExternalShare: false, controller.present(shareController, in: .window(.root))
updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }),
component.context.sharedContext.presentationData)
)
controller.present(shareController, in: .window(.root))
}
} }
private func clearInputText(view: StoryItemSetContainerComponent.View) { private func clearInputText(view: StoryItemSetContainerComponent.View) {

View File

@ -126,6 +126,10 @@ public final class TextFieldComponent: Component {
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged)))
} }
public func activateInput() {
self.textView.becomeFirstResponder()
}
func update(component: TextFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize { func update(component: TextFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
self.component = component self.component = component
self.state = state self.state = state

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_forward.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}