mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
[WIP] Stories
This commit is contained in:
parent
d1817dca18
commit
ab8d40b940
@ -593,10 +593,18 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
|
||||
func activateInput() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
if component.slice.peer.id == component.context.account.peerId {
|
||||
self.displayViewList = true
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
} else {
|
||||
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||
inputPanelView.activateInput()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn(transitionIn: StoryContainerScreen.TransitionIn) {
|
||||
self.closeButton.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2, delay: 0.12, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
@ -621,6 +629,16 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
)
|
||||
footerPanelView.layer.animateAlpha(from: 0.0, to: footerPanelView.alpha, duration: 0.28)
|
||||
}
|
||||
if let viewListView = self.viewList?.view.view {
|
||||
viewListView.layer.animatePosition(
|
||||
from: CGPoint(x: 0.0, y: self.bounds.height - self.contentContainerView.frame.maxY),
|
||||
to: CGPoint(),
|
||||
duration: 0.3,
|
||||
timingFunction: kCAMediaTimingFunctionSpring,
|
||||
additive: true
|
||||
)
|
||||
viewListView.layer.animateAlpha(from: 0.0, to: viewListView.alpha, duration: 0.28)
|
||||
}
|
||||
if let captionItemView = self.captionItem?.view.view {
|
||||
captionItemView.layer.animatePosition(
|
||||
from: CGPoint(x: 0.0, y: self.bounds.height - captionItemView.frame.minY),
|
||||
@ -704,6 +722,16 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
)
|
||||
footerPanelView.layer.animateAlpha(from: footerPanelView.alpha, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
}
|
||||
if let viewListView = self.viewList?.view.view {
|
||||
viewListView.layer.animatePosition(
|
||||
from: CGPoint(),
|
||||
to: CGPoint(x: 0.0, y: self.bounds.height - self.contentContainerView.frame.maxY),
|
||||
duration: 0.3,
|
||||
timingFunction: kCAMediaTimingFunctionSpring,
|
||||
additive: true
|
||||
)
|
||||
viewListView.layer.animateAlpha(from: 0.0, to: viewListView.alpha, duration: 0.28)
|
||||
}
|
||||
if let captionItemView = self.captionItem?.view.view {
|
||||
captionItemView.layer.animatePosition(
|
||||
from: CGPoint(),
|
||||
@ -1270,6 +1298,13 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.viewList = viewList
|
||||
}
|
||||
|
||||
let outerExpansionFraction: CGFloat
|
||||
if self.displayViewList {
|
||||
outerExpansionFraction = 1.0
|
||||
} else {
|
||||
outerExpansionFraction = component.verticalPanFraction
|
||||
}
|
||||
|
||||
viewList.view.parentState = state
|
||||
let viewListSize = viewList.view.update(
|
||||
transition: viewListTransition,
|
||||
@ -1280,13 +1315,235 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
strings: component.strings,
|
||||
safeInsets: component.safeInsets,
|
||||
storyItem: component.slice.item.storyItem,
|
||||
outerExpansionFraction: component.verticalPanFraction,
|
||||
outerExpansionFraction: outerExpansionFraction,
|
||||
close: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.displayViewList = false
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
},
|
||||
expandViewStats: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if !self.displayViewList {
|
||||
self.displayViewList = true
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
}
|
||||
},
|
||||
deleteAction: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: "Delete", color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.delete()
|
||||
|
||||
/*if let currentSlice = self.currentSlice, let index = currentSlice.items.firstIndex(where: { $0.id == focusedItemId }) {
|
||||
let item = currentSlice.items[index]
|
||||
|
||||
if currentSlice.items.count == 1 {
|
||||
component.navigateToItemSet(.next)
|
||||
} else {
|
||||
var nextIndex: Int = index + 1
|
||||
if nextIndex >= currentSlice.items.count {
|
||||
nextIndex = currentSlice.items.count - 1
|
||||
}
|
||||
self.focusedItemId = currentSlice.items[nextIndex].id
|
||||
|
||||
currentSlice.items[nextIndex].markAsSeen?()
|
||||
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
|
||||
item.delete?()
|
||||
}*/
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
|
||||
actionSheet.dismissed = { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.actionSheet = nil
|
||||
self.updateIsProgressPaused()
|
||||
}
|
||||
self.actionSheet = actionSheet
|
||||
self.updateIsProgressPaused()
|
||||
|
||||
component.presentController(actionSheet)
|
||||
},
|
||||
moreAction: { [weak self] sourceView, gesture in
|
||||
guard let self, let component = self.component, let controller = component.controller() else {
|
||||
return
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
let additionalCount = component.slice.item.storyItem.privacy?.additionallyIncludePeers.count ?? 0
|
||||
|
||||
let privacyText: String
|
||||
switch component.slice.item.storyItem.privacy?.base {
|
||||
case .closeFriends:
|
||||
if additionalCount != 0 {
|
||||
privacyText = "Close Friends (+\(additionalCount)"
|
||||
} else {
|
||||
privacyText = "Close Friends"
|
||||
}
|
||||
case .contacts:
|
||||
if additionalCount != 0 {
|
||||
privacyText = "Contacts (+\(additionalCount)"
|
||||
} else {
|
||||
privacyText = "Contacts"
|
||||
}
|
||||
case .nobody:
|
||||
if additionalCount != 0 {
|
||||
if additionalCount == 1 {
|
||||
privacyText = "\(additionalCount) Person"
|
||||
} else {
|
||||
privacyText = "\(additionalCount) People"
|
||||
}
|
||||
} else {
|
||||
privacyText = "Only Me"
|
||||
}
|
||||
default:
|
||||
privacyText = "Everyone"
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Who can see", textLayout: .secondLineWithValue(privacyText), icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Channels"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openItemPrivacySettings()
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Story", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openStoryEditing()
|
||||
})))
|
||||
|
||||
items.append(.separator)
|
||||
|
||||
component.controller()?.forEachController { c in
|
||||
if let c = c as? UndoOverlayController {
|
||||
c.dismiss()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: component.slice.item.storyItem.isPinned ? "Remove from profile" : "Save to profile", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: component.slice.item.storyItem.isPinned ? "Chat/Context Menu/Check" : "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).start()
|
||||
|
||||
if component.slice.item.storyItem.isPinned {
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
self.component?.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: nil, text: "Story removed from your profile", timeout: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { _ in return false }
|
||||
))
|
||||
} else {
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
self.component?.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: "Story saved to your profile", text: "Saved stories can be viewed by others on your profile until you remove them.", timeout: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { _ in return false }
|
||||
))
|
||||
}
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: "Save image", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, a in
|
||||
a(.default)
|
||||
})))
|
||||
|
||||
if component.slice.item.storyItem.isPublic {
|
||||
items.append(.action(ContextMenuActionItem(text: "Copy link", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] link in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
if let link {
|
||||
UIPasteboard.general.string = link
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
component.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .linkCopied(text: "Link copied."),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { _ in return false }
|
||||
))
|
||||
}
|
||||
})
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, a in
|
||||
a(.default)
|
||||
})))
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
contextController.dismissed = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.contextController = nil
|
||||
self.updateIsProgressPaused()
|
||||
}
|
||||
self.contextController = contextController
|
||||
self.updateIsProgressPaused()
|
||||
controller.present(contextController, in: .window(.root))
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
|
@ -29,6 +29,9 @@ final class StoryItemSetViewListComponent: Component {
|
||||
let storyItem: EngineStoryItem
|
||||
let outerExpansionFraction: CGFloat
|
||||
let close: () -> Void
|
||||
let expandViewStats: () -> Void
|
||||
let deleteAction: () -> Void
|
||||
let moreAction: (UIView, ContextGesture?) -> Void
|
||||
|
||||
init(
|
||||
externalState: ExternalState,
|
||||
@ -38,7 +41,10 @@ final class StoryItemSetViewListComponent: Component {
|
||||
safeInsets: UIEdgeInsets,
|
||||
storyItem: EngineStoryItem,
|
||||
outerExpansionFraction: CGFloat,
|
||||
close: @escaping () -> Void
|
||||
close: @escaping () -> Void,
|
||||
expandViewStats: @escaping () -> Void,
|
||||
deleteAction: @escaping () -> Void,
|
||||
moreAction: @escaping (UIView, ContextGesture?) -> Void
|
||||
) {
|
||||
self.externalState = externalState
|
||||
self.context = context
|
||||
@ -48,6 +54,9 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.storyItem = storyItem
|
||||
self.outerExpansionFraction = outerExpansionFraction
|
||||
self.close = close
|
||||
self.expandViewStats = expandViewStats
|
||||
self.deleteAction = deleteAction
|
||||
self.moreAction = moreAction
|
||||
}
|
||||
|
||||
static func ==(lhs: StoryItemSetViewListComponent, rhs: StoryItemSetViewListComponent) -> Bool {
|
||||
@ -284,6 +293,11 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if let navigationPanelView = self.navigationPanel.view {
|
||||
if let result = navigationPanelView.hitTest(self.convert(point, to: navigationPanelView), with: event) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
if !self.backgroundView.frame.contains(point) {
|
||||
return nil
|
||||
}
|
||||
@ -478,7 +492,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let minimizedHeight = min(availableSize.height, 488.0)
|
||||
let minimizedHeight = min(availableSize.height, 500.0)
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundView.backgroundColor = component.theme.rootController.navigationBar.blurredBackgroundColor
|
||||
@ -512,7 +526,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
let sideInset: CGFloat = 16.0
|
||||
|
||||
let navigationHeight: CGFloat = 56.0
|
||||
let navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - minimizedHeight), size: CGSize(width: availableSize.width, height: navigationHeight))
|
||||
let navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - minimizedHeight + 12.0), size: CGSize(width: availableSize.width, height: navigationHeight))
|
||||
transition.setFrame(view: self.navigationBarBackground, frame: navigationBarFrame)
|
||||
self.navigationBarBackground.update(size: navigationBarFrame.size, cornerRadius: 10.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition.containedViewLayoutTransition)
|
||||
|
||||
@ -540,29 +554,41 @@ final class StoryItemSetViewListComponent: Component {
|
||||
transition.setFrame(view: navigationLeftButtonView, frame: navigationLeftButtonFrame)
|
||||
}
|
||||
|
||||
let expansionOffset = availableSize.height - self.navigationBarBackground.frame.minY
|
||||
|
||||
var dismissOffsetY: CGFloat = 0.0
|
||||
if let dismissPanState = self.dismissPanState {
|
||||
dismissOffsetY = -dismissPanState.accumulatedOffset
|
||||
}
|
||||
|
||||
dismissOffsetY -= (1.0 - component.outerExpansionFraction) * expansionOffset
|
||||
|
||||
let dismissFraction: CGFloat = 1.0 - max(0.0, min(1.0, -dismissOffsetY / expansionOffset))
|
||||
|
||||
let navigationPanelSize = self.navigationPanel.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(StoryFooterPanelComponent(
|
||||
context: component.context,
|
||||
storyItem: component.storyItem,
|
||||
expandFraction: dismissFraction,
|
||||
expandViewStats: { [weak self] in
|
||||
guard let self else {
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
component.expandViewStats()
|
||||
},
|
||||
deleteAction: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
let _ = component
|
||||
component.deleteAction()
|
||||
},
|
||||
moreAction: { [weak self] sourceView, gesture in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = component
|
||||
component.moreAction(sourceView, gesture)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
@ -573,21 +599,12 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.addSubview(navigationPanelView)
|
||||
}
|
||||
|
||||
let expandedNavigationPanelFrame = CGRect(origin: navigationBarFrame.origin, size: navigationPanelSize)
|
||||
let collapsedNavigationPanelFrame = CGRect(origin: CGPoint(x: navigationBarFrame.minX, y: availableSize.height - navigationPanelSize.height), size: navigationPanelSize)
|
||||
let expandedNavigationPanelFrame = CGRect(origin: CGPoint(x: navigationBarFrame.minX, y: navigationBarFrame.minY + 4.0), size: navigationPanelSize)
|
||||
let collapsedNavigationPanelFrame = CGRect(origin: CGPoint(x: navigationBarFrame.minX, y: navigationBarFrame.minY - navigationPanelSize.height - component.safeInsets.bottom - 1.0), size: navigationPanelSize)
|
||||
|
||||
transition.setFrame(view: navigationPanelView, frame: collapsedNavigationPanelFrame.interpolate(to: expandedNavigationPanelFrame, amount: component.outerExpansionFraction))
|
||||
transition.setFrame(view: navigationPanelView, frame: collapsedNavigationPanelFrame.interpolate(to: expandedNavigationPanelFrame, amount: dismissFraction))
|
||||
}
|
||||
|
||||
/*let navigationTitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - navigationTitleSize.width) * 0.5), y: navigationBarFrame.minY + floor((navigationBarFrame.height - navigationTitleSize.height) * 0.5)), size: navigationTitleSize)
|
||||
if let navigationTitleView = self.navigationTitle.view {
|
||||
if navigationTitleView.superview == nil {
|
||||
self.addSubview(navigationTitleView)
|
||||
}
|
||||
transition.setPosition(view: navigationTitleView, position: navigationTitleFrame.center)
|
||||
transition.setBounds(view: navigationTitleView, bounds: CGRect(origin: CGPoint(), size: navigationTitleFrame.size))
|
||||
}*/
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarFrame.maxY), size: CGSize(width: availableSize.width, height: availableSize.height)))
|
||||
|
||||
let measureItemSize = self.measureItem.update(
|
||||
@ -663,18 +680,12 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.ignoreScrolling = false
|
||||
self.updateScrolling(transition: transition)
|
||||
|
||||
var dismissOffsetY: CGFloat = 0.0
|
||||
if let dismissPanState = self.dismissPanState {
|
||||
dismissOffsetY = -dismissPanState.accumulatedOffset
|
||||
}
|
||||
|
||||
let expansionOffset = availableSize.height - self.navigationBarBackground.frame.minY
|
||||
dismissOffsetY -= (1.0 - component.outerExpansionFraction) * expansionOffset
|
||||
|
||||
transition.setBoundsOrigin(view: self, origin: CGPoint(x: 0.0, y: dismissOffsetY))
|
||||
|
||||
component.externalState.minimizedHeight = minimizedHeight
|
||||
component.externalState.effectiveHeight = min(minimizedHeight, max(0.0, minimizedHeight + dismissOffsetY))
|
||||
|
||||
let effectiveHeight: CGFloat = minimizedHeight * dismissFraction + (1.0 - dismissFraction) * (60.0 + component.safeInsets.bottom + 1.0)
|
||||
component.externalState.effectiveHeight = min(minimizedHeight, max(0.0, effectiveHeight))
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import MoreHeaderButton
|
||||
public final class StoryFooterPanelComponent: Component {
|
||||
public let context: AccountContext
|
||||
public let storyItem: EngineStoryItem?
|
||||
public let expandFraction: CGFloat
|
||||
public let expandViewStats: () -> Void
|
||||
public let deleteAction: () -> Void
|
||||
public let moreAction: (UIView, ContextGesture?) -> Void
|
||||
@ -19,6 +20,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
public init(
|
||||
context: AccountContext,
|
||||
storyItem: EngineStoryItem?,
|
||||
expandFraction: CGFloat,
|
||||
expandViewStats: @escaping () -> Void,
|
||||
deleteAction: @escaping () -> Void,
|
||||
moreAction: @escaping (UIView, ContextGesture?) -> Void
|
||||
@ -26,6 +28,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
self.context = context
|
||||
self.storyItem = storyItem
|
||||
self.expandViewStats = expandViewStats
|
||||
self.expandFraction = expandFraction
|
||||
self.deleteAction = deleteAction
|
||||
self.moreAction = moreAction
|
||||
}
|
||||
@ -37,12 +40,16 @@ public final class StoryFooterPanelComponent: Component {
|
||||
if lhs.storyItem != rhs.storyItem {
|
||||
return false
|
||||
}
|
||||
if lhs.expandFraction != rhs.expandFraction {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let viewStatsButton: HighlightableButton
|
||||
private let viewStatsText = ComponentView<Empty>()
|
||||
private let viewStatsExpandedText = ComponentView<Empty>()
|
||||
private let deleteButton = ComponentView<Empty>()
|
||||
private var moreButton: MoreHeaderButton?
|
||||
|
||||
@ -98,6 +105,8 @@ public final class StoryFooterPanelComponent: Component {
|
||||
|
||||
let avatarsNodeFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - avatarsSize.height) * 0.5)), size: avatarsSize)
|
||||
self.avatarsNode.frame = avatarsNodeFrame
|
||||
//transition.setScale(view: self.avatarsNode.view, scale: CGFloat(1.0).interpolate(to: 0.001, amount: component.expandFraction))
|
||||
transition.setAlpha(view: self.avatarsNode.view, alpha: pow(1.0 - component.expandFraction, 1.0))
|
||||
if !avatarsSize.width.isZero {
|
||||
leftOffset = avatarsNodeFrame.maxX + avatarSpacing
|
||||
}
|
||||
@ -124,18 +133,45 @@ public final class StoryFooterPanelComponent: Component {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: size.height)
|
||||
)
|
||||
let viewStatsTextFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - viewStatsTextSize.height) * 0.5)), size: viewStatsTextSize)
|
||||
let viewStatsExpandedTextSize = self.viewStatsExpandedText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(text: viewsText, font: Font.semibold(17.0), color: .white)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: size.height)
|
||||
)
|
||||
|
||||
let viewStatsCollapsedFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - viewStatsTextSize.height) * 0.5)), size: viewStatsTextSize)
|
||||
let viewStatsExpandedFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - viewStatsExpandedTextSize.width) * 0.5), y: floor((size.height - viewStatsExpandedTextSize.height) * 0.5)), size: viewStatsExpandedTextSize)
|
||||
let viewStatsCurrentFrame = viewStatsCollapsedFrame.interpolate(to: viewStatsExpandedFrame, amount: component.expandFraction)
|
||||
|
||||
let viewStatsTextCenter = viewStatsCollapsedFrame.center.interpolate(to: viewStatsExpandedFrame.center, amount: component.expandFraction)
|
||||
|
||||
let viewStatsTextFrame = viewStatsCollapsedFrame.size.centered(around: viewStatsTextCenter)
|
||||
if let viewStatsTextView = self.viewStatsText.view {
|
||||
if viewStatsTextView.superview == nil {
|
||||
viewStatsTextView.layer.anchorPoint = CGPoint()
|
||||
viewStatsTextView.isUserInteractionEnabled = false
|
||||
self.viewStatsButton.addSubview(viewStatsTextView)
|
||||
}
|
||||
transition.setPosition(view: viewStatsTextView, position: viewStatsTextFrame.origin)
|
||||
transition.setPosition(view: viewStatsTextView, position: viewStatsTextFrame.center)
|
||||
transition.setBounds(view: viewStatsTextView, bounds: CGRect(origin: CGPoint(), size: viewStatsTextFrame.size))
|
||||
transition.setAlpha(view: viewStatsTextView, alpha: pow(1.0 - component.expandFraction, 1.2))
|
||||
transition.setScale(view: viewStatsTextView, scale: viewStatsCurrentFrame.width / viewStatsTextFrame.width)
|
||||
}
|
||||
|
||||
let viewStatsExpandedTextFrame = viewStatsExpandedFrame.size.centered(around: viewStatsTextCenter)
|
||||
if let viewStatsExpandedTextView = self.viewStatsExpandedText.view {
|
||||
if viewStatsExpandedTextView.superview == nil {
|
||||
viewStatsExpandedTextView.isUserInteractionEnabled = false
|
||||
self.viewStatsButton.addSubview(viewStatsExpandedTextView)
|
||||
}
|
||||
transition.setPosition(view: viewStatsExpandedTextView, position: viewStatsExpandedTextFrame.center)
|
||||
transition.setBounds(view: viewStatsExpandedTextView, bounds: CGRect(origin: CGPoint(), size: viewStatsExpandedTextFrame.size))
|
||||
transition.setAlpha(view: viewStatsExpandedTextView, alpha: pow(component.expandFraction, 1.2))
|
||||
transition.setScale(view: viewStatsExpandedTextView, scale: viewStatsCurrentFrame.width / viewStatsExpandedTextFrame.width)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.viewStatsButton, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: viewStatsTextFrame.maxX, height: viewStatsTextFrame.maxY + 8.0)))
|
||||
self.viewStatsButton.isUserInteractionEnabled = component.expandFraction == 0.0
|
||||
|
||||
var rightContentOffset: CGFloat = availableSize.width - 12.0
|
||||
|
||||
@ -162,6 +198,8 @@ public final class StoryFooterPanelComponent: Component {
|
||||
}
|
||||
transition.setFrame(view: deleteButtonView, frame: CGRect(origin: CGPoint(x: rightContentOffset - deleteButtonSize.width, y: floor((size.height - deleteButtonSize.height) * 0.5)), size: deleteButtonSize))
|
||||
rightContentOffset -= deleteButtonSize.width + 8.0
|
||||
|
||||
transition.setAlpha(view: deleteButtonView, alpha: pow(1.0 - component.expandFraction, 1.0))
|
||||
}
|
||||
|
||||
let moreButton: MoreHeaderButton
|
||||
@ -197,6 +235,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
let buttonSize = CGSize(width: 32.0, height: 44.0)
|
||||
moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: .white)))
|
||||
transition.setFrame(view: moreButton.view, frame: CGRect(origin: CGPoint(x: rightContentOffset - buttonSize.width, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize))
|
||||
transition.setAlpha(view: moreButton.view, alpha: pow(1.0 - component.expandFraction, 1.0))
|
||||
|
||||
return size
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user