This commit is contained in:
Ali 2023-08-03 22:33:05 +03:00
parent 9a50913b57
commit 616a371830
13 changed files with 285 additions and 53 deletions

View File

@ -373,7 +373,13 @@ final class DemoPagerComponent: Component {
if firstTime {
self.scrollView.contentOffset = CGPoint(x: CGFloat(component.index) * availableSize.width, y: 0.0)
component.updated(self.scrollView.contentOffset.x / (self.scrollView.contentSize.width - self.scrollView.frame.width), component.items.count)
var position: CGFloat
if self.scrollView.contentSize.width > self.scrollView.frame.width {
position = self.scrollView.contentOffset.x / (self.scrollView.contentSize.width - self.scrollView.frame.width)
} else {
position = 0.0
}
component.updated(position, component.items.count)
}
let viewportCenter = self.scrollView.contentOffset.x + availableSize.width * 0.5
@ -1230,12 +1236,12 @@ public class PremiumDemoScreen: ViewControllerComponentContainer {
return self._ready
}
public convenience init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source = .other, action: @escaping () -> Void) {
self.init(context: context, subject: subject, source: source, order: nil, action: action)
public convenience init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source = .other, forceDark: Bool = false, action: @escaping () -> Void) {
self.init(context: context, subject: subject, source: source, order: nil, forceDark: forceDark, 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)
init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source = .other, order: [PremiumPerk]?, forceDark: Bool = false, action: @escaping () -> Void) {
super.init(context: context, component: DemoSheetComponent(context: context, subject: subject, source: source, order: order, action: action), navigationBarAppearance: .none, theme: forceDark ? .dark : .default)
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)

View File

@ -283,7 +283,7 @@ public enum PremiumSource: Equatable {
}
}
enum PremiumPerk: CaseIterable {
public enum PremiumPerk: CaseIterable {
case doubleLimits
case moreUpload
case fasterDownload
@ -300,7 +300,7 @@ enum PremiumPerk: CaseIterable {
case translation
case stories
static var allCases: [PremiumPerk] {
public static var allCases: [PremiumPerk] {
return [
.doubleLimits,
.moreUpload,
@ -2771,7 +2771,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
public weak var containerView: UIView?
public var animationColor: UIColor?
public init(context: AccountContext, modal: Bool = true, source: PremiumSource) {
public init(context: AccountContext, modal: Bool = true, source: PremiumSource, forceDark: Bool = false) {
self.context = context
var updateInProgressImpl: ((Bool) -> Void)?
@ -2793,7 +2793,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
completion: {
completionImpl?()
}
), navigationBarAppearance: .transparent)
), navigationBarAppearance: .transparent, theme: forceDark ? .dark : .default)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -48,8 +48,11 @@ public class PremiumLimitsListScreen: ViewController {
let nextAction = ActionSlot<Void>()
init(context: AccountContext, controller: PremiumLimitsListScreen, buttonTitle: String, gloss: Bool) {
init(context: AccountContext, controller: PremiumLimitsListScreen, buttonTitle: String, gloss: Bool, forceDark: Bool) {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
if forceDark {
self.presentationData = self.presentationData.withUpdated(theme: defaultDarkPresentationTheme)
}
self.controller = controller
@ -413,6 +416,7 @@ public class PremiumLimitsListScreen: ViewController {
component: AnyComponent(
StoriesPageComponent(
context: context,
theme: self.presentationData.theme,
bottomInset: self.footerNode.frame.height,
updatedBottomAlpha: { [weak self] alpha in
if let strongSelf = self {
@ -1008,17 +1012,19 @@ public class PremiumLimitsListScreen: ViewController {
private let buttonText: String
private let buttonGloss: Bool
private let forceDark: Bool
var action: () -> Void = {}
var disposed: () -> Void = {}
public var action: () -> Void = {}
public var disposed: () -> Void = {}
init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source, order: [PremiumPerk]?, buttonText: String, isPremium: Bool) {
public init(context: AccountContext, subject: PremiumDemoScreen.Subject, source: PremiumDemoScreen.Source, order: [PremiumPerk]?, buttonText: String, isPremium: Bool, forceDark: Bool = false) {
self.context = context
self.subject = subject
self.source = source
self.order = order
self.buttonText = buttonText
self.buttonGloss = !isPremium
self.forceDark = forceDark
super.init(navigationBarPresentationData: nil)
@ -1041,7 +1047,7 @@ public class PremiumLimitsListScreen: ViewController {
}
override open func loadDisplayNode() {
self.displayNode = Node(context: self.context, controller: self, buttonTitle: self.buttonText, gloss: self.buttonGloss)
self.displayNode = Node(context: self.context, controller: self, buttonTitle: self.buttonText, gloss: self.buttonGloss, forceDark: self.forceDark)
self.displayNodeDidLoad()
self.view.disablesInteractiveModalDismiss = true
@ -1163,9 +1169,15 @@ private class FooterNode: ASDisplayNode {
let bottomPanelPadding: CGFloat = 12.0
let bottomInset: CGFloat = layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : bottomPanelPadding
let panelHeight: CGFloat = bottomPanelPadding + 50.0 + bottomInset + 28.0
var panelHeight: CGFloat = bottomPanelPadding + 50.0 + bottomInset + 8.0
var buttonOffset: CGFloat = 20.0
if let (_, count) = self.currentParams, count > 1 {
panelHeight += 20.0
buttonOffset += 20.0
}
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight))
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: 40.0), size: CGSize(width: buttonWidth, height: buttonHeight)))
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: buttonOffset), size: CGSize(width: buttonWidth, height: buttonHeight)))
transition.updateFrame(node: self.backgroundNode, frame: panelFrame)
self.backgroundNode.update(size: panelFrame.size, transition: transition)
@ -1187,6 +1199,7 @@ private class FooterNode: ASDisplayNode {
containerSize: layout.size
)
self.pageIndicatorView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - indicatorSize.width) / 2.0), y: 10.0), size: indicatorSize)
transition.updateAlpha(layer: self.pageIndicatorView.layer, alpha: count <= 1 ? 0.0 : 1.0)
}
transition.updateFrame(node: self.coverNode, frame: panelFrame)

View File

@ -15,10 +15,12 @@ import AvatarStoryIndicatorComponent
private final class AvatarComponent: Component {
let context: AccountContext
let theme: PresentationTheme
let peer: EnginePeer
init(context: AccountContext, peer: EnginePeer) {
init(context: AccountContext, theme: PresentationTheme, peer: EnginePeer) {
self.context = context
self.theme = theme
self.peer = peer
}
@ -26,6 +28,9 @@ private final class AvatarComponent: Component {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.peer != rhs.peer {
return false
}
@ -60,7 +65,7 @@ private final class AvatarComponent: Component {
self.avatarNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - size.width) / 2.0), y: -22.0), size: size)
self.avatarNode.setPeer(
context: component.context,
theme: component.context.sharedContext.currentPresentationData.with({ $0 }).theme,
theme: component.theme,
peer: component.peer,
synchronousLoad: true
)
@ -234,11 +239,13 @@ private final class StoriesListComponent: CombinedComponent {
typealias EnvironmentType = (Empty, ScrollChildEnvironment)
let context: AccountContext
let theme: PresentationTheme
let topInset: CGFloat
let bottomInset: CGFloat
init(context: AccountContext, topInset: CGFloat, bottomInset: CGFloat) {
init(context: AccountContext, theme: PresentationTheme, topInset: CGFloat, bottomInset: CGFloat) {
self.context = context
self.theme = theme
self.topInset = topInset
self.bottomInset = bottomInset
}
@ -247,6 +254,9 @@ private final class StoriesListComponent: CombinedComponent {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.topInset != rhs.topInset {
return false
}
@ -298,7 +308,7 @@ private final class StoriesListComponent: CombinedComponent {
let list = Child(List<Empty>.self)
return { context in
let theme = context.component.context.sharedContext.currentPresentationData.with { $0 }.theme
let theme = context.component.theme
// let strings = context.component.context.sharedContext.currentPresentationData.with { $0 }.strings
let colors = [
@ -322,6 +332,7 @@ private final class StoriesListComponent: CombinedComponent {
id: "avatar",
component: AnyComponent(AvatarComponent(
context: context.component.context,
theme: theme,
peer: accountPeer
))
)
@ -447,13 +458,15 @@ final class StoriesPageComponent: CombinedComponent {
typealias EnvironmentType = DemoPageEnvironment
let context: AccountContext
let theme: PresentationTheme
let bottomInset: CGFloat
let updatedBottomAlpha: (CGFloat) -> Void
let updatedDismissOffset: (CGFloat) -> Void
let updatedIsDisplaying: (Bool) -> Void
init(context: AccountContext, bottomInset: CGFloat, updatedBottomAlpha: @escaping (CGFloat) -> Void, updatedDismissOffset: @escaping (CGFloat) -> Void, updatedIsDisplaying: @escaping (Bool) -> Void) {
init(context: AccountContext, theme: PresentationTheme, bottomInset: CGFloat, updatedBottomAlpha: @escaping (CGFloat) -> Void, updatedDismissOffset: @escaping (CGFloat) -> Void, updatedIsDisplaying: @escaping (Bool) -> Void) {
self.context = context
self.theme = theme
self.bottomInset = bottomInset
self.updatedBottomAlpha = updatedBottomAlpha
self.updatedDismissOffset = updatedDismissOffset
@ -464,6 +477,9 @@ final class StoriesPageComponent: CombinedComponent {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.bottomInset != rhs.bottomInset {
return false
}
@ -544,7 +560,7 @@ final class StoriesPageComponent: CombinedComponent {
state.position = environment.position
state.isDisplaying = environment.isDisplaying
let theme = context.component.context.sharedContext.currentPresentationData.with { $0 }.theme
let theme = context.component.theme
// let strings = context.component.context.sharedContext.currentPresentationData.with { $0 }.strings
let topInset: CGFloat = 56.0
@ -554,6 +570,7 @@ final class StoriesPageComponent: CombinedComponent {
content: AnyComponent(
StoriesListComponent(
context: context.component.context,
theme: theme,
topInset: topInset,
bottomInset: context.component.bottomInset + 110.0
)

View File

@ -4488,8 +4488,17 @@ func replayFinalState(
}
case let .UpdateStory(peerId, story):
var updatedPeerEntries: [StoryItemsTableEntry] = transaction.getStoryItems(peerId: peerId)
if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peerId, transaction: transaction) {
let previousEntryStory = updatedPeerEntries.first(where: { item in
return item.id == story.id
}).flatMap { item -> Stories.Item? in
if let value = item.value.get(Stories.StoredItem.self), case let .item(item) = value {
return item
} else {
return nil
}
}
if let storedItem = Stories.StoredItem(apiStoryItem: story, existingItem: previousEntryStory, peerId: peerId, transaction: transaction) {
if let currentIndex = updatedPeerEntries.firstIndex(where: { $0.id == storedItem.id }) {
if case .item = storedItem {
if let codedEntry = CodableEntry(storedItem) {

View File

@ -1368,7 +1368,7 @@ extension Stories.Item.Views {
}
extension Stories.StoredItem {
init?(apiStoryItem: Api.StoryItem, peerId: PeerId, transaction: Transaction) {
init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) {
switch apiStoryItem {
case let .storyItem(flags, id, date, expireDate, caption, entities, media, mediaAreas, privacy, views):
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
@ -1414,11 +1414,19 @@ extension Stories.StoredItem {
let isExpired = (flags & (1 << 6)) != 0
let isPublic = (flags & (1 << 7)) != 0
let isCloseFriends = (flags & (1 << 8)) != 0
let isMin = (flags & (1 << 9)) != 0
let isForwardingDisabled = (flags & (1 << 10)) != 0
let isEdited = (flags & (1 << 11)) != 0
let isContacts = (flags & (1 << 12)) != 0
let isSelectedContacts = (flags & (1 << 13)) != 0
var mergedViews: Stories.Item.Views?
if isMin, let existingItem = existingItem {
mergedViews = existingItem.views
} else {
mergedViews = views.flatMap(Stories.Item.Views.init(apiViews:))
}
let item = Stories.Item(
id: id,
timestamp: date,
@ -1427,7 +1435,7 @@ extension Stories.StoredItem {
mediaAreas: mediaAreas?.compactMap(mediaAreaFromApiMediaArea) ?? [],
text: caption ?? "",
entities: entities.flatMap { entities in return messageTextEntitiesFromApiEntities(entities) } ?? [],
views: views.flatMap(Stories.Item.Views.init(apiViews:)),
views: mergedViews,
privacy: parsedPrivacy,
isPinned: isPinned,
isExpired: isExpired,

View File

@ -288,6 +288,7 @@ public final class ButtonComponent: Component {
public let background: Background
public let content: AnyComponentWithIdentity<Empty>
public let isEnabled: Bool
public let allowActionWhenDisabled: Bool
public let displaysProgress: Bool
public let action: () -> Void
@ -295,12 +296,14 @@ public final class ButtonComponent: Component {
background: Background,
content: AnyComponentWithIdentity<Empty>,
isEnabled: Bool,
allowActionWhenDisabled: Bool = false,
displaysProgress: Bool,
action: @escaping () -> Void
) {
self.background = background
self.content = content
self.isEnabled = isEnabled
self.allowActionWhenDisabled = allowActionWhenDisabled
self.displaysProgress = displaysProgress
self.action = action
}
@ -315,6 +318,9 @@ public final class ButtonComponent: Component {
if lhs.isEnabled != rhs.isEnabled {
return false
}
if lhs.allowActionWhenDisabled != rhs.allowActionWhenDisabled {
return false
}
if lhs.displaysProgress != rhs.displaysProgress {
return false
}
@ -375,7 +381,7 @@ public final class ButtonComponent: Component {
self.component = component
self.componentState = state
self.isEnabled = component.isEnabled && !component.displaysProgress
self.isEnabled = (component.isEnabled || component.allowActionWhenDisabled) && !component.displaysProgress
transition.setBackgroundColor(view: self, color: component.background.color)
transition.setCornerRadius(layer: self.layer, cornerRadius: component.background.cornerRadius)

View File

@ -2229,6 +2229,25 @@ public final class StoryItemSetContainerComponent: Component {
}
}
}
var preloadViewListIds: [(Int32, EngineStoryItem.Views)] = []
if let views = component.slice.item.storyItem.views {
preloadViewListIds.append((component.slice.item.storyItem.id, views))
}
if currentIndex != 0, let views = component.slice.allItems[currentIndex - 1].storyItem.views {
preloadViewListIds.append((component.slice.allItems[currentIndex - 1].storyItem.id, views))
}
if currentIndex != component.slice.allItems.count - 1, let views = component.slice.allItems[currentIndex + 1].storyItem.views {
preloadViewListIds.append((component.slice.allItems[currentIndex + 1].storyItem.id, views))
}
for (id, views) in preloadViewListIds {
if component.sharedViewListsContext.viewLists[StoryId(peerId: component.slice.peer.id, id: id)] == nil {
let viewList = component.context.engine.messages.storyViewList(id: id, views: views)
component.sharedViewListsContext.viewLists[StoryId(peerId: component.slice.peer.id, id: id)] = viewList
}
}
if self.viewListPanState != nil || self.isCompletingViewListPan {
for (id, _) in self.viewLists {
if !visibleViewListIds.contains(id) {
@ -2447,18 +2466,40 @@ public final class StoryItemSetContainerComponent: Component {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
let animationBackgroundColor = presentationData.theme.rootController.tabBar.backgroundColor
self.component?.presentController(UndoOverlayController(
presentationData: presentationData,
content: .info(title: nil, text: "**\(peer.compactDisplayTitle)** has been removed from your contacts.", timeout: nil),
content: .universal(
animation: "anim_infotip",
scale: 1.0,
colors: [
"info1.info1.stroke": animationBackgroundColor,
"info2.info2.Fill": animationBackgroundColor
],
title: nil,
text: "**\(peer.compactDisplayTitle)** has been removed from your contacts.",
customUndoText: "Undo",
timeout: nil
),
elevatedLayout: false,
position: .top,
animateInAsReplacement: false,
action: { _ in return false }
action: { [weak self] action in
guard let self, let component = self.component else {
return false
}
guard case let .user(user) = peer else {
return false
}
if case .undo = action {
let _ = component.context.engine.contacts.addContactInteractively(peerId: peer.id, firstName: user.firstName ?? "", lastName: user.lastName ?? "", phoneNumber: user.phone ?? "", addToPrivacyExceptions: false).start()
}
return false
}
), nil)
})))
} else {
if isBlocked {
} else {
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuBlock, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.destructiveColor)
@ -2471,13 +2512,33 @@ public final class StoryItemSetContainerComponent: Component {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
let animationBackgroundColor = presentationData.theme.rootController.tabBar.backgroundColor
self.component?.presentController(UndoOverlayController(
presentationData: presentationData,
content: .info(title: nil, text: "**\(peer.compactDisplayTitle)** has been blocked.", timeout: nil),
content: .universal(
animation: "anim_infotip",
scale: 1.0,
colors: [
"info1.info1.stroke": animationBackgroundColor,
"info2.info2.Fill": animationBackgroundColor
],
title: nil,
text: "**\(peer.compactDisplayTitle)** has been blocked.",
customUndoText: "Undo",
timeout: nil
),
elevatedLayout: false,
position: .top,
animateInAsReplacement: false,
action: { _ in return false }
action: { [weak self] action in
guard let self, let component = self.component else {
return false
}
if case .undo = action {
let _ = component.context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peer.id, isBlocked: false).start()
}
return false
}
), nil)
})))
}
@ -3108,6 +3169,12 @@ public final class StoryItemSetContainerComponent: Component {
switch action {
case .copy:
storeAttributedTextInPasteboard(text)
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let undoController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in true })
self.sendMessageContext.tooltipScreen?.dismiss()
self.sendMessageContext.tooltipScreen = undoController
component.controller()?.present(undoController, in: .current)
case .share:
self.sendMessageContext.performShareTextAction(view: self, text: text.string)
case .lookup:
@ -4015,6 +4082,58 @@ public final class StoryItemSetContainerComponent: Component {
}
}
private func presentStoriesUpgradeScreen() {
guard let component = self.component else {
return
}
let context = component.context
//TODO:localize
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitsListScreen(context: context, subject: .stories, source: .other, order: [.stories], buttonText: "Upgrade Stories", isPremium: false, forceDark: true)
controller.action = { [weak self] in
guard let self else {
return
}
let controller = PremiumIntroScreen(context: context, source: .stories, forceDark: true)
self.sendMessageContext.actionSheet = controller
controller.wasDismissed = { [weak self, weak controller]in
guard let self else {
return
}
if self.sendMessageContext.actionSheet === controller {
self.sendMessageContext.actionSheet = nil
}
self.updateIsProgressPaused()
}
replaceImpl?(controller)
}
controller.disposed = { [weak self, weak controller] in
guard let self else {
return
}
if self.sendMessageContext.actionSheet === controller {
self.sendMessageContext.actionSheet = nil
}
self.updateIsProgressPaused()
}
replaceImpl = { [weak self, weak controller] c in
controller?.dismiss(animated: true, completion: {
guard let self, let component = self.component else {
return
}
component.controller()?.push(c)
})
}
self.sendMessageContext.actionSheet = controller
self.updateIsProgressPaused()
component.controller()?.push(controller)
}
private func requestSave() {
guard let component = self.component, let peerReference = PeerReference(component.slice.peer._asPeer()) else {
return
@ -4360,6 +4479,24 @@ public final class StoryItemSetContainerComponent: Component {
self.requestSave()
})))
if case let .user(accountUser) = component.slice.peer {
//TODO:localize
items.append(.action(ContextMenuActionItem(text: "Stealth Mode", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: accountUser.isPremium ? "Chat/Context Menu/Eye" : "Chat/Context Menu/EyeLocked"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
a(.default)
guard let self else {
return
}
if accountUser.isPremium {
self.sendMessageContext.requestStealthMode(view: self)
} else {
self.presentStoriesUpgradeScreen()
}
})))
}
if component.slice.item.storyItem.isPublic && (component.slice.peer.addressName != nil || !component.slice.peer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) {
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
@ -4560,15 +4697,14 @@ public final class StoryItemSetContainerComponent: Component {
}, action: { [weak self] _, a in
a(.default)
guard let self, let component = self.component else {
guard let self else {
return
}
if accountUser.isPremium {
self.requestSave()
} else {
let premiumScreen = PremiumIntroScreen(context: component.context, source: .stories)
component.controller()?.push(premiumScreen)
self.presentStoriesUpgradeScreen()
}
})))
}
@ -4579,14 +4715,13 @@ public final class StoryItemSetContainerComponent: Component {
}, action: { [weak self] _, a in
a(.default)
guard let self, let component = self.component else {
guard let self else {
return
}
if accountUser.isPremium {
self.sendMessageContext.requestStealthMode(view: self)
} else {
let premiumScreen = PremiumIntroScreen(context: component.context, source: .stories)
component.controller()?.push(premiumScreen)
self.presentStoriesUpgradeScreen()
}
})))

View File

@ -2989,12 +2989,12 @@ final class StoryItemSetContainerSendMessage {
}
let timestamp = Int32(Date().timeIntervalSince1970)
if let activeUntilTimestamp = config.stealthModeState.actualizedNow().activeUntilTimestamp, activeUntilTimestamp > timestamp {
if let activeUntilTimestamp = config.stealthModeState.actualizedNow().activeUntilTimestamp, activeUntilTimestamp > timestamp, !"".isEmpty {
let remainingActiveSeconds = activeUntilTimestamp - timestamp
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme)
//TODO:localize
let text = "The creators of stories you will view in the next \(timeIntervalString(strings: presentationData.strings, value: remainingActiveSeconds)) won't see you in the viewers' lists."
let text = "The creators of stories you will view in the next **\(timeIntervalString(strings: presentationData.strings, value: remainingActiveSeconds))** won't see you in the viewers' lists."
let tooltipScreen = UndoOverlayController(
presentationData: presentationData,
content: .actionSucceeded(title: "You are in Stealth Mode", text: text, cancel: "", destructive: false),
@ -3021,7 +3021,7 @@ final class StoryItemSetContainerSendMessage {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme)
//TODO:localize
let text = "The creators of stories you will view in the next \(timeIntervalString(strings: presentationData.strings, value: remainingActiveSeconds)) won't see you in the viewers' lists."
let text = "The creators of stories you will view in the next **\(timeIntervalString(strings: presentationData.strings, value: remainingActiveSeconds))** won't see you in the viewers' lists."
tooltipScreenValue.content = .actionSucceeded(title: "You are in Stealth Mode", text: text, cancel: "", destructive: false)
})
@ -3062,7 +3062,7 @@ final class StoryItemSetContainerSendMessage {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme)
//TODO:localize
let text = "The creators of stories you viewed in the last \(timeIntervalString(strings: presentationData.strings, value: pastPeriod)) or will view in the next \(timeIntervalString(strings: presentationData.strings, value: futurePeriod)) wont see you in the viewers lists."
let text = "The creators of stories you viewed in the last \(timeIntervalString(strings: presentationData.strings, value: pastPeriod)) or will view in the next **\(timeIntervalString(strings: presentationData.strings, value: futurePeriod))** wont see you in the viewers lists."
let tooltipScreen = UndoOverlayController(
presentationData: presentationData,
content: .actionSucceeded(title: "Stealth Mode On", text: text, cancel: "", destructive: false),

View File

@ -311,7 +311,7 @@ public final class StoryFooterPanelComponent: Component {
if viewCount != 0 {
expandedSegments.append(.number(viewCount, NSAttributedString(string: "\(viewCount)", font: Font.semibold(17.0), textColor: .white)))
}
expandedSegments.append(.text(1, NSAttributedString(string: " Views", font: Font.semibold(17.0), textColor: .white)))
expandedSegments.append(.text(1, NSAttributedString(string: viewPart, font: Font.semibold(17.0), textColor: .white)))
let viewStatsTextLayout = self.viewStatsText.update(size: CGSize(width: availableSize.width, height: size.height), segments: regularSegments, transition: isFirstTime ? .immediate : ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut))
let expandedViewStatsTextLayout = self.viewStatsExpandedText.update(size: CGSize(width: availableSize.width, height: size.height), segments: expandedSegments, transition: isFirstTime ? .immediate : ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut))

View File

@ -54,6 +54,8 @@ private final class StoryStealthModeSheetContentComponent: Component {
private weak var state: EmptyComponentState?
private var timer: Timer?
private var showCooldownToast: Bool = false
private var hideCooldownTimer: Timer?
override init(frame: CGRect) {
super.init(frame: frame)
@ -65,6 +67,27 @@ private final class StoryStealthModeSheetContentComponent: Component {
deinit {
self.timer?.invalidate()
self.hideCooldownTimer?.invalidate()
}
private func displayCooldown() {
if !self.showCooldownToast {
self.showCooldownToast = true
self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut)))
}
self.hideCooldownTimer?.invalidate()
self.hideCooldownTimer = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
self.hideCooldownTimer?.invalidate()
self.hideCooldownTimer = nil
self.showCooldownToast = false
self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut)))
})
}
func update(component: StoryStealthModeSheetContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
@ -96,7 +119,7 @@ private final class StoryStealthModeSheetContentComponent: Component {
let sideInset: CGFloat = 16.0
if remainingCooldownSeconds > 0 {
if remainingCooldownSeconds > 0, self.showCooldownToast {
let toast: ComponentView<Empty>
var toastTransition = transition
if let current = self.toast {
@ -128,6 +151,7 @@ private final class StoryStealthModeSheetContentComponent: Component {
if let toastView = toast.view {
if toastView.superview == nil {
self.addSubview(toastView)
toastView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
if let toastView = toastView as? ToastContentComponent.View, let iconView = toastView.iconView as? LottieComponent.View {
iconView.playOnce()
@ -138,7 +162,11 @@ private final class StoryStealthModeSheetContentComponent: Component {
} else {
if let toast = self.toast {
self.toast = nil
toast.view?.removeFromSuperview()
if let toastView = toast.view {
toastView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak toastView] _ in
toastView?.removeFromSuperview()
})
}
}
}
@ -184,12 +212,24 @@ private final class StoryStealthModeSheetContentComponent: Component {
Text(text: buttonText, font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor)
)),
isEnabled: remainingCooldownSeconds <= 0,
allowActionWhenDisabled: true,
displaysProgress: false,
action: { [weak self] in
guard let self, let component = self.component else {
return
}
component.dismiss()
var remainingCooldownSeconds: Int32 = 0
if let cooldownUntilTimestamp = component.cooldownUntilTimestamp {
remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970)
remainingCooldownSeconds = max(0, remainingCooldownSeconds)
}
if remainingCooldownSeconds > 0 {
self.displayCooldown()
} else {
component.dismiss()
}
}
)),
environment: {},

View File

@ -3355,7 +3355,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if self.navigationTransition != nil {
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
var trueAvatarSize = transitionSourceAvatarFrame.size
if self.avatarListNode.avatarContainerNode.avatarNode.storyStats != nil {
if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 {
trueAvatarSize.width -= 1.33 * 4.0
trueAvatarSize.height -= 1.33 * 4.0
}
@ -3397,7 +3397,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.avatarListNode.listContainerNode.isHidden = false
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
var trueAvatarSize = transitionSourceAvatarFrame.size
if self.avatarListNode.avatarContainerNode.avatarNode.storyStats != nil {
if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 {
trueAvatarSize.width -= 1.33 * 4.0
trueAvatarSize.height -= 1.33 * 4.0
}
@ -3446,7 +3446,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
apparentAvatarFrame = CGRect(origin: CGPoint(x: expandedAvatarCenter.x * (1.0 - transitionFraction) + transitionFraction * avatarCenter.x, y: expandedAvatarCenter.y * (1.0 - transitionFraction) + transitionFraction * avatarCenter.y), size: CGSize())
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
var trueAvatarSize = transitionSourceAvatarFrame.size
if self.avatarListNode.avatarContainerNode.avatarNode.storyStats != nil {
if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 {
trueAvatarSize.width -= 1.33 * 4.0
trueAvatarSize.height -= 1.33 * 4.0
}
@ -3459,7 +3459,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
} else {
var trueAvatarSize = avatarFrame.size
if self.avatarListNode.avatarContainerNode.avatarNode.storyStats != nil {
if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 {
trueAvatarSize.width -= 3.0 * 4.0
trueAvatarSize.height -= 3.0 * 4.0
}
@ -3484,7 +3484,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let neutralAvatarListContainerSize = expandedAvatarListSize
var avatarListContainerSize = CGSize(width: neutralAvatarListContainerSize.width * (1.0 - transitionFraction) + transitionSourceAvatarFrame.width * transitionFraction, height: neutralAvatarListContainerSize.height * (1.0 - transitionFraction) + transitionSourceAvatarFrame.height * transitionFraction)
if self.avatarListNode.avatarContainerNode.avatarNode.storyStats != nil {
if let storyStats = self.avatarListNode.avatarContainerNode.avatarNode.storyStats, storyStats.unseenCount != 0 {
avatarListContainerSize.width -= 1.33 * 5.0
avatarListContainerSize.height -= 1.33 * 5.0
}

View File

@ -116,7 +116,6 @@ public class ShareRootControllerImpl {
private var currentShareController: ShareController?
private var currentPasscodeController: ViewController?
private var shouldBeMaster = Promise<Bool>()
private let disposable = MetaDisposable()
private var observer1: AnyObject?
private var observer2: AnyObject?
@ -130,7 +129,6 @@ public class ShareRootControllerImpl {
deinit {
self.disposable.dispose()
self.shouldBeMaster.set(.single(false))
if let observer = self.observer1 {
NotificationCenter.default.removeObserver(observer)
}