This commit is contained in:
Isaac 2026-02-04 03:22:41 +08:00
commit d40e803719
16 changed files with 177 additions and 58 deletions

View file

@ -15703,7 +15703,8 @@ Error: %8$@";
"Gift.Craft.Select.Title" = "Select Gifts";
"Gift.Craft.Select.YourGifts" = "Your Gifts";
"Gift.Craft.Select.SaleGifts" = "Suitable Gifts On Sale";
"Gift.Craft.Select.SaleGiftsCount_1" = "%@ Suitable Gift On Sale";
"Gift.Craft.Select.SaleGiftsCount_any" = "%@ Suitable Gifts On Sale";
"Gift.Craft.Select.NoGiftsFromCollection" = "You don't have other gifts\nfrom this collection";
"Gift.Attribute.Rare" = "rare";

View file

@ -47,7 +47,7 @@ private enum IntentsSettingsSection: Int32 {
private enum IntentsSettingsControllerEntry: ItemListNodeEntry {
case accountHeader(PresentationTheme, String)
case account(PresentationTheme, EnginePeer, Bool, Int32)
case account(PresentationTheme, Account, EnginePeer, Bool, Int32)
case accountInfo(PresentationTheme, String)
case chatsHeader(PresentationTheme, String)
@ -80,7 +80,7 @@ private enum IntentsSettingsControllerEntry: ItemListNodeEntry {
switch self {
case .accountHeader:
return 0
case let .account(_, _, _, index):
case let .account(_, _, _, _, index):
return 1 + index
case .accountInfo:
return 1000
@ -115,8 +115,8 @@ private enum IntentsSettingsControllerEntry: ItemListNodeEntry {
} else {
return false
}
case let .account(lhsTheme, lhsPeer, lhsSelected, lhsIndex):
if case let .account(rhsTheme, rhsPeer, rhsSelected, rhsIndex) = rhs, lhsTheme === rhsTheme, lhsPeer == rhsPeer, lhsSelected == rhsSelected, lhsIndex == rhsIndex {
case let .account(lhsTheme, lhsAccount, lhsPeer, lhsSelected, lhsIndex):
if case let .account(rhsTheme, rhsAccount, rhsPeer, rhsSelected, rhsIndex) = rhs, lhsTheme === rhsTheme, lhsAccount === rhsAccount, lhsPeer == rhsPeer, lhsSelected == rhsSelected, lhsIndex == rhsIndex {
return true
} else {
return false
@ -200,8 +200,8 @@ private enum IntentsSettingsControllerEntry: ItemListNodeEntry {
switch self {
case let .accountHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .account(_, peer, selected, _):
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context.sharedContext.makeTempAccountContext(account: arguments.context.account), peer: peer, height: .generic, aliasHandling: .standard, nameStyle: .plain, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: false), revealOptions: nil, switchValue: ItemListPeerItemSwitch(value: selected, style: .check), enabled: true, selectable: true, sectionId: self.section, action: {
case let .account(_, account, peer, selected, _):
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context.sharedContext.makeTempAccountContext(account: account), peer: peer, height: .generic, aliasHandling: .standard, nameStyle: .plain, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: false), revealOptions: nil, switchValue: ItemListPeerItemSwitch(value: selected, style: .check), enabled: true, selectable: true, sectionId: self.section, action: {
arguments.updateSettings { $0.withUpdatedAccount(peer.id) }
}, setPeerIdWithRevealedOptions: { _, _ in}, removePeer: { _ in })
case let .accountInfo(_, text):
@ -251,8 +251,8 @@ private func intentsSettingsControllerEntries(context: AccountContext, presentat
if accounts.count > 1 {
entries.append(.accountHeader(presentationData.theme, presentationData.strings.IntentsSettings_MainAccount.uppercased()))
var index: Int32 = 0
for (_, peer) in accounts {
entries.append(.account(presentationData.theme, peer, peer.id == settings.account, index))
for (account, peer) in accounts {
entries.append(.account(presentationData.theme, account, peer, peer.id == settings.account, index))
index += 1
}
entries.append(.accountInfo(presentationData.theme, presentationData.strings.IntentsSettings_MainAccountInfo))

View file

@ -4241,11 +4241,15 @@ public final class ResaleGiftsContext {
}
}
public let forCrafting: Bool
public init(
account: Account,
giftId: Int64,
forCrafting: Bool
) {
self.forCrafting = forCrafting
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return ResaleGiftsContextImpl(queue: queue, account: account, giftId: giftId, forCrafting: forCrafting)

View file

@ -98,6 +98,10 @@ final class CraftTableComponent: Component {
super.init(frame: frame)
self.addSubview(self.animationView)
self.animationView.onStickerLaunch = {
HapticFeedback().impact(.soft)
}
}
required init?(coder: NSCoder) {
@ -110,26 +114,29 @@ final class CraftTableComponent: Component {
}
self.didSetupFinishAnimation = true
self.animationView.onFinishApproach = { [weak self] isUpsideDown in
guard let self else {
self.animationView.onFinishApproach = { [weak self] isUpsideDown, isClockwise in
guard let self, let component = self.component else {
return
}
self.isFailed = true
self.animationView.setSticker(nil, face: 0, mirror: false)
var availableStickers: [ComponentView<Empty>] = []
for gift in self.selectedGifts.values {
availableStickers.append(gift)
for (id, gift) in self.selectedGifts {
if let id = id.base as? Int, component.gifts[Int32(id)] != nil {
availableStickers.append(gift)
}
}
for i in 0 ..< min(2, availableStickers.count) {
let wrappingCount = min(2, availableStickers.count)
for i in 0 ..< wrappingCount {
if let sticker = availableStickers[i].view {
let face: Int
if isUpsideDown {
if isClockwise {
face = i + 1
} else {
face = 3 - i
}
self.animationView.setSticker(sticker, face: face, mirror: isUpsideDown)
self.animationView.setSticker(sticker, face: face, mirror: isUpsideDown, animated: true)
}
}
@ -159,25 +166,28 @@ final class CraftTableComponent: Component {
self.animationView.isSuccess = true
self.animationView.onFinishApproach = { [weak self] isUpsideDown in
self.animationView.onFinishApproach = { [weak self] isUpsideDown, isClockwise in
guard let self else {
return
}
self.isSuccess = true
var availableStickers: [ComponentView<Empty>] = []
for gift in self.selectedGifts.values {
availableStickers.append(gift)
for (id, gift) in self.selectedGifts {
if let id = id.base as? Int, component.gifts[Int32(id)] != nil {
availableStickers.append(gift)
}
}
for i in 0 ..< min(2, availableStickers.count) {
let wrappingCount = min(2, availableStickers.count)
for i in 0 ..< wrappingCount {
if let sticker = availableStickers[i].view {
let face: Int
if isUpsideDown {
if isClockwise {
face = i + 1
} else {
face = 3 - i
}
self.animationView.setSticker(sticker, face: face, mirror: isUpsideDown)
self.animationView.setSticker(sticker, face: face, mirror: isUpsideDown, animated: true)
}
}
@ -258,7 +268,7 @@ final class CraftTableComponent: Component {
if index == 0 {
faceItems.append(
AnyComponentWithIdentity(id: "background", component: AnyComponent(
FilledRoundedRectangleComponent(color: component.buttonColor, cornerRadius: .value(28.0), smoothCorners: true)
CubeFaceComponent(color: component.buttonColor, cornerRadius: 28.0)
))
)
if !component.isCrafting || self.isFailed {
@ -324,12 +334,12 @@ final class CraftTableComponent: Component {
} else {
faceItems.append(
AnyComponentWithIdentity(id: "background", component: AnyComponent(
FilledRoundedRectangleComponent(color: component.buttonColor, cornerRadius: .value(28.0), smoothCorners: true)
CubeFaceComponent(color: component.buttonColor, cornerRadius: 28.0)
))
)
faceItems.append(
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
BundleIconComponent(name: "Components/CubeSide", tintColor: nil, flipVertically: self.flipFaces)
BundleIconComponent(name: "Components/CubeSide", tintColor: nil, flipVertically: index < 4 ? self.flipFaces : false)
))
)
}
@ -408,8 +418,13 @@ final class CraftTableComponent: Component {
for index in component.gifts.keys.sorted() {
indices.append(Int(index))
}
Queue.mainQueue().after(0.55) {
HapticFeedback().impact(.light)
}
self.anvilPlayOnce.invoke(Void())
Queue.mainQueue().after(0.6, {
Queue.mainQueue().after(0.75, {
self.animationView.startStickerSequence(indices: indices)
switch component.result {
@ -772,3 +787,47 @@ private func generateAddIcon(backgroundColor: UIColor) -> UIImage? {
context.strokePath()
})
}
private final class CubeFaceComponent: Component {
private let color: UIColor
private let cornerRadius: CGFloat
public init(color: UIColor, cornerRadius: CGFloat) {
self.color = color
self.cornerRadius = cornerRadius
}
public static func ==(lhs: CubeFaceComponent, rhs: CubeFaceComponent) -> Bool {
if !lhs.color.isEqual(rhs.color) {
return false
}
if lhs.cornerRadius != rhs.cornerRadius {
return false
}
return true
}
public final class View: UIView {
override public init(frame: CGRect) {
super.init(frame: frame)
self.clipsToBounds = true
self.layer.cornerCurve = .continuous
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
transition.setBackgroundColor(view: view, color: self.color)
transition.setCornerRadius(layer: view.layer, cornerRadius: self.cornerRadius)
return availableSize
}
}

View file

@ -82,7 +82,8 @@ final class CubeAnimationView: UIView {
var isSuccess = false
var onFinishApproach: ((Bool) -> Void)?
var onStickerLaunch: (() -> Void)?
var onFinishApproach: ((Bool, Bool) -> Void)?
private let defaultStickOrder: [Int] = [0, 5, 4, 3]
private let sequenceStickOrders: [String: [Int]] = [
@ -160,7 +161,7 @@ final class CubeAnimationView: UIView {
self.layoutStickers()
}
func setSticker(_ sticker: UIView?, face index: Int, mirror: Bool) {
func setSticker(_ sticker: UIView?, face index: Int, mirror: Bool, animated: Bool = false) {
guard self.faces.indices.contains(index) else {
return
}
@ -177,6 +178,13 @@ final class CubeAnimationView: UIView {
if let priorIndex = self.faceOccupants.first(where: { $0.value === sticker })?.key {
self.faceOccupants[priorIndex] = nil
}
if animated, let stickerSuperview = sticker.superview, let snapshotView = sticker.snapshotView(afterScreenUpdates: false) {
stickerSuperview.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
snapshotView.removeFromSuperview()
})
}
sticker.removeFromSuperview()
let targetFace = self.faces[index]
@ -200,6 +208,10 @@ final class CubeAnimationView: UIView {
snappedAngle += .pi
}
sticker.transform = CGAffineTransform(rotationAngle: snappedAngle)
if animated {
sticker.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
func startStickerSequence(indices: [Int]? = nil) {
@ -520,6 +532,8 @@ final class CubeAnimationView: UIView {
return
}
self.onStickerLaunch?()
if let animationView, animationView !== sticker {
animationView.removeFromSuperview()
self.warpSnapshot = nil
@ -697,7 +711,8 @@ final class CubeAnimationView: UIView {
if !self.hasFiredFinishApproach && absRemaining <= self.finishApproachTriggerAngle {
self.hasFiredFinishApproach = true
let upsideDown = abs(shortestAngleDelta(from: self.rotation.x, to: Float.pi)) < (Float.pi / 2)
self.onFinishApproach?(upsideDown)
let isClockwise = self.finishDirectionY > 0
self.onFinishApproach?(upsideDown, isClockwise)
}
if self.isSuccess, absRemaining <= self.finishSuccessScaleTriggerAngle {
let raw = (self.finishSuccessScaleTriggerAngle - absRemaining) / self.finishSuccessScaleTriggerAngle

View file

@ -271,8 +271,8 @@ final class ColorSwatchComponent: Component {
if let image = UIImage(bundleImageName: "Premium/Craft/DialColorMask"), let cgImage = image.cgImage {
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
}
var locations: [CGFloat] = [1.0, 0.0]
let colors: [CGColor] = [component.innerColor.cgColor, component.outerColor.cgColor]
var locations: [CGFloat] = [1.0, 0.95, 0.1, 0.0]
let colors: [CGColor] = [component.innerColor.cgColor, component.innerColor.cgColor, component.outerColor.cgColor, component.outerColor.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!

View file

@ -1215,7 +1215,9 @@ private final class CraftGiftPageContent: Component {
HapticFeedback().success()
} else {
HapticFeedback().error()
Queue.mainQueue().after(0.35) {
HapticFeedback().error()
}
}
}
)

View file

@ -406,10 +406,13 @@ final class SelectGiftPageContent: Component {
contentHeight = self.updateScrolling(interactive: false, transition: transition)
let resaleCount = component.genericGift.availability?.resale ?? 0
let saleTitle = environment.strings.Gift_Craft_Select_SaleGiftsCount(Int32(clamping: resaleCount)).uppercased()
let storeGiftsTitleSize = self.storeGiftsTitle.update(
transition: transition,
component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_SaleGifts.uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
MultilineTextComponent(text: .plain(NSAttributedString(string: saleTitle, font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)

View file

@ -552,7 +552,10 @@ public final class GiftStoreContentComponent: Component {
let attributes = self.starGiftsState?.attributes ?? []
let modelAttributes = attributes.filter { attribute in
if case .model = attribute {
if case let .model(_, _, _, crafted) = attribute {
if component.resaleGiftsContext.forCrafting && crafted {
return false
}
return true
} else {
return false

View file

@ -1536,6 +1536,14 @@ private final class GiftViewSheetContent: CombinedComponent {
controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_Share, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, _ in
c?.dismiss(completion: nil)
self?.shareGift()
})))
var canCraft = false
if let data = self.context.currentAppConfiguration.with({ $0 }).data {
if let _ = data["ios_enable_crafting"] {
@ -1555,14 +1563,6 @@ private final class GiftViewSheetContent: CombinedComponent {
})))
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_Share, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, _ in
c?.dismiss(completion: nil)
self?.shareGift()
})))
if case let .unique(uniqueGift) = arguments.gift, case let .peerId(ownerPeerId) = uniqueGift.owner, ownerPeerId != self.context.account.peerId, uniqueGift.minOfferStars != nil {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_BuyOffer, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Paid"), color: theme.contextMenu.primaryColor)

View file

@ -689,6 +689,10 @@ public final class GlassBackgroundContainerView: UIView {
return nil
}
if result === self.contentView {
return nil
}
return result
}

View file

@ -30,6 +30,7 @@ swift_library(
"//submodules/Components/SheetComponent",
"//submodules/UndoUI",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramUI/Components/PlainButtonComponent",
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
"//submodules/TelegramUI/Components/ListSectionComponent",
"//submodules/TelegramUI/Components/ListActionItemComponent",

View file

@ -21,6 +21,7 @@ import StarsImageComponent
import ConfettiEffect
import PremiumPeerShortcutComponent
import StarsBalanceOverlayComponent
import PlainButtonComponent
import GlassBarButtonComponent
import TelegramStringFormatting
@ -33,7 +34,7 @@ private final class SheetContent: CombinedComponent {
let source: BotPaymentInvoiceSource
let extendedMedia: [TelegramExtendedMedia]
let inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>
let navigateToPeer: (EnginePeer) -> Void
let navigateToPeer: ((EnginePeer) -> Void)?
let dismiss: () -> Void
init(
@ -43,7 +44,7 @@ private final class SheetContent: CombinedComponent {
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia],
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void,
navigateToPeer: ((EnginePeer) -> Void)?,
dismiss: @escaping () -> Void
) {
self.context = context
@ -84,7 +85,7 @@ private final class SheetContent: CombinedComponent {
private var peerDisposable: Disposable?
private(set) var balance: StarsAmount?
private(set) var form: BotPaymentForm?
private(set) var navigateToPeer: (EnginePeer) -> Void
private(set) var navigateToPeer: ((EnginePeer) -> Void)?
private var stateDisposable: Disposable?
@ -105,7 +106,7 @@ private final class SheetContent: CombinedComponent {
extendedMedia: [TelegramExtendedMedia],
invoice: TelegramMediaInvoice,
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void
navigateToPeer: ((EnginePeer) -> Void)?
) {
self.context = context
self.starsContext = starsContext
@ -188,7 +189,7 @@ private final class SheetContent: CombinedComponent {
let _ = (self.context.engine.peers.joinLinkInformation(link)
|> deliverOnMainQueue).startStandalone(next: { result in
if case let .alreadyJoined(peer) = result {
navigateToPeer(peer)
navigateToPeer?(peer)
}
})
}
@ -266,7 +267,7 @@ private final class SheetContent: CombinedComponent {
let star = Child(StarsImageComponent.self)
let closeButton = Child(GlassBarButtonComponent.self)
let title = Child(Text.self)
let peerShortcut = Child(PremiumPeerShortcutComponent.self)
let peerShortcut = Child(PlainButtonComponent.self)
let text = Child(BalancedTextComponent.self)
let button = Child(ButtonComponent.self)
@ -392,10 +393,19 @@ private final class SheetContent: CombinedComponent {
if isBot && !isExtendedMedia, let peer = state.botPeer {
contentSize.height -= 3.0
let peerShortcut = peerShortcut.update(
component: PremiumPeerShortcutComponent(
context: component.context,
theme: theme,
peer: peer
component: PlainButtonComponent(
content: AnyComponent(
PremiumPeerShortcutComponent(
context: component.context,
theme: theme,
peer: peer
)
),
action: {
component.navigateToPeer?(peer)
},
animateAlpha: component.navigateToPeer != nil,
animateScale: false
),
availableSize: CGSize(width: context.availableSize.width - 32.0, height: context.availableSize.height),
transition: .immediate
@ -732,7 +742,7 @@ private final class StarsTransferSheetComponent: CombinedComponent {
private let source: BotPaymentInvoiceSource
private let extendedMedia: [TelegramExtendedMedia]
private let inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>
private let navigateToPeer: (EnginePeer) -> Void
private let navigateToPeer: ((EnginePeer) -> Void)?
init(
context: AccountContext,
@ -741,7 +751,7 @@ private final class StarsTransferSheetComponent: CombinedComponent {
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia],
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void
navigateToPeer: ((EnginePeer) -> Void)?
) {
self.context = context
self.starsContext = starsContext
@ -796,6 +806,7 @@ private final class StarsTransferSheetComponent: CombinedComponent {
backgroundColor: .color(environment.theme.list.modalBlocksBackgroundColor),
followContentSizeChanges: true,
clipsContent: true,
autoAnimateOut: false,
animateOut: animateOut
),
environment: {
@ -847,7 +858,7 @@ public final class StarsTransferScreen: ViewControllerComponentContainer {
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia] = [],
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void = { _ in },
navigateToPeer: ((EnginePeer) -> Void)? = nil,
completion: @escaping (Bool) -> Void
) {
self.context = context

View file

@ -3783,7 +3783,24 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
public func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, extendedMedia: [TelegramExtendedMedia], inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>, completion: @escaping (Bool) -> Void) -> ViewController {
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: source, extendedMedia: extendedMedia, inputData: inputData, completion: completion)
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: source, extendedMedia: extendedMedia, inputData: inputData, navigateToPeer: { [weak self] peer in
guard let self else {
return
}
if let infoController = self.makePeerInfoController(
context: context,
updatedPresentationData: nil,
peer: peer._asPeer(),
mode: .generic,
avatarInitiallyExpanded: peer.smallProfileImage != nil,
fromChat: false,
requestsContext: nil
) {
if let navigationController = self.mainWindow?.viewController as? NavigationController {
navigationController.pushViewController(infoController)
}
}
}, completion: completion)
}
public func makeStarsSubscriptionTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, link: String, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>, navigateToPeer: @escaping (EnginePeer) -> Void) -> ViewController {

View file

@ -626,9 +626,8 @@ private final class TranslateSheetComponent: CombinedComponent {
}
)
),
hasTopEdgeEffect: false,
bottomItem: nil,
backgroundColor: .color(theme.actionSheet.opaqueItemBackgroundColor),
backgroundColor: .color(theme.list.modalBlocksBackgroundColor),
animateOut: animateOut
),
environment: {