[WIP] Stories

This commit is contained in:
Ali 2023-06-12 21:11:51 +03:00
parent d1817dca18
commit ab8d40b940
3 changed files with 341 additions and 34 deletions

View File

@ -593,10 +593,18 @@ public final class StoryItemSetContainerComponent: Component {
} }
func activateInput() { 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 { if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
inputPanelView.activateInput() 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)
@ -621,6 +629,16 @@ public final class StoryItemSetContainerComponent: Component {
) )
footerPanelView.layer.animateAlpha(from: 0.0, to: footerPanelView.alpha, duration: 0.28) 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 { if let captionItemView = self.captionItem?.view.view {
captionItemView.layer.animatePosition( captionItemView.layer.animatePosition(
from: CGPoint(x: 0.0, y: self.bounds.height - captionItemView.frame.minY), 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) 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 { if let captionItemView = self.captionItem?.view.view {
captionItemView.layer.animatePosition( captionItemView.layer.animatePosition(
from: CGPoint(), from: CGPoint(),
@ -1270,6 +1298,13 @@ public final class StoryItemSetContainerComponent: Component {
self.viewList = viewList self.viewList = viewList
} }
let outerExpansionFraction: CGFloat
if self.displayViewList {
outerExpansionFraction = 1.0
} else {
outerExpansionFraction = component.verticalPanFraction
}
viewList.view.parentState = state viewList.view.parentState = state
let viewListSize = viewList.view.update( let viewListSize = viewList.view.update(
transition: viewListTransition, transition: viewListTransition,
@ -1280,13 +1315,235 @@ public final class StoryItemSetContainerComponent: Component {
strings: component.strings, strings: component.strings,
safeInsets: component.safeInsets, safeInsets: component.safeInsets,
storyItem: component.slice.item.storyItem, storyItem: component.slice.item.storyItem,
outerExpansionFraction: component.verticalPanFraction, outerExpansionFraction: outerExpansionFraction,
close: { [weak self] in close: { [weak self] in
guard let self else { guard let self else {
return return
} }
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)))
},
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: {}, environment: {},

View File

@ -29,6 +29,9 @@ final class StoryItemSetViewListComponent: Component {
let storyItem: EngineStoryItem let storyItem: EngineStoryItem
let outerExpansionFraction: CGFloat let outerExpansionFraction: CGFloat
let close: () -> Void let close: () -> Void
let expandViewStats: () -> Void
let deleteAction: () -> Void
let moreAction: (UIView, ContextGesture?) -> Void
init( init(
externalState: ExternalState, externalState: ExternalState,
@ -38,7 +41,10 @@ final class StoryItemSetViewListComponent: Component {
safeInsets: UIEdgeInsets, safeInsets: UIEdgeInsets,
storyItem: EngineStoryItem, storyItem: EngineStoryItem,
outerExpansionFraction: CGFloat, outerExpansionFraction: CGFloat,
close: @escaping () -> Void close: @escaping () -> Void,
expandViewStats: @escaping () -> Void,
deleteAction: @escaping () -> Void,
moreAction: @escaping (UIView, ContextGesture?) -> Void
) { ) {
self.externalState = externalState self.externalState = externalState
self.context = context self.context = context
@ -48,6 +54,9 @@ final class StoryItemSetViewListComponent: Component {
self.storyItem = storyItem self.storyItem = storyItem
self.outerExpansionFraction = outerExpansionFraction self.outerExpansionFraction = outerExpansionFraction
self.close = close self.close = close
self.expandViewStats = expandViewStats
self.deleteAction = deleteAction
self.moreAction = moreAction
} }
static func ==(lhs: StoryItemSetViewListComponent, rhs: StoryItemSetViewListComponent) -> Bool { static func ==(lhs: StoryItemSetViewListComponent, rhs: StoryItemSetViewListComponent) -> Bool {
@ -284,6 +293,11 @@ final class StoryItemSetViewListComponent: Component {
} }
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 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) { if !self.backgroundView.frame.contains(point) {
return nil return nil
} }
@ -478,7 +492,7 @@ final class StoryItemSetViewListComponent: Component {
self.component = component self.component = component
self.state = state self.state = state
let minimizedHeight = min(availableSize.height, 488.0) let minimizedHeight = min(availableSize.height, 500.0)
if themeUpdated { if themeUpdated {
self.backgroundView.backgroundColor = component.theme.rootController.navigationBar.blurredBackgroundColor self.backgroundView.backgroundColor = component.theme.rootController.navigationBar.blurredBackgroundColor
@ -512,7 +526,7 @@ final class StoryItemSetViewListComponent: Component {
let sideInset: CGFloat = 16.0 let sideInset: CGFloat = 16.0
let navigationHeight: CGFloat = 56.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) transition.setFrame(view: self.navigationBarBackground, frame: navigationBarFrame)
self.navigationBarBackground.update(size: navigationBarFrame.size, cornerRadius: 10.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition.containedViewLayoutTransition) 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) 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( let navigationPanelSize = self.navigationPanel.update(
transition: transition, transition: transition,
component: AnyComponent(StoryFooterPanelComponent( component: AnyComponent(StoryFooterPanelComponent(
context: component.context, context: component.context,
storyItem: component.storyItem, storyItem: component.storyItem,
expandFraction: dismissFraction,
expandViewStats: { [weak self] in expandViewStats: { [weak self] in
guard let self else { guard let self, let component = self.component else {
return return
} }
let _ = self component.expandViewStats()
}, },
deleteAction: { [weak self] in deleteAction: { [weak self] in
guard let self, let component = self.component else { guard let self, let component = self.component else {
return return
} }
let _ = component component.deleteAction()
}, },
moreAction: { [weak self] sourceView, gesture in moreAction: { [weak self] sourceView, gesture in
guard let self, let component = self.component else { guard let self, let component = self.component else {
return return
} }
let _ = component component.moreAction(sourceView, gesture)
} }
)), )),
environment: {}, environment: {},
@ -573,21 +599,12 @@ final class StoryItemSetViewListComponent: Component {
self.addSubview(navigationPanelView) self.addSubview(navigationPanelView)
} }
let expandedNavigationPanelFrame = CGRect(origin: navigationBarFrame.origin, 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: availableSize.height - navigationPanelSize.height), 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))) 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( let measureItemSize = self.measureItem.update(
@ -663,18 +680,12 @@ final class StoryItemSetViewListComponent: Component {
self.ignoreScrolling = false self.ignoreScrolling = false
self.updateScrolling(transition: transition) 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)) transition.setBoundsOrigin(view: self, origin: CGPoint(x: 0.0, y: dismissOffsetY))
component.externalState.minimizedHeight = minimizedHeight 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 return availableSize
} }

View File

@ -12,6 +12,7 @@ import MoreHeaderButton
public final class StoryFooterPanelComponent: Component { public final class StoryFooterPanelComponent: Component {
public let context: AccountContext public let context: AccountContext
public let storyItem: EngineStoryItem? public let storyItem: EngineStoryItem?
public let expandFraction: CGFloat
public let expandViewStats: () -> Void public let expandViewStats: () -> Void
public let deleteAction: () -> Void public let deleteAction: () -> Void
public let moreAction: (UIView, ContextGesture?) -> Void public let moreAction: (UIView, ContextGesture?) -> Void
@ -19,6 +20,7 @@ public final class StoryFooterPanelComponent: Component {
public init( public init(
context: AccountContext, context: AccountContext,
storyItem: EngineStoryItem?, storyItem: EngineStoryItem?,
expandFraction: CGFloat,
expandViewStats: @escaping () -> Void, expandViewStats: @escaping () -> Void,
deleteAction: @escaping () -> Void, deleteAction: @escaping () -> Void,
moreAction: @escaping (UIView, ContextGesture?) -> Void moreAction: @escaping (UIView, ContextGesture?) -> Void
@ -26,6 +28,7 @@ public final class StoryFooterPanelComponent: Component {
self.context = context self.context = context
self.storyItem = storyItem self.storyItem = storyItem
self.expandViewStats = expandViewStats self.expandViewStats = expandViewStats
self.expandFraction = expandFraction
self.deleteAction = deleteAction self.deleteAction = deleteAction
self.moreAction = moreAction self.moreAction = moreAction
} }
@ -37,12 +40,16 @@ public final class StoryFooterPanelComponent: Component {
if lhs.storyItem != rhs.storyItem { if lhs.storyItem != rhs.storyItem {
return false return false
} }
if lhs.expandFraction != rhs.expandFraction {
return false
}
return true return true
} }
public final class View: UIView { public final class View: UIView {
private let viewStatsButton: HighlightableButton private let viewStatsButton: HighlightableButton
private let viewStatsText = ComponentView<Empty>() private let viewStatsText = ComponentView<Empty>()
private let viewStatsExpandedText = ComponentView<Empty>()
private let deleteButton = ComponentView<Empty>() private let deleteButton = ComponentView<Empty>()
private var moreButton: MoreHeaderButton? 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) let avatarsNodeFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - avatarsSize.height) * 0.5)), size: avatarsSize)
self.avatarsNode.frame = avatarsNodeFrame 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 { if !avatarsSize.width.isZero {
leftOffset = avatarsNodeFrame.maxX + avatarSpacing leftOffset = avatarsNodeFrame.maxX + avatarSpacing
} }
@ -124,18 +133,45 @@ public final class StoryFooterPanelComponent: Component {
environment: {}, environment: {},
containerSize: CGSize(width: availableSize.width, height: size.height) 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 let viewStatsTextView = self.viewStatsText.view {
if viewStatsTextView.superview == nil { if viewStatsTextView.superview == nil {
viewStatsTextView.layer.anchorPoint = CGPoint()
viewStatsTextView.isUserInteractionEnabled = false viewStatsTextView.isUserInteractionEnabled = false
self.viewStatsButton.addSubview(viewStatsTextView) 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.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))) 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 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)) 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 rightContentOffset -= deleteButtonSize.width + 8.0
transition.setAlpha(view: deleteButtonView, alpha: pow(1.0 - component.expandFraction, 1.0))
} }
let moreButton: MoreHeaderButton let moreButton: MoreHeaderButton
@ -197,6 +235,7 @@ public final class StoryFooterPanelComponent: Component {
let buttonSize = CGSize(width: 32.0, height: 44.0) let buttonSize = CGSize(width: 32.0, height: 44.0)
moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: .white))) 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.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 return size
} }