Various fixes

This commit is contained in:
Ilya Laktyushin 2022-05-30 21:08:05 +04:00
parent e59a6a9238
commit 03a13c0305
10 changed files with 357 additions and 162 deletions

View File

@ -746,6 +746,7 @@ public enum PremiumIntroSource {
case folders case folders
case chatsPerFolder case chatsPerFolder
case accounts case accounts
case about
case deeplink(String?) case deeplink(String?)
case profile(PeerId) case profile(PeerId)
} }

View File

@ -460,13 +460,15 @@ private final class DemoSheetContent: CombinedComponent {
let context: AccountContext let context: AccountContext
let subject: PremiumDemoScreen.Subject let subject: PremiumDemoScreen.Subject
let source: PremiumDemoScreen.Source let source: PremiumDemoScreen.Source
let order: [PremiumPerk]
let action: () -> Void let action: () -> Void
let dismiss: () -> Void let dismiss: () -> Void
init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source, action: @escaping () -> Void, dismiss: @escaping () -> Void) { init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source, order: [PremiumPerk]?, action: @escaping () -> Void, dismiss: @escaping () -> Void) {
self.context = context self.context = context
self.subject = subject self.subject = subject
self.source = source self.source = source
self.order = order ?? [.moreUpload, .fasterDownload, .voiceToText, .noAds, .uniqueReactions, .premiumStickers, .advancedChatManagement, .profileBadge, .animatedUserpics]
self.action = action self.action = action
self.dismiss = dismiss self.dismiss = dismiss
} }
@ -481,6 +483,9 @@ private final class DemoSheetContent: CombinedComponent {
if lhs.source != rhs.source { if lhs.source != rhs.source {
return false return false
} }
if lhs.order != rhs.order {
return false
}
return true return true
} }
@ -598,164 +603,166 @@ private final class DemoSheetContent: CombinedComponent {
if let reactions = state.reactions, let stickers = state.stickers { if let reactions = state.reactions, let stickers = state.stickers {
let textColor = theme.actionSheet.primaryTextColor let textColor = theme.actionSheet.primaryTextColor
var items: [DemoPagerComponent.Item] = [ var availableItems: [PremiumPerk: DemoPagerComponent.Item] = [:]
DemoPagerComponent.Item(
AnyComponentWithIdentity( availableItems[.moreUpload] = DemoPagerComponent.Item(
id: PremiumDemoScreen.Subject.moreUpload, AnyComponentWithIdentity(
component: AnyComponent( id: PremiumDemoScreen.Subject.moreUpload,
PageComponent( component: AnyComponent(
content: AnyComponent(PhoneDemoComponent( PageComponent(
context: component.context, content: AnyComponent(PhoneDemoComponent(
position: .bottom, context: component.context,
videoName: "4gb" position: .bottom,
)), videoName: "4gb"
title: strings.Premium_UploadSize, )),
text: strings.Premium_UploadSizeInfo, title: strings.Premium_UploadSize,
textColor: textColor text: strings.Premium_UploadSizeInfo,
) textColor: textColor
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.fasterDownload,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "fastdownload"
)),
title: strings.Premium_FasterSpeed,
text: strings.Premium_FasterSpeedInfo,
textColor: textColor
)
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.voiceToText,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "voice"
)),
title: strings.Premium_VoiceToText,
text: strings.Premium_VoiceToTextInfo,
textColor: textColor
)
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.noAds,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .bottom,
videoName: "noads"
)),
title: strings.Premium_NoAds,
text: strings.Premium_NoAdsInfo,
textColor: textColor
)
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.uniqueReactions,
component: AnyComponent(
PageComponent(
content: AnyComponent(
ReactionsCarouselComponent(
context: component.context,
theme: environment.theme,
reactions: reactions
)
),
title: isStandalone ? strings.Premium_ReactionsStandalone : strings.Premium_Reactions,
text: isStandalone ? strings.Premium_ReactionsStandaloneInfo : strings.Premium_ReactionsInfo,
textColor: textColor
)
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.premiumStickers,
component: AnyComponent(
PageComponent(
content: AnyComponent(
StickersCarouselComponent(
context: component.context,
stickers: stickers
)
),
title: strings.Premium_Stickers,
text: strings.Premium_StickersInfo,
textColor: textColor
)
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.advancedChatManagement,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "fastdownload"
)),
title: strings.Premium_ChatManagement,
text: strings.Premium_ChatManagementInfo,
textColor: textColor
)
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.profileBadge,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "badge"
)),
title: strings.Premium_Badge,
text: strings.Premium_BadgeInfo,
textColor: textColor
)
)
)
),
DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.animatedUserpics,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "badge"
)),
title: strings.Premium_Avatar,
text: strings.Premium_AvatarInfo,
textColor: textColor
)
) )
) )
) )
] )
availableItems[.fasterDownload] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.fasterDownload,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "fastdownload"
)),
title: strings.Premium_FasterSpeed,
text: strings.Premium_FasterSpeedInfo,
textColor: textColor
)
)
)
)
availableItems[.voiceToText] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.voiceToText,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "voice"
)),
title: strings.Premium_VoiceToText,
text: strings.Premium_VoiceToTextInfo,
textColor: textColor
)
)
)
)
availableItems[.noAds] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.noAds,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .bottom,
videoName: "noads"
)),
title: strings.Premium_NoAds,
text: strings.Premium_NoAdsInfo,
textColor: textColor
)
)
)
)
availableItems[.uniqueReactions] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.uniqueReactions,
component: AnyComponent(
PageComponent(
content: AnyComponent(
ReactionsCarouselComponent(
context: component.context,
theme: environment.theme,
reactions: reactions
)
),
title: isStandalone ? strings.Premium_ReactionsStandalone : strings.Premium_Reactions,
text: isStandalone ? strings.Premium_ReactionsStandaloneInfo : strings.Premium_ReactionsInfo,
textColor: textColor
)
)
)
)
availableItems[.premiumStickers] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.premiumStickers,
component: AnyComponent(
PageComponent(
content: AnyComponent(
StickersCarouselComponent(
context: component.context,
stickers: stickers
)
),
title: strings.Premium_Stickers,
text: strings.Premium_StickersInfo,
textColor: textColor
)
)
)
)
availableItems[.advancedChatManagement] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.advancedChatManagement,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "fastdownload"
)),
title: strings.Premium_ChatManagement,
text: strings.Premium_ChatManagementInfo,
textColor: textColor
)
)
)
)
availableItems[.profileBadge] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.profileBadge,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "badge"
)),
title: strings.Premium_Badge,
text: strings.Premium_BadgeInfo,
textColor: textColor
)
)
)
)
availableItems[.animatedUserpics] = DemoPagerComponent.Item(
AnyComponentWithIdentity(
id: PremiumDemoScreen.Subject.animatedUserpics,
component: AnyComponent(
PageComponent(
content: AnyComponent(PhoneDemoComponent(
context: component.context,
position: .top,
videoName: "badge"
)),
title: strings.Premium_Avatar,
text: strings.Premium_AvatarInfo,
textColor: textColor
)
)
)
)
var items: [DemoPagerComponent.Item] = component.order.compactMap { availableItems[$0] }
let index: Int let index: Int
switch component.source { switch component.source {
case .intro: case .intro:
@ -881,12 +888,14 @@ private final class DemoSheetComponent: CombinedComponent {
let context: AccountContext let context: AccountContext
let subject: PremiumDemoScreen.Subject let subject: PremiumDemoScreen.Subject
let source: PremiumDemoScreen.Source let source: PremiumDemoScreen.Source
let order: [PremiumPerk]?
let action: () -> Void let action: () -> Void
init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source, action: @escaping () -> Void) { init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source, order: [PremiumPerk]?, action: @escaping () -> Void) {
self.context = context self.context = context
self.subject = subject self.subject = subject
self.source = source self.source = source
self.order = order
self.action = action self.action = action
} }
@ -900,6 +909,9 @@ private final class DemoSheetComponent: CombinedComponent {
if lhs.source != rhs.source { if lhs.source != rhs.source {
return false return false
} }
if lhs.order != rhs.order {
return false
}
return true return true
} }
@ -919,6 +931,7 @@ private final class DemoSheetComponent: CombinedComponent {
context: context.component.context, context: context.component.context,
subject: context.component.subject, subject: context.component.subject,
source: context.component.source, source: context.component.source,
order: context.component.order,
action: context.component.action, action: context.component.action,
dismiss: { dismiss: {
animateOut.invoke(Action { _ in animateOut.invoke(Action { _ in
@ -989,8 +1002,12 @@ public class PremiumDemoScreen: ViewControllerComponentContainer {
return self._ready return self._ready
} }
public init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source = .other, action: @escaping () -> Void) { public convenience init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source = .other, action: @escaping () -> Void) {
super.init(context: context, component: DemoSheetComponent(context: context, subject: subject, source: source, action: action), navigationBarAppearance: .none) self.init(context: context, subject: subject, source: source, order: nil, action: action)
}
init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source = .other, order: [PremiumPerk]?, action: @escaping () -> Void) {
super.init(context: context, component: DemoSheetComponent(context: context, subject: subject, source: source, order: order, action: action), navigationBarAppearance: .none)
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)

View File

@ -34,6 +34,7 @@ public enum PremiumSource: Equatable {
case folders case folders
case chatsPerFolder case chatsPerFolder
case accounts case accounts
case about
case deeplink(String?) case deeplink(String?)
case profile(PeerId) case profile(PeerId)
@ -65,6 +66,8 @@ public enum PremiumSource: Equatable {
return "double_limits__dialog_filters_chats" return "double_limits__dialog_filters_chats"
case .accounts: case .accounts:
return "double_limits__accounts" return "double_limits__accounts"
case .about:
return "double_limits__about"
case let .profile(id): case let .profile(id):
return "profile__\(id.id._internalGetInt64Value())" return "profile__\(id.id._internalGetInt64Value())"
case let .deeplink(reference): case let .deeplink(reference):
@ -77,7 +80,7 @@ public enum PremiumSource: Equatable {
} }
} }
private enum PremiumPerk: CaseIterable { enum PremiumPerk: CaseIterable {
case doubleLimits case doubleLimits
case moreUpload case moreUpload
case fasterDownload case fasterDownload
@ -990,6 +993,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
context: accountContext, context: accountContext,
subject: demoSubject, subject: demoSubject,
source: .intro(state?.price), source: .intro(state?.price),
order: state?.configuration.perks,
action: { action: {
dismissImpl?() dismissImpl?()
if !isPremium { if !isPremium {

View File

@ -1150,6 +1150,7 @@ public class Account {
self.managedOperationsDisposable.add(managedConfigurationUpdates(accountManager: accountManager, postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedConfigurationUpdates(accountManager: accountManager, postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedVoipConfigurationUpdates(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedVoipConfigurationUpdates(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedAppConfigurationUpdates(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedAppConfigurationUpdates(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedPremiumPromoConfigurationUpdates(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedAutodownloadSettingsUpdates(accountManager: accountManager, network: self.network).start()) self.managedOperationsDisposable.add(managedAutodownloadSettingsUpdates(accountManager: accountManager, network: self.network).start())
self.managedOperationsDisposable.add(managedTermsOfServiceUpdates(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedTermsOfServiceUpdates(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedAppUpdateInfo(network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedAppUpdateInfo(network: self.network, stateManager: self.stateManager).start())

View File

@ -0,0 +1,67 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
return network.request(Api.functions.help.getPremiumPromo())
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.help.PremiumPromo?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Void, NoError> in
guard let result = result else {
return .complete()
}
return postbox.transaction { transaction -> Void in
updatePremiumPromoConfiguration(transaction: transaction, { configuration -> PremiumPromoConfiguration in
return PremiumPromoConfiguration(apiPremiumPromo: result)
})
}
}
}
func managedPremiumPromoConfigurationUpdates(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = Signal<Void, NoError> { subscriber in
return updatePremiumPromoConfigurationOnce(postbox: postbox, network: network).start(completed: {
subscriber.putCompletion()
})
}
return (poll |> then(.complete() |> suspendAwareDelay(10.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
}
private func currentPremiumPromoConfiguration(transaction: Transaction) -> PremiumPromoConfiguration {
if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.premiumPromo)?.get(PremiumPromoConfiguration.self) {
return entry
} else {
return PremiumPromoConfiguration.defaultValue
}
}
private func updatePremiumPromoConfiguration(transaction: Transaction, _ f: (PremiumPromoConfiguration) -> PremiumPromoConfiguration) {
let current = currentPremiumPromoConfiguration(transaction: transaction)
let updated = f(current)
if updated != current {
transaction.setPreferencesEntry(key: PreferencesKeys.premiumPromo, value: PreferencesEntry(updated))
}
}
private extension PremiumPromoConfiguration {
init(apiPremiumPromo: Api.help.PremiumPromo) {
switch apiPremiumPromo {
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles):
self.status = statusText
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
var videos: [String: TelegramMediaFile] = [:]
for (key, document) in zip(videoSections, videoFiles) {
if let file = telegramMediaFileFromApiDocument(document) {
videos[key] = file
}
}
self.videos = videos
}
}
}

View File

@ -232,6 +232,7 @@ private enum PreferencesKeyValues: Int32 {
case chatListFiltersFeaturedState = 22 case chatListFiltersFeaturedState = 22
case secretChatSettings = 23 case secretChatSettings = 23
case reactionSettings = 24 case reactionSettings = 24
case premiumPromo = 25
} }
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
@ -360,6 +361,12 @@ public struct PreferencesKeys {
key.setInt32(0, value: PreferencesKeyValues.reactionSettings.rawValue) key.setInt32(0, value: PreferencesKeyValues.reactionSettings.rawValue)
return key return key
}() }()
public static let premiumPromo: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: PreferencesKeyValues.premiumPromo.rawValue)
return key
}()
} }
private enum SharedDataKeyValues: Int32 { private enum SharedDataKeyValues: Int32 {

View File

@ -0,0 +1,75 @@
import Foundation
import Postbox
public struct PremiumPromoConfiguration: Codable, Equatable {
enum CodingKeys: String, CodingKey {
case status
case statusEntities
case videos
}
private struct DictionaryPair: Codable {
var key: String
var value: TelegramMediaFile
init(_ key: String, value: TelegramMediaFile) {
self.key = key
self.value = value
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.key = try container.decode(String.self, forKey: "k")
self.value = try container.decode(TelegramMediaFile.self, forKey: "v")
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.key, forKey: "k")
try container.encode(self.value, forKey: "v")
}
}
public var status: String
public var statusEntities: [MessageTextEntity]
public var videos: [String: TelegramMediaFile]
public static var defaultValue: PremiumPromoConfiguration {
return PremiumPromoConfiguration(status: "", statusEntities: [], videos: [:])
}
init(status: String, statusEntities: [MessageTextEntity], videos: [String: TelegramMediaFile]) {
self.status = status
self.statusEntities = statusEntities
self.videos = videos
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.status = try container.decode(String.self, forKey: .status)
self.statusEntities = try container.decode([MessageTextEntity].self, forKey: .statusEntities)
var videos: [String: TelegramMediaFile] = [:]
let pairs = try container.decode([DictionaryPair].self, forKey: .videos)
for pair in pairs {
videos[pair.key] = pair.value
}
self.videos = videos
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.status, forKey: .status)
try container.encode(self.statusEntities, forKey: .statusEntities)
var pairs: [DictionaryPair] = []
for (key, file) in self.videos {
pairs.append(DictionaryPair(key, value: file))
}
try container.encode(pairs, forKey: .videos)
}
}

View File

@ -354,5 +354,26 @@ public extension TelegramEngine.EngineData.Item {
return localizationListState return localizationListState
} }
} }
public struct PremiumPromo: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = PremiumPromoConfiguration
public init() {
}
var key: PostboxViewKey {
return .preferences(keys: Set([PreferencesKeys.premiumPromo]))
}
func extract(view: PostboxView) -> Result {
guard let view = view as? PreferencesView else {
preconditionFailure()
}
guard let premiumPromoConfiguration = view.values[PreferencesKeys.premiumPromo]?.get(PremiumPromoConfiguration.self) else {
return PremiumPromoConfiguration.defaultValue
}
return premiumPromoConfiguration
}
}
} }
} }

View File

@ -1491,6 +1491,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
mappedSource = .chatsPerFolder mappedSource = .chatsPerFolder
case .accounts: case .accounts:
mappedSource = .accounts mappedSource = .accounts
case .about:
mappedSource = .about
case let .deeplink(reference): case let .deeplink(reference):
mappedSource = .deeplink(reference) mappedSource = .deeplink(reference)
case let .profile(peerId): case let .profile(peerId):

View File

@ -526,8 +526,8 @@ class StickerPaneTrendingListGridItemNode: GridItemNode {
self.dismissButtonNode.setImage(PresentationResourcesChat.chatInputMediaPanelGridDismissImage(item.theme), for: []) self.dismissButtonNode.setImage(PresentationResourcesChat.chatInputMediaPanelGridDismissImage(item.theme), for: [])
} }
let leftInset: CGFloat = 12.0 let leftInset: CGFloat = 9.0
let rightInset: CGFloat = 16.0 let rightInset: CGFloat = 18.0 + UIScreenPixel
let topOffset: CGFloat = 9.0 let topOffset: CGFloat = 9.0
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.StickerPacksSettings_FeaturedPacks.uppercased(), font: titleFont, textColor: item.theme.chat.inputMediaPanel.stickersSectionTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.StickerPacksSettings_FeaturedPacks.uppercased(), font: titleFont, textColor: item.theme.chat.inputMediaPanel.stickersSectionTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))