mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-01 04:08:07 +00:00
Merge commit '1bbfc8da52b41aeedf68ed0e5b6635889685a2ca'
This commit is contained in:
commit
40246a3b11
@ -10988,6 +10988,8 @@ Sorry for the inconvenience.";
|
||||
"ChannelBoost.MaxLevelReached.Text" = "**%1$@** reached **Level %2$@**.";
|
||||
|
||||
"ChannelBoost.MoreBoostsNeeded.Boosted.Text" = "%@ needed to unlock new features.";
|
||||
"ChannelBoost.MoreBoostsNeeded.Boosted.Level.Text" = "This channel reached **%@** and unlocked new features.";
|
||||
"GroupBoost.MoreBoostsNeeded.Boosted.Level.Text" = "This group reached **%@** and unlocked new features.";
|
||||
|
||||
"ContactList.Context.Delete" = "Delete Contact";
|
||||
"ContactList.Context.Select" = "Select";
|
||||
@ -11013,7 +11015,7 @@ Sorry for the inconvenience.";
|
||||
"GroupBoost.EnableStoriesText" = "Your group needs %1$@ to enable posting stories.";
|
||||
"GroupBoost.IncreaseLimitText" = "Your group needs %1$@ to post %2$@.";
|
||||
|
||||
"BoostGift.Group.Description" = "Get more boosts for your group by gifting\nPremium to your subscribers.";
|
||||
"BoostGift.Group.Description" = "Get more boosts for your group by gifting\nPremium to your members.";
|
||||
|
||||
"BoostGift.Group.AllMembers" = "All members";
|
||||
"BoostGift.Group.OnlyNewMembers" = "Only new members";
|
||||
@ -11276,3 +11278,31 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Chat.Giveaway.Info.OtherChannelsAndGroups_1" = "**%@** other listed channel and group";
|
||||
"Chat.Giveaway.Info.OtherChannelsAndGroups_any" = "**%@** other listed channels and groups";
|
||||
|
||||
"GroupBoost.MemberBoosted.Times_1" = "**%@** time";
|
||||
"GroupBoost.MemberBoosted.Times_any" = "**%@** times";
|
||||
|
||||
"GroupBoost.MemberBoosted" = "**%1$@** boosted the group %2$@.";
|
||||
"GroupBoost.MemberBoosted.BoostForBadge" = "Boost **%1$@** to help it unlock new features and get a booster **badge** for your messages.";
|
||||
|
||||
"GroupBoost.BoostToUnrestrict.Times_1" = "**%@** time";
|
||||
"GroupBoost.BoostToUnrestrict.Times_any" = "**%@** times";
|
||||
|
||||
"GroupBoost.BoostToUnrestrict" = "Boost the group %1$@ to remove messaging restrictions. Your boosts will help **%2$@** to unlock new features.";
|
||||
|
||||
"Story.InputPlaceholderReplyInGroup" = "Comment Story...";
|
||||
|
||||
"Story.SendReactionAsGroupMessage" = "Send reaction as a message";
|
||||
|
||||
"StoryList.TooltipStoriesSavedToGroup_1" = "Story saved to groups's profile";
|
||||
"StoryList.TooltipStoriesSavedToGroup_any" = "%d stories saved to groups's profile.";
|
||||
"Story.ToastRemovedFromGroupText" = "Story removed from the group page";
|
||||
"Story.ToastSavedToGroupTitle" = "Story saved to the group page";
|
||||
"Story.ToastSavedToGroupText" = "Posted stories can be viewed by others on the group page until an admin removes them.";
|
||||
|
||||
"Channel.AdminLog.MessageChangedGroupEmojiPack" = "%@ changed group emoji pack";
|
||||
"Channel.AdminLog.MessageRemovedGroupEmojiPack" = "%@ removed group emoji pack";
|
||||
|
||||
"Group.Appearance.EmojiPackUpdated" = "Group emoji pack updated.";
|
||||
|
||||
"Attachment.BoostToUnlock" = "Boost to Unlock";
|
||||
|
||||
@ -821,11 +821,13 @@ public struct StoryCameraTransitionInCoordinator {
|
||||
|
||||
public class MediaEditorTransitionOutExternalState {
|
||||
public var storyTarget: Stories.PendingTarget?
|
||||
public var isForcedTarget: Bool
|
||||
public var isPeerArchived: Bool
|
||||
public var transitionOut: ((Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?)?
|
||||
|
||||
public init(storyTarget: Stories.PendingTarget?, isPeerArchived: Bool, transitionOut: ((Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?)?) {
|
||||
public init(storyTarget: Stories.PendingTarget?, isForcedTarget: Bool, isPeerArchived: Bool, transitionOut: ((Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?)?) {
|
||||
self.storyTarget = storyTarget
|
||||
self.isForcedTarget = isForcedTarget
|
||||
self.isPeerArchived = isPeerArchived
|
||||
self.transitionOut = transitionOut
|
||||
}
|
||||
@ -959,7 +961,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController
|
||||
func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController
|
||||
func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, forceDark: Bool, cancel: @escaping () -> Void, action: @escaping () -> Bool) -> ViewController
|
||||
func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource) -> ViewController
|
||||
func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, completion: (() -> Void)?) -> ViewController
|
||||
func makePremiumPrivacyControllerController(context: AccountContext, subject: PremiumPrivacySubject, peerId: EnginePeer.Id) -> ViewController
|
||||
func makePremiumBoostLevelsController(context: AccountContext, peerId: EnginePeer.Id, boostStatus: ChannelBoostStatus, myBoostStatus: MyBoostStatus, forceDark: Bool, openStats: (() -> Void)?) -> ViewController
|
||||
|
||||
|
||||
@ -726,9 +726,9 @@ public final class Camera {
|
||||
self.metrics = Camera.Metrics(model: DeviceModel.current)
|
||||
|
||||
let session = CameraSession()
|
||||
session.session.usesApplicationAudioSession = true
|
||||
session.session.automaticallyConfiguresApplicationAudioSession = false
|
||||
session.session.automaticallyConfiguresCaptureDeviceForWideColor = false
|
||||
session.session.usesApplicationAudioSession = true
|
||||
if let previewView {
|
||||
previewView.setSession(session.session, autoConnect: !session.hasMultiCam)
|
||||
}
|
||||
|
||||
@ -309,7 +309,11 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
||||
if let forwardInfo = message.forwardInfo, let author = forwardInfo.author {
|
||||
messageText = strings.Message_GiveawayStartedOther(EnginePeer(author).compactDisplayTitle).string
|
||||
} else {
|
||||
messageText = isPeerGroup ? strings.Message_GiveawayStartedGroup : strings.Message_GiveawayStarted
|
||||
if let author = message.author, case let .channel(channel) = author, case .group = channel.info {
|
||||
messageText = strings.Message_GiveawayStartedGroup
|
||||
} else {
|
||||
messageText = strings.Message_GiveawayStarted
|
||||
}
|
||||
}
|
||||
case let results as TelegramMediaGiveawayResults:
|
||||
if results.winnersCount == 0 {
|
||||
|
||||
@ -1683,7 +1683,7 @@ public final class ChatListNode: ListView {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList)
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList, completion: nil)
|
||||
self.push?(controller)
|
||||
}, openActiveSessions: { [weak self] in
|
||||
guard let self else {
|
||||
|
||||
@ -26,6 +26,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
||||
private var cameraTextNode: ImmediateTextNode
|
||||
private var cameraIconNode: ASImageNode
|
||||
|
||||
var boostPressed: () -> Void = {}
|
||||
var settingsPressed: () -> Void = {}
|
||||
var cameraPressed: () -> Void = {}
|
||||
|
||||
@ -76,7 +77,13 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
||||
self.addSubnode(self.animationNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
if case .intro = self.content {
|
||||
switch self.content {
|
||||
case .bannedSendMedia(_, true):
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.buttonNode.pressed = { [weak self] in
|
||||
self?.boostPressed()
|
||||
}
|
||||
case .intro:
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
@ -104,6 +111,8 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
||||
self.buttonNode.pressed = { [weak self] in
|
||||
self?.settingsPressed()
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +137,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
||||
|
||||
let imageSpacing: CGFloat = 12.0
|
||||
let textSpacing: CGFloat = 12.0
|
||||
let buttonSpacing: CGFloat = 15.0
|
||||
let buttonSpacing: CGFloat = 20.0
|
||||
let cameraSpacing: CGFloat = 13.0
|
||||
|
||||
let imageHeight = layout.size.width < layout.size.height ? imageSize.height + imageSpacing : 0.0
|
||||
@ -137,7 +146,13 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
||||
self.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: theme))
|
||||
self.cameraIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/OpenCamera"), color: theme.list.itemAccentColor)
|
||||
}
|
||||
self.buttonNode.title = strings.Attachment_OpenSettings
|
||||
switch self.content {
|
||||
case .intro:
|
||||
self.buttonNode.title = strings.Attachment_OpenSettings
|
||||
case .bannedSendMedia:
|
||||
self.buttonNode.title = strings.Attachment_BoostToUnlock
|
||||
}
|
||||
|
||||
let buttonWidth: CGFloat = 248.0
|
||||
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
||||
|
||||
|
||||
@ -174,6 +174,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
private let chatLocation: ChatLocation?
|
||||
private let bannedSendPhotos: (Int32, Bool)?
|
||||
private let bannedSendVideos: (Int32, Bool)?
|
||||
private let canBoostToUnrestrict: Bool
|
||||
private let subject: Subject
|
||||
private let saveEditedPhotos: Bool
|
||||
|
||||
@ -187,6 +188,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in }
|
||||
public var presentWebSearch: (MediaGroupsScreen, Bool) -> Void = { _, _ in }
|
||||
public var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil }
|
||||
public var openBoost: () -> Void = { }
|
||||
|
||||
public var customSelection: ((MediaPickerScreen, Any) -> Void)? = nil
|
||||
|
||||
@ -1324,7 +1326,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
if let current = self.placeholderNode {
|
||||
placeholderNode = current
|
||||
} else {
|
||||
placeholderNode = MediaPickerPlaceholderNode(content: .bannedSendMedia(text: banDescription, canBoost: false))
|
||||
placeholderNode = MediaPickerPlaceholderNode(content: .bannedSendMedia(text: banDescription, canBoost: controller.canBoostToUnrestrict))
|
||||
placeholderNode.boostPressed = { [weak controller] in
|
||||
controller?.openBoost()
|
||||
}
|
||||
self.containerNode.insertSubnode(placeholderNode, aboveSubnode: self.gridNode)
|
||||
self.placeholderNode = placeholderNode
|
||||
|
||||
@ -1525,8 +1530,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
threadTitle: String?,
|
||||
chatLocation: ChatLocation?,
|
||||
isScheduledMessages: Bool = false,
|
||||
bannedSendPhotos: (Int32, Bool)?,
|
||||
bannedSendVideos: (Int32, Bool)?,
|
||||
bannedSendPhotos: (Int32, Bool)? = nil,
|
||||
bannedSendVideos: (Int32, Bool)? = nil,
|
||||
canBoostToUnrestrict: Bool = false,
|
||||
subject: Subject,
|
||||
editingContext: TGMediaEditingContext? = nil,
|
||||
selectionContext: TGMediaSelectionContext? = nil,
|
||||
@ -1545,6 +1551,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.isScheduledMessages = isScheduledMessages
|
||||
self.bannedSendPhotos = bannedSendPhotos
|
||||
self.bannedSendVideos = bannedSendVideos
|
||||
self.canBoostToUnrestrict = canBoostToUnrestrict
|
||||
self.subject = subject
|
||||
self.saveEditedPhotos = saveEditedPhotos
|
||||
self.mainButtonState = mainButtonState
|
||||
|
||||
@ -1210,7 +1210,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
|
||||
let title = presentationData.strings.BoostGift_GiveawayCreated_Title
|
||||
let text = presentationData.strings.BoostGift_GiveawayCreated_Text
|
||||
let text = isGroup ? presentationData.strings.BoostGift_Group_GiveawayCreated_Text : presentationData.strings.BoostGift_GiveawayCreated_Text
|
||||
|
||||
let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in
|
||||
let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil)
|
||||
|
||||
@ -647,33 +647,37 @@ private final class SheetContent: CombinedComponent {
|
||||
}
|
||||
} else {
|
||||
textString = strings.ChannelBoost_MaxLevelReached_Text(peerName, "\(level)").string
|
||||
// let storiesString = strings.ChannelBoost_StoriesPerDay(Int32(level))
|
||||
// textString = strings.ChannelBoost_MaxLevelReachedTextAuthor("\(level)", storiesString).string
|
||||
}
|
||||
case let .user(mode):
|
||||
switch mode {
|
||||
case let .groupPeer(_, peerBoostCount):
|
||||
let memberName = state.memberPeer?.compactDisplayTitle ?? ""
|
||||
//TODO:localize
|
||||
let timesString = strings.GroupBoost_MemberBoosted_Times(Int32(peerBoostCount))
|
||||
let memberString = strings.GroupBoost_MemberBoosted(memberName, timesString).string
|
||||
if myBoostCount > 0 {
|
||||
if let remaining {
|
||||
if let remaining, remaining != 0 {
|
||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
||||
textString = "**\(memberName)** boosted the group **\(peerBoostCount)** times. \(strings.ChannelBoost_MoreBoostsNeeded_Boosted_Text(boostsString).string)"
|
||||
textString = "\(memberString) \(strings.ChannelBoost_MoreBoostsNeeded_Boosted_Text(boostsString).string)"
|
||||
} else {
|
||||
textString = "**\(memberName)** boosted the group **\(peerBoostCount)** times."
|
||||
textString = memberString
|
||||
}
|
||||
} else {
|
||||
textString = "**\(memberName)** boosted the group **\(peerBoostCount)** times. Boost **\(peerName)** to help it unlock new features and get a booster **badge** for your messages."
|
||||
textString = "\(memberString) \(strings.GroupBoost_MemberBoosted_BoostForBadge(peerName).string)"
|
||||
}
|
||||
isCurrent = true
|
||||
case let .unrestrict(unrestrictCount):
|
||||
textString = "Boost the group **\(unrestrictCount)** times to remove messaging restrictions. Your boosts will help **\(peerName)** to unlock new features."
|
||||
let timesString = strings.GroupBoost_BoostToUnrestrict_Times(Int32(unrestrictCount))
|
||||
textString = strings.GroupBoost_BoostToUnrestrict(timesString, peerName).string
|
||||
isCurrent = true
|
||||
default:
|
||||
if let remaining {
|
||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
||||
if myBoostCount > 0 {
|
||||
textString = strings.ChannelBoost_MoreBoostsNeeded_Boosted_Text(boostsString).string
|
||||
if remaining == 0 {
|
||||
textString = isGroup ? strings.GroupBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level)").string : strings.ChannelBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level)").string
|
||||
} else {
|
||||
textString = strings.ChannelBoost_MoreBoostsNeeded_Boosted_Text(boostsString).string
|
||||
}
|
||||
} else {
|
||||
textString = strings.ChannelBoost_MoreBoostsNeeded_Text(peerName, boostsString).string
|
||||
}
|
||||
@ -1612,25 +1616,26 @@ public class PremiumBoostLevelsScreen: ViewController {
|
||||
self.wrappingView.addSubview(self.containerView)
|
||||
self.containerView.addSubview(self.contentView)
|
||||
|
||||
if case .user = controller.mode, let status = controller.status {
|
||||
if case .user = controller.mode {
|
||||
self.containerView.addSubview(self.footerContainerView)
|
||||
self.footerContainerView.addSubview(self.footerView)
|
||||
}
|
||||
|
||||
if let status = controller.status, let myBoostStatus = controller.myBoostStatus {
|
||||
var myBoostCount: Int32 = 0
|
||||
var currentMyBoostCount: Int32 = 0
|
||||
var availableBoosts: [MyBoostStatus.Boost] = []
|
||||
var occupiedBoosts: [MyBoostStatus.Boost] = []
|
||||
if let myBoostStatus = controller.myBoostStatus {
|
||||
for boost in myBoostStatus.boosts {
|
||||
if let boostPeer = boost.peer {
|
||||
if boostPeer.id == controller.peerId {
|
||||
myBoostCount += 1
|
||||
} else {
|
||||
occupiedBoosts.append(boost)
|
||||
}
|
||||
|
||||
for boost in myBoostStatus.boosts {
|
||||
if let boostPeer = boost.peer {
|
||||
if boostPeer.id == controller.peerId {
|
||||
myBoostCount += 1
|
||||
} else {
|
||||
availableBoosts.append(boost)
|
||||
occupiedBoosts.append(boost)
|
||||
}
|
||||
} else {
|
||||
availableBoosts.append(boost)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1868,23 +1873,10 @@ public class PremiumBoostLevelsScreen: ViewController {
|
||||
status: controller.status,
|
||||
boostState: self.boostState,
|
||||
boost: { [weak controller] in
|
||||
guard let controller, let navigationController = controller.navigationController else {
|
||||
guard let controller else {
|
||||
return
|
||||
}
|
||||
|
||||
controller.dismiss(animated: true)
|
||||
|
||||
Queue.mainQueue().justDispatch {
|
||||
let boostController = PremiumBoostLevelsScreen(
|
||||
context: controller.context,
|
||||
peerId: controller.peerId,
|
||||
mode: .user(mode: .current),
|
||||
status: controller.status,
|
||||
myBoostStatus: nil,
|
||||
forceDark: controller.forceDark
|
||||
)
|
||||
navigationController.pushViewController(boostController, animated: true)
|
||||
}
|
||||
controller.node.updateBoostState()
|
||||
},
|
||||
copyLink: { [weak self, weak controller] link in
|
||||
guard let self else {
|
||||
@ -2217,6 +2209,13 @@ public class PremiumBoostLevelsScreen: ViewController {
|
||||
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot])
|
||||
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
|
||||
self?.updatedState.set(context.engine.peers.getChannelBoostStatus(peerId: peerId)
|
||||
|> beforeNext { [weak self] status in
|
||||
if let self, let status {
|
||||
Queue.mainQueue().async {
|
||||
self.controller?.boostStatusUpdated(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> map { status in
|
||||
if let status {
|
||||
return InternalBoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1))
|
||||
@ -2361,7 +2360,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
||||
controller?.dismiss(animated: true, completion: nil)
|
||||
|
||||
Queue.mainQueue().after(0.4) {
|
||||
let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost)
|
||||
let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil)
|
||||
navigationController.pushViewController(giftController, animated: true)
|
||||
}
|
||||
}
|
||||
@ -2432,6 +2431,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
||||
|
||||
private var currentLayout: ContainerViewLayout?
|
||||
|
||||
public var boostStatusUpdated: (ChannelBoostStatus) -> Void = { _ in }
|
||||
public var disposed: () -> Void = {}
|
||||
|
||||
public init(
|
||||
@ -2501,7 +2501,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
||||
|
||||
override open func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
|
||||
self.node.updateIsVisible(isVisible: false)
|
||||
}
|
||||
|
||||
|
||||
@ -239,7 +239,7 @@ public func PremiumBoostScreen(
|
||||
dismissImpl?()
|
||||
|
||||
Queue.mainQueue().after(0.4) {
|
||||
let controller = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost)
|
||||
let controller = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil)
|
||||
pushController(controller)
|
||||
}
|
||||
}),
|
||||
|
||||
@ -252,49 +252,53 @@ public class PremiumLimitDisplayComponent: Component {
|
||||
rotationAngle = 0.26
|
||||
}
|
||||
|
||||
let to: CGFloat = self.badgeView.center.x
|
||||
|
||||
let positionAnimation = CABasicAnimation(keyPath: "position.x")
|
||||
positionAnimation.fromValue = NSValue(cgPoint: CGPoint(x: from ?? 0.0, y: 0.0))
|
||||
positionAnimation.toValue = NSValue(cgPoint: self.badgeView.center)
|
||||
positionAnimation.toValue = NSValue(cgPoint: CGPoint(x: to, y: 0.0))
|
||||
positionAnimation.duration = 0.5
|
||||
positionAnimation.fillMode = .forwards
|
||||
positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
self.badgeView.layer.add(positionAnimation, forKey: "appearance1")
|
||||
|
||||
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
rotateAnimation.fromValue = 0.0 as NSNumber
|
||||
rotateAnimation.toValue = -rotationAngle as NSNumber
|
||||
rotateAnimation.duration = 0.15
|
||||
rotateAnimation.fillMode = .forwards
|
||||
rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||
rotateAnimation.isRemovedOnCompletion = false
|
||||
self.badgeView.layer.add(rotateAnimation, forKey: "appearance2")
|
||||
|
||||
Queue.mainQueue().after(0.5, {
|
||||
let bounceAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
bounceAnimation.fromValue = -rotationAngle as NSNumber
|
||||
bounceAnimation.toValue = 0.04 as NSNumber
|
||||
bounceAnimation.duration = 0.2
|
||||
bounceAnimation.fillMode = .forwards
|
||||
bounceAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||
bounceAnimation.isRemovedOnCompletion = false
|
||||
self.badgeView.layer.add(bounceAnimation, forKey: "appearance3")
|
||||
self.badgeView.layer.removeAnimation(forKey: "appearance2")
|
||||
if from != to {
|
||||
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
rotateAnimation.fromValue = 0.0 as NSNumber
|
||||
rotateAnimation.toValue = -rotationAngle as NSNumber
|
||||
rotateAnimation.duration = 0.15
|
||||
rotateAnimation.fillMode = .forwards
|
||||
rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||
rotateAnimation.isRemovedOnCompletion = false
|
||||
self.badgeView.layer.add(rotateAnimation, forKey: "appearance2")
|
||||
|
||||
if !self.badgeView.isHidden {
|
||||
self.hapticFeedback.impact(.light)
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(0.2) {
|
||||
let returnAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
returnAnimation.fromValue = 0.04 as NSNumber
|
||||
returnAnimation.toValue = 0.0 as NSNumber
|
||||
returnAnimation.duration = 0.15
|
||||
returnAnimation.fillMode = .forwards
|
||||
returnAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)
|
||||
self.badgeView.layer.add(returnAnimation, forKey: "appearance4")
|
||||
self.badgeView.layer.removeAnimation(forKey: "appearance3")
|
||||
}
|
||||
})
|
||||
Queue.mainQueue().after(0.5, {
|
||||
let bounceAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
bounceAnimation.fromValue = -rotationAngle as NSNumber
|
||||
bounceAnimation.toValue = 0.04 as NSNumber
|
||||
bounceAnimation.duration = 0.2
|
||||
bounceAnimation.fillMode = .forwards
|
||||
bounceAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||
bounceAnimation.isRemovedOnCompletion = false
|
||||
self.badgeView.layer.add(bounceAnimation, forKey: "appearance3")
|
||||
self.badgeView.layer.removeAnimation(forKey: "appearance2")
|
||||
|
||||
if !self.badgeView.isHidden {
|
||||
self.hapticFeedback.impact(.light)
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(0.2) {
|
||||
let returnAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
returnAnimation.fromValue = 0.04 as NSNumber
|
||||
returnAnimation.toValue = 0.0 as NSNumber
|
||||
returnAnimation.duration = 0.15
|
||||
returnAnimation.fillMode = .forwards
|
||||
returnAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)
|
||||
self.badgeView.layer.add(returnAnimation, forKey: "appearance4")
|
||||
self.badgeView.layer.removeAnimation(forKey: "appearance3")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if from == nil {
|
||||
self.badgeView.alpha = 1.0
|
||||
|
||||
@ -880,7 +880,7 @@ public class ReplaceBoostScreen: ViewController {
|
||||
}
|
||||
let navigationController = self.navigationController
|
||||
self.dismiss(animated: true, completion: {
|
||||
let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost)
|
||||
let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil)
|
||||
navigationController?.pushViewController(giftController, animated: true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ final class BoostHeaderItem: ItemListControllerHeaderItem {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let status: ChannelBoostStatus
|
||||
let status: ChannelBoostStatus?
|
||||
let title: String
|
||||
let text: String
|
||||
let openBoost: () -> Void
|
||||
@ -28,7 +28,7 @@ final class BoostHeaderItem: ItemListControllerHeaderItem {
|
||||
let back: () -> Void
|
||||
let updateStatusBar: (StatusBarStyle) -> Void
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, status: ChannelBoostStatus, title: String, text: String, openBoost: @escaping () -> Void, createGiveaway: @escaping () -> Void, openFeatures: @escaping () -> Void, back: @escaping () -> Void, updateStatusBar: @escaping (StatusBarStyle) -> Void) {
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, status: ChannelBoostStatus?, title: String, text: String, openBoost: @escaping () -> Void, createGiveaway: @escaping () -> Void, openFeatures: @escaping () -> Void, back: @escaping () -> Void, updateStatusBar: @escaping (StatusBarStyle) -> Void) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
@ -44,7 +44,7 @@ final class BoostHeaderItem: ItemListControllerHeaderItem {
|
||||
|
||||
func isEqual(to: ItemListControllerHeaderItem) -> Bool {
|
||||
if let item = to as? BoostHeaderItem {
|
||||
return self.theme === item.theme && self.title == item.title && self.text == item.text
|
||||
return self.theme === item.theme && self.title == item.title && self.text == item.text && self.status == item.status
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@ -78,7 +78,7 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||
didSet {
|
||||
self.updateItem()
|
||||
if let layout = self.validLayout {
|
||||
let _ = self.updateLayout(layout: layout, transition: .immediate)
|
||||
let _ = self.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -197,6 +197,7 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||
strings: self.item.strings,
|
||||
text: self.item.text,
|
||||
status: self.item.status,
|
||||
insets: layout.safeInsets,
|
||||
openBoost: self.item.openBoost,
|
||||
createGiveaway: self.item.createGiveaway,
|
||||
openFeatures: self.item.openFeatures
|
||||
@ -205,7 +206,7 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||
|
||||
if let hostView = self.hostView {
|
||||
let size = hostView.update(
|
||||
transition: .immediate,
|
||||
transition: Transition(transition),
|
||||
component: component,
|
||||
environment: {},
|
||||
containerSize: containerSize
|
||||
@ -220,7 +221,7 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||
self.whiteTitleNode.position = self.titleNode.position
|
||||
|
||||
let backSize = self.backButton.update(key: .back, presentationData: self.item.context.sharedContext.currentPresentationData.with { $0 }, height: 44.0)
|
||||
self.backButton.frame = CGRect(origin: CGPoint(x: 16.0, y: 54.0), size: backSize)
|
||||
self.backButton.frame = CGRect(origin: CGPoint(x: layout.safeInsets.left + 16.0, y: statusBarHeight), size: backSize)
|
||||
|
||||
self.component = component
|
||||
self.validLayout = layout
|
||||
@ -244,7 +245,8 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||
private final class BoostHeaderComponent: CombinedComponent {
|
||||
let strings: PresentationStrings
|
||||
let text: String
|
||||
let status: ChannelBoostStatus
|
||||
let status: ChannelBoostStatus?
|
||||
let insets: UIEdgeInsets
|
||||
let openBoost: () -> Void
|
||||
let createGiveaway: () -> Void
|
||||
let openFeatures: () -> Void
|
||||
@ -252,7 +254,8 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||
public init(
|
||||
strings: PresentationStrings,
|
||||
text: String,
|
||||
status: ChannelBoostStatus,
|
||||
status: ChannelBoostStatus?,
|
||||
insets: UIEdgeInsets,
|
||||
openBoost: @escaping () -> Void,
|
||||
createGiveaway: @escaping () -> Void,
|
||||
openFeatures: @escaping () -> Void
|
||||
@ -260,6 +263,7 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||
self.strings = strings
|
||||
self.text = text
|
||||
self.status = status
|
||||
self.insets = insets
|
||||
self.openBoost = openBoost
|
||||
self.createGiveaway = createGiveaway
|
||||
self.openFeatures = openFeatures
|
||||
@ -275,6 +279,9 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||
if lhs.status != rhs.status {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -290,8 +297,9 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||
|
||||
return { context in
|
||||
let size = context.availableSize
|
||||
let sideInset: CGFloat = 16.0
|
||||
|
||||
let component = context.component
|
||||
let sideInset: CGFloat = 16.0 + component.insets.left
|
||||
|
||||
let background = background.update(
|
||||
component: PremiumGradientBackgroundComponent(
|
||||
@ -321,12 +329,19 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height / 2.0 + 10.0))
|
||||
)
|
||||
|
||||
let level = component.status.level
|
||||
let boosts: Int
|
||||
let level = component.status?.level ?? 0
|
||||
let position: CGFloat
|
||||
if let nextLevelBoosts = component.status.nextLevelBoosts {
|
||||
position = CGFloat(component.status.boosts - component.status.currentLevelBoosts) / CGFloat(nextLevelBoosts - component.status.currentLevelBoosts)
|
||||
if let status = component.status {
|
||||
if let nextLevelBoosts = status.nextLevelBoosts {
|
||||
position = CGFloat(status.boosts - status.currentLevelBoosts) / CGFloat(nextLevelBoosts - status.currentLevelBoosts)
|
||||
} else {
|
||||
position = 1.0
|
||||
}
|
||||
boosts = status.boosts
|
||||
} else {
|
||||
position = 1.0
|
||||
boosts = 0
|
||||
position = 0.0
|
||||
}
|
||||
|
||||
let inactiveText = component.strings.ChannelBoost_Level("\(level)").string
|
||||
@ -343,7 +358,7 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||
activeValue: activeText,
|
||||
activeTitleColor: UIColor(rgb: 0x6f8fff),
|
||||
badgeIconName: "Premium/Boost",
|
||||
badgeText: "\(component.status.boosts)",
|
||||
badgeText: "\(boosts)",
|
||||
badgePosition: position,
|
||||
badgeGraphPosition: position,
|
||||
invertProgress: true,
|
||||
|
||||
@ -1046,7 +1046,7 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
|
||||
return entries
|
||||
}
|
||||
|
||||
public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = nil) -> ViewController {
|
||||
public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = nil, boostStatusUpdated: ((ChannelBoostStatus) -> Void)? = nil) -> ViewController {
|
||||
let statePromise = ValuePromise(ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false))
|
||||
let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in
|
||||
@ -1087,12 +1087,16 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
})
|
||||
dataPromise.set(.single(nil) |> then(dataSignal))
|
||||
|
||||
let boostData: Signal<ChannelBoostStatus?, NoError>
|
||||
if let boostStatus {
|
||||
boostData = .single(boostStatus)
|
||||
} else {
|
||||
boostData = .single(nil) |> then(context.engine.peers.getChannelBoostStatus(peerId: peerId))
|
||||
}
|
||||
let boostDataPromise = Promise<ChannelBoostStatus?>()
|
||||
boostDataPromise.set(.single(boostStatus) |> then(context.engine.peers.getChannelBoostStatus(peerId: peerId)))
|
||||
|
||||
actionsDisposable.add((boostDataPromise.get()
|
||||
|> deliverOnMainQueue).start(next: { boostStatus in
|
||||
if let boostStatus, let boostStatusUpdated {
|
||||
boostStatusUpdated(boostStatus)
|
||||
}
|
||||
}))
|
||||
|
||||
let boostsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: false)
|
||||
let giftsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: true)
|
||||
|
||||
@ -1253,7 +1257,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
dataPromise.get(),
|
||||
messagesPromise.get(),
|
||||
storiesPromise.get(),
|
||||
boostData,
|
||||
boostDataPromise.get(),
|
||||
boostsContext.state,
|
||||
giftsContext.state,
|
||||
longLoadingSignal
|
||||
@ -1307,9 +1311,9 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
var headerItem: BoostHeaderItem?
|
||||
var leftNavigationButton: ItemListNavigationButton?
|
||||
var boostsOnly = false
|
||||
if isGroup, section == .boosts, let boostStatus {
|
||||
if isGroup, section == .boosts {
|
||||
title = .text("")
|
||||
headerItem = BoostHeaderItem(context: context, theme: presentationData.theme, strings: presentationData.strings, status: boostStatus, title: presentationData.strings.GroupBoost_Title, text: presentationData.strings.GroupBoost_Info, openBoost: {
|
||||
headerItem = BoostHeaderItem(context: context, theme: presentationData.theme, strings: presentationData.strings, status: boostData, title: presentationData.strings.GroupBoost_Title, text: presentationData.strings.GroupBoost_Info, openBoost: {
|
||||
openBoostImpl?(false)
|
||||
}, createGiveaway: {
|
||||
arguments.openGifts()
|
||||
@ -1508,13 +1512,18 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
guard let boostStatus, let myBoostStatus else {
|
||||
return
|
||||
}
|
||||
boostDataPromise.set(.single(boostStatus))
|
||||
|
||||
let boostController = PremiumBoostLevelsScreen(
|
||||
context: context,
|
||||
peerId: peerId,
|
||||
mode: .user(mode: .current),
|
||||
mode: .owner(subject: nil),
|
||||
status: boostStatus,
|
||||
myBoostStatus: myBoostStatus
|
||||
)
|
||||
boostController.boostStatusUpdated = { boostStatus in
|
||||
boostDataPromise.set(.single(boostStatus))
|
||||
}
|
||||
controller?.push(boostController)
|
||||
})
|
||||
}
|
||||
|
||||
@ -566,7 +566,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1328256121] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||
dict[-2083123262] = { return Api.MessageReplies.parse_messageReplies($0) }
|
||||
dict[-1346631205] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) }
|
||||
dict[-1667711039] = { return Api.MessageReplyHeader.parse_messageReplyStoryHeader($0) }
|
||||
dict[240843065] = { return Api.MessageReplyHeader.parse_messageReplyStoryHeader($0) }
|
||||
dict[1163625789] = { return Api.MessageViews.parse_messageViews($0) }
|
||||
dict[975236280] = { return Api.MessagesFilter.parse_inputMessagesFilterChatPhotos($0) }
|
||||
dict[-530392189] = { return Api.MessagesFilter.parse_inputMessagesFilterContacts($0) }
|
||||
@ -833,7 +833,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) }
|
||||
dict[1898850301] = { return Api.StoriesStealthMode.parse_storiesStealthMode($0) }
|
||||
dict[-1205411504] = { return Api.StoryFwdHeader.parse_storyFwdHeader($0) }
|
||||
dict[-1352440415] = { return Api.StoryItem.parse_storyItem($0) }
|
||||
dict[2041735716] = { return Api.StoryItem.parse_storyItem($0) }
|
||||
dict[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) }
|
||||
dict[-5388013] = { return Api.StoryItem.parse_storyItemSkipped($0) }
|
||||
dict[1620104917] = { return Api.StoryReaction.parse_storyReaction($0) }
|
||||
|
||||
@ -323,7 +323,7 @@ public extension Api {
|
||||
public extension Api {
|
||||
indirect enum MessageReplyHeader: TypeConstructorDescription {
|
||||
case messageReplyHeader(flags: Int32, replyToMsgId: Int32?, replyToPeerId: Api.Peer?, replyFrom: Api.MessageFwdHeader?, replyMedia: Api.MessageMedia?, replyToTopId: Int32?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?)
|
||||
case messageReplyStoryHeader(userId: Int64, storyId: Int32)
|
||||
case messageReplyStoryHeader(peer: Api.Peer, storyId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -345,11 +345,11 @@ public extension Api {
|
||||
}}
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageReplyStoryHeader(let userId, let storyId):
|
||||
case .messageReplyStoryHeader(let peer, let storyId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1667711039)
|
||||
buffer.appendInt32(240843065)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(storyId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
@ -359,8 +359,8 @@ public extension Api {
|
||||
switch self {
|
||||
case .messageReplyHeader(let flags, let replyToMsgId, let replyToPeerId, let replyFrom, let replyMedia, let replyToTopId, let quoteText, let quoteEntities, let quoteOffset):
|
||||
return ("messageReplyHeader", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("replyFrom", replyFrom as Any), ("replyMedia", replyMedia as Any), ("replyToTopId", replyToTopId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)])
|
||||
case .messageReplyStoryHeader(let userId, let storyId):
|
||||
return ("messageReplyStoryHeader", [("userId", userId as Any), ("storyId", storyId as Any)])
|
||||
case .messageReplyStoryHeader(let peer, let storyId):
|
||||
return ("messageReplyStoryHeader", [("peer", peer as Any), ("storyId", storyId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,14 +408,16 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public static func parse_messageReplyStoryHeader(_ reader: BufferReader) -> MessageReplyHeader? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageReplyHeader.messageReplyStoryHeader(userId: _1!, storyId: _2!)
|
||||
return Api.MessageReplyHeader.messageReplyStoryHeader(peer: _1!, storyId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -1034,19 +1034,20 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum StoryItem: TypeConstructorDescription {
|
||||
case storyItem(flags: Int32, id: Int32, date: Int32, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?)
|
||||
case storyItem(flags: Int32, id: Int32, date: Int32, fromId: Api.Peer?, fwdFrom: Api.StoryFwdHeader?, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, mediaAreas: [Api.MediaArea]?, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?, sentReaction: Api.Reaction?)
|
||||
case storyItemDeleted(id: Int32)
|
||||
case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyItem(let flags, let id, let date, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction):
|
||||
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1352440415)
|
||||
buffer.appendInt32(2041735716)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 18) != 0 {fromId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {fwdFrom!.serialize(buffer, true)}
|
||||
serializeInt32(expireDate, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, buffer: buffer, boxed: false)}
|
||||
@ -1089,8 +1090,8 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyItem(let flags, let id, let date, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction):
|
||||
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any)])
|
||||
case .storyItem(let flags, let id, let date, let fromId, let fwdFrom, let expireDate, let caption, let entities, let media, let mediaAreas, let privacy, let views, let sentReaction):
|
||||
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("fromId", fromId as Any), ("fwdFrom", fwdFrom as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("mediaAreas", mediaAreas as Any), ("privacy", privacy as Any), ("views", views as Any), ("sentReaction", sentReaction as Any)])
|
||||
case .storyItemDeleted(let id):
|
||||
return ("storyItemDeleted", [("id", id as Any)])
|
||||
case .storyItemSkipped(let flags, let id, let date, let expireDate):
|
||||
@ -1105,52 +1106,57 @@ public extension Api {
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Api.StoryFwdHeader?
|
||||
var _4: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _5: Api.StoryFwdHeader?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.StoryFwdHeader
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.StoryFwdHeader
|
||||
} }
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||
var _7: [Api.MessageEntity]?
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
|
||||
var _8: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _8: Api.MessageMedia?
|
||||
var _9: Api.MessageMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
}
|
||||
var _9: [Api.MediaArea]?
|
||||
var _10: [Api.MediaArea]?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let _ = reader.readInt32() {
|
||||
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MediaArea.self)
|
||||
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MediaArea.self)
|
||||
} }
|
||||
var _10: [Api.PrivacyRule]?
|
||||
var _11: [Api.PrivacyRule]?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self)
|
||||
_11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self)
|
||||
} }
|
||||
var _11: Api.StoryViews?
|
||||
var _12: Api.StoryViews?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_11 = Api.parse(reader, signature: signature) as? Api.StoryViews
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.StoryViews
|
||||
} }
|
||||
var _12: Api.Reaction?
|
||||
var _13: Api.Reaction?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 17) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 14) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 15) == 0) || _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fwdFrom: _4, expireDate: _5!, caption: _6, entities: _7, media: _8!, mediaAreas: _9, privacy: _10, views: _11, sentReaction: _12)
|
||||
let _c4 = (Int(_1!) & Int(1 << 18) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 17) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 2) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
|
||||
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -200,8 +200,8 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
|
||||
if let replyTo = replyTo {
|
||||
switch replyTo {
|
||||
case let .messageReplyStoryHeader(userId, _):
|
||||
let storyPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
case let .messageReplyStoryHeader(peer, _):
|
||||
let storyPeerId = peer.peerId
|
||||
if !result.contains(storyPeerId) {
|
||||
result.append(storyPeerId)
|
||||
}
|
||||
@ -668,8 +668,8 @@ extension StoreMessage {
|
||||
if let replyHeader = replyHeader {
|
||||
attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote))
|
||||
}
|
||||
case let .messageReplyStoryHeader(userId, storyId):
|
||||
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), id: storyId)))
|
||||
case let .messageReplyStoryHeader(peer, storyId):
|
||||
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: peer.peerId, id: storyId)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -952,8 +952,8 @@ extension StoreMessage {
|
||||
} else if let replyHeader = replyHeader {
|
||||
attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote))
|
||||
}
|
||||
case let .messageReplyStoryHeader(userId, storyId):
|
||||
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), id: storyId)))
|
||||
case let .messageReplyStoryHeader(peer, storyId):
|
||||
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: peer.peerId, id: storyId)))
|
||||
}
|
||||
} else {
|
||||
switch action {
|
||||
|
||||
@ -4712,7 +4712,8 @@ func replayFinalState(
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: updatedReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
updatedPeerEntries[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: item.expirationTimestamp, isCloseFriends: item.isCloseFriends)
|
||||
@ -4745,7 +4746,8 @@ func replayFinalState(
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: MessageReaction.Reaction(apiReaction: reaction),
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
transaction.setStory(id: StoryId(peerId: peerId, id: id), value: entry)
|
||||
|
||||
@ -349,7 +349,8 @@ private final class StoryStatsPublicForwardsContextImpl {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
)
|
||||
resultForwards.append(.story(EnginePeer(peer), mappedItem))
|
||||
}
|
||||
|
||||
@ -556,7 +556,8 @@ public final class EngineStoryViewListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
),
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peer.id)
|
||||
)))
|
||||
@ -595,7 +596,8 @@ public final class EngineStoryViewListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry)
|
||||
@ -634,7 +636,8 @@ public final class EngineStoryViewListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
@ -748,7 +751,8 @@ public final class EngineStoryViewListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
),
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peer.id)
|
||||
)))
|
||||
|
||||
@ -261,6 +261,7 @@ public enum Stories {
|
||||
case isMy
|
||||
case myReaction
|
||||
case forwardInfo
|
||||
case authorId
|
||||
}
|
||||
|
||||
public let id: Int32
|
||||
@ -284,6 +285,7 @@ public enum Stories {
|
||||
public let isMy: Bool
|
||||
public let myReaction: MessageReaction.Reaction?
|
||||
public let forwardInfo: ForwardInfo?
|
||||
public let authorId: PeerId?
|
||||
|
||||
public init(
|
||||
id: Int32,
|
||||
@ -306,7 +308,8 @@ public enum Stories {
|
||||
isEdited: Bool,
|
||||
isMy: Bool,
|
||||
myReaction: MessageReaction.Reaction?,
|
||||
forwardInfo: ForwardInfo?
|
||||
forwardInfo: ForwardInfo?,
|
||||
authorId: PeerId?
|
||||
) {
|
||||
self.id = id
|
||||
self.timestamp = timestamp
|
||||
@ -329,6 +332,7 @@ public enum Stories {
|
||||
self.isMy = isMy
|
||||
self.myReaction = myReaction
|
||||
self.forwardInfo = forwardInfo
|
||||
self.authorId = authorId
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -367,6 +371,7 @@ public enum Stories {
|
||||
self.isMy = try container.decodeIfPresent(Bool.self, forKey: .isMy) ?? false
|
||||
self.myReaction = try container.decodeIfPresent(MessageReaction.Reaction.self, forKey: .myReaction)
|
||||
self.forwardInfo = try container.decodeIfPresent(ForwardInfo.self, forKey: .forwardInfo)
|
||||
self.authorId = try container.decodeIfPresent(Int64.self, forKey: .authorId).flatMap { PeerId($0) }
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -407,6 +412,7 @@ public enum Stories {
|
||||
try container.encode(self.isMy, forKey: .isMy)
|
||||
try container.encodeIfPresent(self.myReaction, forKey: .myReaction)
|
||||
try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo)
|
||||
try container.encodeIfPresent(self.authorId?.toInt64(), forKey: .authorId)
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
@ -485,6 +491,9 @@ public enum Stories {
|
||||
if lhs.forwardInfo != rhs.forwardInfo {
|
||||
return false
|
||||
}
|
||||
if lhs.authorId != rhs.authorId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1173,7 +1182,7 @@ func _internal_uploadStoryImpl(
|
||||
for update in updates.allUpdates {
|
||||
if case let .updateStory(_, story) = update {
|
||||
switch story {
|
||||
case let .storyItem(_, idValue, _, _, _, _, _, media, _, _, _, _):
|
||||
case let .storyItem(_, idValue, _, fromId, _, _, _, _, media, _, _, _, _):
|
||||
if let parsedStory = Stories.StoredItem(apiStoryItem: story, peerId: toPeerId, transaction: transaction) {
|
||||
var items = transaction.getStoryItems(peerId: toPeerId)
|
||||
var updatedItems: [Stories.Item] = []
|
||||
@ -1199,7 +1208,8 @@ func _internal_uploadStoryImpl(
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: fromId?.peerId
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends))
|
||||
@ -1336,7 +1346,7 @@ func _internal_editStory(account: Account, peerId: PeerId, id: Int32, media: Eng
|
||||
for update in updates.allUpdates {
|
||||
if case let .updateStory(_, story) = update {
|
||||
switch story {
|
||||
case let .storyItem(_, _, _, _, _, _, _, media, _, _, _, _):
|
||||
case let .storyItem(_, _, _, _, _, _, _, _, media, _, _, _, _):
|
||||
let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
|
||||
if let parsedMedia = parsedMedia, let originalMedia = originalMedia {
|
||||
applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false)
|
||||
@ -1381,7 +1391,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
transaction.setStory(id: storyId, value: entry)
|
||||
@ -1412,7 +1423,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
@ -1606,7 +1618,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
@ -1636,7 +1649,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
)
|
||||
updatedItems.append(updatedItem)
|
||||
}
|
||||
@ -1668,7 +1682,7 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
||||
extension Api.StoryItem {
|
||||
var id: Int32 {
|
||||
switch self {
|
||||
case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return id
|
||||
case let .storyItemDeleted(id):
|
||||
return id
|
||||
@ -1731,7 +1745,7 @@ extension Stories.Item.ForwardInfo {
|
||||
extension Stories.StoredItem {
|
||||
init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) {
|
||||
switch apiStoryItem {
|
||||
case let .storyItem(flags, id, date, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction):
|
||||
case let .storyItem(flags, id, date, fromId, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction):
|
||||
let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
||||
if let parsedMedia = parsedMedia {
|
||||
var parsedPrivacy: Stories.Item.Privacy?
|
||||
@ -1840,7 +1854,8 @@ extension Stories.StoredItem {
|
||||
isEdited: isEdited,
|
||||
isMy: mergedIsMy,
|
||||
myReaction: mergedMyReaction,
|
||||
forwardInfo: mergedForwardInfo
|
||||
forwardInfo: mergedForwardInfo,
|
||||
authorId: fromId?.peerId
|
||||
)
|
||||
self = .item(item)
|
||||
} else {
|
||||
@ -1916,7 +1931,8 @@ func _internal_getStoryById(accountPeerId: PeerId, postbox: Postbox, network: Ne
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -2386,7 +2402,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: reaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
))
|
||||
updatedItemValue = updatedItem
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
@ -2419,7 +2436,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: reaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
))
|
||||
updatedItemValue = updatedItem
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
|
||||
@ -78,8 +78,9 @@ public final class EngineStoryItem: Equatable {
|
||||
public let isMy: Bool
|
||||
public let myReaction: MessageReaction.Reaction?
|
||||
public let forwardInfo: ForwardInfo?
|
||||
public let author: EnginePeer?
|
||||
|
||||
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, alternativeMedia: EngineMedia?, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?, forwardInfo: ForwardInfo?) {
|
||||
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, alternativeMedia: EngineMedia?, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?, forwardInfo: ForwardInfo?, author: EnginePeer?) {
|
||||
self.id = id
|
||||
self.timestamp = timestamp
|
||||
self.expirationTimestamp = expirationTimestamp
|
||||
@ -102,6 +103,7 @@ public final class EngineStoryItem: Equatable {
|
||||
self.isMy = isMy
|
||||
self.myReaction = myReaction
|
||||
self.forwardInfo = forwardInfo
|
||||
self.author = author
|
||||
}
|
||||
|
||||
public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool {
|
||||
@ -171,6 +173,9 @@ public final class EngineStoryItem: Equatable {
|
||||
if lhs.forwardInfo != rhs.forwardInfo {
|
||||
return false
|
||||
}
|
||||
if lhs.author != rhs.author {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -223,7 +228,8 @@ public extension EngineStoryItem {
|
||||
isEdited: self.isEdited,
|
||||
isMy: self.isMy,
|
||||
myReaction: self.myReaction,
|
||||
forwardInfo: self.forwardInfo?.storedForwardInfo
|
||||
forwardInfo: self.forwardInfo?.storedForwardInfo,
|
||||
authorId: self.author?.id
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -600,7 +606,8 @@ public final class PeerStoryListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
)
|
||||
items.append(mappedItem)
|
||||
|
||||
@ -745,7 +752,8 @@ public final class PeerStoryListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
)
|
||||
storyItems.append(mappedItem)
|
||||
}
|
||||
@ -839,6 +847,11 @@ public final class PeerStoryListContext {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
if let peerId = item.authorId {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -907,7 +920,8 @@ public final class PeerStoryListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
@ -955,7 +969,8 @@ public final class PeerStoryListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
)
|
||||
finalUpdatedState = updatedState
|
||||
} else {
|
||||
@ -1005,7 +1020,8 @@ public final class PeerStoryListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
return lhs.timestamp > rhs.timestamp
|
||||
@ -1051,7 +1067,8 @@ public final class PeerStoryListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
return lhs.timestamp > rhs.timestamp
|
||||
@ -1221,7 +1238,8 @@ public final class PeerExpiringStoryListContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
)
|
||||
items.append(.item(mappedItem))
|
||||
}
|
||||
|
||||
@ -1249,7 +1249,8 @@ public extension TelegramEngine {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
|
||||
@ -232,6 +232,7 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
private static let botGameNoticeNamespace: Int32 = 7
|
||||
private static let peerInviteRequestsNamespace: Int32 = 8
|
||||
private static let dismissedPremiumGiftNamespace: Int32 = 9
|
||||
private static let groupEmojiPackNamespace: Int32 = 9
|
||||
|
||||
static func inlineBotLocationRequestNotice(peerId: PeerId) -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: inlineBotLocationRequestNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
@ -253,6 +254,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: dismissedPremiumGiftNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
}
|
||||
|
||||
static func groupEmojiPackNotice(peerId: PeerId) -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: groupEmojiPackNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
}
|
||||
|
||||
static func forcedPasswordSetup() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.secretChatInlineBotUsage.key)
|
||||
}
|
||||
@ -1582,6 +1587,33 @@ public struct ApplicationSpecificNotice {
|
||||
}
|
||||
}
|
||||
|
||||
public static func groupEmojiPackSuggestion(accountManager: AccountManager<TelegramAccountManagerTypes>, peerId: PeerId) -> Signal<Int32, NoError> {
|
||||
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.groupEmojiPackNotice(peerId: peerId))
|
||||
|> map { view -> Int32 in
|
||||
if let value = view.value?.get(ApplicationSpecificCounterNotice.self) {
|
||||
return value.value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func incrementGroupEmojiPackSuggestion(accountManager: AccountManager<TelegramAccountManagerTypes>, peerId: PeerId, count: Int32 = 1) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
var currentValue: Int32 = 0
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.groupEmojiPackNotice(peerId: peerId))?.get(ApplicationSpecificCounterNotice.self) {
|
||||
currentValue = value.value
|
||||
}
|
||||
let previousValue = currentValue
|
||||
currentValue += count
|
||||
|
||||
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.groupEmojiPackNotice(peerId: peerId), entry)
|
||||
}
|
||||
return previousValue
|
||||
}
|
||||
}
|
||||
|
||||
public static func getSendWhenOnlineTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.sendWhenOnlineTip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
|
||||
@ -2274,7 +2274,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
}
|
||||
|
||||
nameNodeOriginY = headerSize.height
|
||||
headerSize.width = max(headerSize.width, nameNodeSizeApply.0.width + adminBadgeSizeAndApply.0.size.width + credibilityIconWidth + boostBadgeWidth + closeButtonWidth + bubbleWidthInsets)
|
||||
|
||||
headerSize.width = max(headerSize.width, nameNodeSizeApply.0.width + 8.0 + adminBadgeSizeAndApply.0.size.width + credibilityIconWidth + boostBadgeWidth + closeButtonWidth + bubbleWidthInsets)
|
||||
headerSize.height += nameNodeSizeApply.0.height
|
||||
}
|
||||
|
||||
|
||||
@ -2129,14 +2129,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
var entities: [MessageTextEntity] = []
|
||||
|
||||
if new != nil {
|
||||
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupStickerPack(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
||||
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupEmojiPack(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
||||
if index == 0, let author = author {
|
||||
return [.TextMention(peerId: author.id)]
|
||||
}
|
||||
return []
|
||||
}, to: &text, entities: &entities)
|
||||
} else {
|
||||
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRemovedGroupStickerPack(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
||||
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRemovedGroupEmojiPack(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
||||
if index == 0, let author = author {
|
||||
return [.TextMention(peerId: author.id)]
|
||||
}
|
||||
|
||||
@ -390,7 +390,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
public var useExternalSearchContainer: Bool = false
|
||||
|
||||
private var gifContext: GifContext? {
|
||||
@ -2133,6 +2133,12 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
strongSelf.interaction?.presentGlobalOverlayController(contextController, nil)
|
||||
})
|
||||
}
|
||||
|
||||
public func scrollToGroupEmoji() {
|
||||
if let pagerView = self.entityKeyboardView.componentView as? EntityKeyboardComponent.View {
|
||||
pagerView.scrollToItemGroup(contentId: "emoji", groupId: "peerSpecific", subgroupId: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||
|
||||
@ -23,6 +23,7 @@ swift_library(
|
||||
"//submodules/SearchBarNode:SearchBarNode",
|
||||
"//submodules/SearchUI:SearchUI",
|
||||
"//submodules/MergeLists:MergeLists",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -344,6 +344,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
if case .searching = item.content {
|
||||
strongSelf.activityIndicator.isHidden = false
|
||||
strongSelf.imageNode.isHidden = true
|
||||
} else {
|
||||
strongSelf.activityIndicator.isHidden = true
|
||||
}
|
||||
@ -351,6 +352,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
||||
if case .found = item.content {
|
||||
strongSelf.removeButtonIcon.isHidden = false
|
||||
strongSelf.removeButton.isHidden = false
|
||||
strongSelf.imageNode.isHidden = false
|
||||
} else {
|
||||
strongSelf.removeButtonIcon.isHidden = true
|
||||
strongSelf.removeButton.isHidden = true
|
||||
|
||||
@ -11,6 +11,7 @@ import PresentationDataUtils
|
||||
import AccountContext
|
||||
import StickerPackPreviewUI
|
||||
import ItemListStickerPackItem
|
||||
import UndoUI
|
||||
|
||||
private final class GroupStickerPackSetupControllerArguments {
|
||||
let context: AccountContext
|
||||
@ -343,6 +344,20 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
initialData.set(.single(.noData))
|
||||
}
|
||||
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
|
||||
var completionImpl: ((StickerPackCollectionInfo?) -> Void)?
|
||||
if let completion {
|
||||
completionImpl = { value in
|
||||
completion(value)
|
||||
if let _ = value {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: presentationData.strings.Group_Appearance_EmojiPackUpdated, cancel: nil, destructive: false), elevatedLayout: false, action: { _ in return true })
|
||||
presentControllerImpl?(controller, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let stickerPacks = Promise<CombinedView>()
|
||||
stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [isEmoji ? Namespaces.ItemCollection.CloudEmojiPacks : Namespaces.ItemCollection.CloudStickerPacks])]))
|
||||
|
||||
@ -353,6 +368,9 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
if searchText.isEmpty {
|
||||
return .single((searchText, .none))
|
||||
} else if case let .data(data) = initialData, searchText.lowercased() == data.info.shortName {
|
||||
Queue.mainQueue().async {
|
||||
completionImpl?(data.info)
|
||||
}
|
||||
return .single((searchText, .found(StickerPackData(info: data.info, item: data.item))))
|
||||
} else {
|
||||
let namespace = isEmoji ? Namespaces.ItemCollection.CloudEmojiPacks : Namespaces.ItemCollection.CloudStickerPacks
|
||||
@ -378,6 +396,13 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
case let .result(info, items, _):
|
||||
return .single((searchText, .found(StickerPackData(info: info, item: items.first))))
|
||||
}
|
||||
}
|
||||
|> afterNext { value in
|
||||
if case let .found(data) = value.1 {
|
||||
Queue.mainQueue().async {
|
||||
completionImpl?(data.info)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
@ -385,7 +410,6 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
}
|
||||
})
|
||||
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var navigateToChatControllerImpl: ((PeerId) -> Void)?
|
||||
var dismissInputImpl: (() -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
@ -402,15 +426,13 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
|
||||
let arguments = GroupStickerPackSetupControllerArguments(context: context, selectStickerPack: { info in
|
||||
searchText.set(info.shortName)
|
||||
if let completion {
|
||||
completion(info)
|
||||
}
|
||||
completionImpl?(info)
|
||||
}, openStickerPack: { info in
|
||||
presentStickerPackController?(info)
|
||||
}, updateSearchText: { text in
|
||||
searchText.set(text)
|
||||
if text == "", let completion {
|
||||
completion(nil)
|
||||
if text == "" {
|
||||
completionImpl?(nil)
|
||||
}
|
||||
}, openStickersBot: {
|
||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers")
|
||||
@ -473,29 +495,24 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
info = data.info
|
||||
}
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: enabled, action: {
|
||||
if let completion {
|
||||
completion(info)
|
||||
if info?.id == currentPackInfo?.id {
|
||||
dismissImpl?()
|
||||
} else {
|
||||
if info?.id == currentPackInfo?.id {
|
||||
dismissImpl?()
|
||||
} else {
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.isSaving = true
|
||||
return state
|
||||
}
|
||||
saveDisposable.set((context.engine.peers.updateGroupSpecificStickerset(peerId: peerId, info: info)
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.isSaving = true
|
||||
state.isSaving = false
|
||||
return state
|
||||
}
|
||||
saveDisposable.set((context.engine.peers.updateGroupSpecificStickerset(peerId: peerId, info: info)
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.isSaving = false
|
||||
return state
|
||||
}
|
||||
}, completed: {
|
||||
dismissImpl?()
|
||||
}))
|
||||
}
|
||||
}, completed: {
|
||||
dismissImpl?()
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -537,6 +554,14 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
|
||||
presentControllerImpl = { [weak controller] c, p in
|
||||
if let controller = controller {
|
||||
if c is UndoOverlayController {
|
||||
controller.window?.forEachController { c in
|
||||
if let controller = c as? UndoOverlayController {
|
||||
controller.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
controller.present(c, in: .window(.root), with: p)
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,6 +208,7 @@ final class PeerInfoScreenData {
|
||||
let isPowerSavingEnabled: Bool?
|
||||
let accountIsPremium: Bool
|
||||
let hasSavedMessageTags: Bool
|
||||
let isPremiumRequiredForStoryPosting: Bool
|
||||
|
||||
let _isContact: Bool
|
||||
var forceIsContact: Bool = false
|
||||
@ -244,7 +245,8 @@ final class PeerInfoScreenData {
|
||||
appConfiguration: AppConfiguration?,
|
||||
isPowerSavingEnabled: Bool?,
|
||||
accountIsPremium: Bool,
|
||||
hasSavedMessageTags: Bool
|
||||
hasSavedMessageTags: Bool,
|
||||
isPremiumRequiredForStoryPosting: Bool
|
||||
) {
|
||||
self.peer = peer
|
||||
self.chatPeer = chatPeer
|
||||
@ -270,6 +272,7 @@ final class PeerInfoScreenData {
|
||||
self.isPowerSavingEnabled = isPowerSavingEnabled
|
||||
self.accountIsPremium = accountIsPremium
|
||||
self.hasSavedMessageTags = hasSavedMessageTags
|
||||
self.isPremiumRequiredForStoryPosting = isPremiumRequiredForStoryPosting
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,7 +669,8 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
|
||||
appConfiguration: appConfiguration,
|
||||
isPowerSavingEnabled: isPowerSavingEnabled,
|
||||
accountIsPremium: peer?.isPremium ?? false,
|
||||
hasSavedMessageTags: false
|
||||
hasSavedMessageTags: false,
|
||||
isPremiumRequiredForStoryPosting: true
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -702,7 +706,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
appConfiguration: nil,
|
||||
isPowerSavingEnabled: nil,
|
||||
accountIsPremium: false,
|
||||
hasSavedMessageTags: false
|
||||
hasSavedMessageTags: false,
|
||||
isPremiumRequiredForStoryPosting: true
|
||||
))
|
||||
case let .user(userPeerId, secretChatId, kind):
|
||||
let groupsInCommon: GroupsInCommonContext?
|
||||
@ -973,7 +978,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
appConfiguration: nil,
|
||||
isPowerSavingEnabled: nil,
|
||||
accountIsPremium: accountIsPremium,
|
||||
hasSavedMessageTags: hasSavedMessageTags
|
||||
hasSavedMessageTags: hasSavedMessageTags,
|
||||
isPremiumRequiredForStoryPosting: false
|
||||
)
|
||||
}
|
||||
case .channel:
|
||||
@ -1047,6 +1053,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
hasSavedMessageTags = .single(false)
|
||||
}
|
||||
|
||||
let isPremiumRequiredForStoryPosting: Signal<Bool, NoError> = isPremiumRequiredForStoryPosting(context: context)
|
||||
|
||||
return combineLatest(
|
||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
||||
@ -1061,9 +1069,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
context.engine.peers.recommendedChannels(peerId: peerId),
|
||||
hasSavedMessages,
|
||||
hasSavedMessagesChats,
|
||||
hasSavedMessageTags
|
||||
hasSavedMessageTags,
|
||||
isPremiumRequiredForStoryPosting
|
||||
)
|
||||
|> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags -> PeerInfoScreenData in
|
||||
|> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting -> PeerInfoScreenData in
|
||||
var availablePanes = availablePanes
|
||||
if let hasStories {
|
||||
if hasStories {
|
||||
@ -1138,7 +1147,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
appConfiguration: nil,
|
||||
isPowerSavingEnabled: nil,
|
||||
accountIsPremium: accountIsPremium,
|
||||
hasSavedMessageTags: hasSavedMessageTags
|
||||
hasSavedMessageTags: hasSavedMessageTags,
|
||||
isPremiumRequiredForStoryPosting: isPremiumRequiredForStoryPosting
|
||||
)
|
||||
}
|
||||
case let .group(groupId):
|
||||
@ -1315,6 +1325,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
hasSavedMessageTags = .single(false)
|
||||
}
|
||||
|
||||
let isPremiumRequiredForStoryPosting: Signal<Bool, NoError> = isPremiumRequiredForStoryPosting(context: context)
|
||||
|
||||
return combineLatest(queue: .mainQueue(),
|
||||
context.account.viewTracker.peerView(groupId, updateData: true),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
||||
@ -1331,9 +1343,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
accountIsPremium,
|
||||
hasSavedMessages,
|
||||
hasSavedMessagesChats,
|
||||
hasSavedMessageTags
|
||||
hasSavedMessageTags,
|
||||
isPremiumRequiredForStoryPosting
|
||||
)
|
||||
|> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, threadData, preferencesView, accountIsPremium, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags -> Signal<PeerInfoScreenData, NoError> in
|
||||
|> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, threadData, preferencesView, accountIsPremium, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting -> Signal<PeerInfoScreenData, NoError> in
|
||||
var discussionPeer: Peer?
|
||||
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
||||
discussionPeer = peer
|
||||
@ -1426,7 +1439,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
appConfiguration: appConfiguration,
|
||||
isPowerSavingEnabled: nil,
|
||||
accountIsPremium: accountIsPremium,
|
||||
hasSavedMessageTags: hasSavedMessageTags
|
||||
hasSavedMessageTags: hasSavedMessageTags,
|
||||
isPremiumRequiredForStoryPosting: isPremiumRequiredForStoryPosting
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -1812,3 +1826,25 @@ func peerInfoIsChatMuted(peer: Peer?, peerNotificationSettings: TelegramPeerNoti
|
||||
}
|
||||
return chatIsMuted
|
||||
}
|
||||
|
||||
private var isPremiumRequired: Bool?
|
||||
private func isPremiumRequiredForStoryPosting(context: AccountContext) -> Signal<Bool, NoError> {
|
||||
if let isPremiumRequired {
|
||||
return .single(isPremiumRequired)
|
||||
}
|
||||
|
||||
return .single(true)
|
||||
|> then(
|
||||
context.engine.messages.checkStoriesUploadAvailability(target: .myStories)
|
||||
|> deliverOnMainQueue
|
||||
|> map { status -> Bool in
|
||||
if case .premiumRequired = status {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} |> afterNext { value in
|
||||
isPremiumRequired = value
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -5718,7 +5718,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
})))
|
||||
}
|
||||
|
||||
if case .broadcast = channel.info, channel.hasPermission(.editStories) {
|
||||
if channel.hasPermission(.editStories) {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_Channel_ArchivedStories, icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
@ -6943,7 +6943,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
|
||||
if channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) {
|
||||
let boostsController = channelStatsController(context: self.context, updatedPresentationData: controller.updatedPresentationData, peerId: self.peerId, section: .boosts, boostStatus: self.boostStatus)
|
||||
let boostsController = channelStatsController(context: self.context, updatedPresentationData: controller.updatedPresentationData, peerId: self.peerId, section: .boosts, boostStatus: self.boostStatus, boostStatusUpdated: { [weak self] boostStatus in
|
||||
if let self {
|
||||
self.boostStatus = boostStatus
|
||||
}
|
||||
})
|
||||
controller.push(boostsController)
|
||||
} else {
|
||||
let _ = combineLatest(
|
||||
@ -8800,7 +8804,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .settings, forceDark: false, dismissed: nil)
|
||||
self.controller?.push(controller)
|
||||
case .premiumGift:
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings)
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings, completion: nil)
|
||||
self.controller?.push(controller)
|
||||
case .stickers:
|
||||
if let settings = self.data?.globalSettings {
|
||||
@ -10339,7 +10343,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
} else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
|
||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
||||
}
|
||||
if let data = self.data, data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories) {
|
||||
if let data = self.data, !data.isPremiumRequiredForStoryPosting || data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories) {
|
||||
rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0)
|
||||
}
|
||||
|
||||
|
||||
@ -348,50 +348,61 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
switch component.scope {
|
||||
case .saved:
|
||||
let selectedCount = paneNode.selectedItems.count
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.peerId, ids: paneNode.selectedItems, isPinned: false).start()
|
||||
|
||||
paneNode.setIsSelectionModeActive(false)
|
||||
(self.environment?.controller() as? PeerInfoStoryGridScreen)?.updateTitle()
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||
|
||||
let title: String = presentationData.strings.StoryList_TooltipStoriesSavedToProfile(Int32(selectedCount))
|
||||
environment.controller()?.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: nil, text: title, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { _ in return false }
|
||||
), in: .current)
|
||||
case .archive:
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.peerId, ids: paneNode.selectedItems, isPinned: true).start()
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||
|
||||
let title: String
|
||||
let text: String
|
||||
|
||||
if component.peerId == component.context.account.peerId {
|
||||
title = presentationData.strings.StoryList_TooltipStoriesSavedToProfile(Int32(paneNode.selectedIds.count))
|
||||
text = presentationData.strings.StoryList_TooltipStoriesSavedToProfileText
|
||||
} else {
|
||||
title = presentationData.strings.StoryList_TooltipStoriesSavedToChannel(Int32(paneNode.selectedIds.count))
|
||||
text = presentationData.strings.Story_ToastSavedToChannelText
|
||||
let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
var isGroup = false
|
||||
if case let .channel(channel) = peer, case .group = channel.info {
|
||||
isGroup = true
|
||||
}
|
||||
|
||||
environment.controller()?.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: title, text: text, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { _ in return false }
|
||||
), in: .current)
|
||||
|
||||
paneNode.clearSelection()
|
||||
}
|
||||
switch component.scope {
|
||||
case .saved:
|
||||
let selectedCount = paneNode.selectedItems.count
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.peerId, ids: paneNode.selectedItems, isPinned: false).start()
|
||||
|
||||
paneNode.setIsSelectionModeActive(false)
|
||||
(self.environment?.controller() as? PeerInfoStoryGridScreen)?.updateTitle()
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||
|
||||
let title: String = presentationData.strings.StoryList_TooltipStoriesSavedToProfile(Int32(selectedCount))
|
||||
environment.controller()?.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: nil, text: title, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { _ in return false }
|
||||
), in: .current)
|
||||
case .archive:
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.peerId, ids: paneNode.selectedItems, isPinned: true).start()
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||
|
||||
let title: String
|
||||
let text: String
|
||||
|
||||
if component.peerId == component.context.account.peerId {
|
||||
title = presentationData.strings.StoryList_TooltipStoriesSavedToProfile(Int32(paneNode.selectedIds.count))
|
||||
text = presentationData.strings.StoryList_TooltipStoriesSavedToProfileText
|
||||
} else {
|
||||
title = isGroup ? presentationData.strings.StoryList_TooltipStoriesSavedToGroup(Int32(paneNode.selectedIds.count)) : presentationData.strings.StoryList_TooltipStoriesSavedToChannel(Int32(paneNode.selectedIds.count))
|
||||
text = isGroup ? presentationData.strings.Story_ToastSavedToGroupText : presentationData.strings.Story_ToastSavedToChannelText
|
||||
}
|
||||
|
||||
environment.controller()?.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: title, text: text, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
action: { _ in return false }
|
||||
), in: .current)
|
||||
|
||||
paneNode.clearSelection()
|
||||
}
|
||||
})
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
|
||||
@ -1083,7 +1083,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
||||
}
|
||||
|
||||
self.backButton.updateContentsColor(backgroundColor: scrolledUp ? UIColor(white: 0.0, alpha: 0.1) : .clear, contentsColor: scrolledUp ? .white : environment.theme.rootController.navigationBar.accentTextColor, canBeExpanded: !scrolledUp, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
self.backButton.frame = CGRect(origin: CGPoint(x: 16.0, y: 54.0), size: backSize)
|
||||
self.backButton.frame = CGRect(origin: CGPoint(x: environment.safeInsets.left + 16.0, y: environment.navigationHeight - 44.0), size: backSize)
|
||||
if self.backButton.view.superview == nil {
|
||||
if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar {
|
||||
navigationBar.view.addSubview(self.backButton.view)
|
||||
|
||||
@ -19,15 +19,17 @@ final class StoryAuthorInfoComponent: Component {
|
||||
let strings: PresentationStrings
|
||||
let peer: EnginePeer?
|
||||
let forwardInfo: EngineStoryItem.ForwardInfo?
|
||||
let author: EnginePeer?
|
||||
let timestamp: Int32
|
||||
let counters: Counters?
|
||||
let isEdited: Bool
|
||||
|
||||
init(context: AccountContext, strings: PresentationStrings, peer: EnginePeer?, forwardInfo: EngineStoryItem.ForwardInfo?, timestamp: Int32, counters: Counters?, isEdited: Bool) {
|
||||
init(context: AccountContext, strings: PresentationStrings, peer: EnginePeer?, forwardInfo: EngineStoryItem.ForwardInfo?, author: EnginePeer?, timestamp: Int32, counters: Counters?, isEdited: Bool) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.peer = peer
|
||||
self.forwardInfo = forwardInfo
|
||||
self.author = author
|
||||
self.timestamp = timestamp
|
||||
self.counters = counters
|
||||
self.isEdited = isEdited
|
||||
@ -46,6 +48,9 @@ final class StoryAuthorInfoComponent: Component {
|
||||
if lhs.forwardInfo != rhs.forwardInfo {
|
||||
return false
|
||||
}
|
||||
if lhs.author != rhs.author {
|
||||
return false
|
||||
}
|
||||
if lhs.timestamp != rhs.timestamp {
|
||||
return false
|
||||
}
|
||||
@ -121,6 +126,16 @@ final class StoryAuthorInfoComponent: Component {
|
||||
}
|
||||
subtitle = combinedString
|
||||
subtitleTruncationType = .middle
|
||||
} else if let author = component.author {
|
||||
let authorName = author.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let timeString = stringForStoryActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, preciseTime: true, relativeTimestamp: component.timestamp, relativeTo: timestamp, short: true)
|
||||
let combinedString = NSMutableAttributedString()
|
||||
combinedString.append(NSAttributedString(string: authorName, font: Font.medium(11.0), textColor: titleColor))
|
||||
if timeString.count < 6 {
|
||||
combinedString.append(NSAttributedString(string: " • \(timeString)", font: Font.regular(11.0), textColor: subtitleColor))
|
||||
}
|
||||
subtitle = combinedString
|
||||
subtitleTruncationType = .middle
|
||||
} else {
|
||||
var subtitleString = stringForStoryActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, preciseTime: true, relativeTimestamp: component.timestamp, relativeTo: timestamp)
|
||||
if component.isEdited {
|
||||
@ -176,7 +191,16 @@ final class StoryAuthorInfoComponent: Component {
|
||||
self.repostIconView = nil
|
||||
repostIconView.removeFromSuperview()
|
||||
}
|
||||
|
||||
|
||||
var authorPeer: EnginePeer?
|
||||
if let forwardInfo = component.forwardInfo, case let .known(peer, _, _) = forwardInfo {
|
||||
authorPeer = peer
|
||||
} else if let author = component.author {
|
||||
authorPeer = author
|
||||
}
|
||||
|
||||
if let peer = authorPeer {
|
||||
let avatarNode: AvatarNode
|
||||
if let current = self.avatarNode {
|
||||
avatarNode = current
|
||||
|
||||
@ -114,6 +114,11 @@ public final class StoryContentContextImpl: StoryContentContext {
|
||||
forwardInfoStories.updateValue(nil, forKey: storyId)
|
||||
}
|
||||
}
|
||||
if let peerId = itemValue.authorId {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
for entity in itemValue.entities {
|
||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
||||
@ -296,7 +301,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: forwardInfo
|
||||
forwardInfo: forwardInfo,
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
)
|
||||
}
|
||||
var totalCount = peerStoryItemsView.items.count
|
||||
@ -332,7 +338,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
||||
isEdited: false,
|
||||
isMy: true,
|
||||
myReaction: nil,
|
||||
forwardInfo: pendingForwardsInfo[item.randomId]
|
||||
forwardInfo: pendingForwardsInfo[item.randomId],
|
||||
author: nil
|
||||
))
|
||||
totalCount += 1
|
||||
}
|
||||
@ -1187,6 +1194,11 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
||||
stories.updateValue(nil, forKey: storyId)
|
||||
}
|
||||
}
|
||||
if let peerId = item.authorId {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
for entity in item.entities {
|
||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
||||
@ -1312,7 +1324,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
||||
isEdited: itemValue.isEdited,
|
||||
isMy: itemValue.isMy,
|
||||
myReaction: itemValue.myReaction,
|
||||
forwardInfo: forwardInfo
|
||||
forwardInfo: forwardInfo,
|
||||
author: itemValue.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
)
|
||||
|
||||
let mainItem = StoryContentItem(
|
||||
@ -2191,7 +2204,8 @@ private func getCachedStory(storyId: StoryId, transaction: Transaction) -> Engin
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
)
|
||||
} else {
|
||||
return nil
|
||||
|
||||
@ -1660,11 +1660,18 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var canShare = true
|
||||
var displayFooter = false
|
||||
if case let .channel(channel) = component.slice.peer {
|
||||
displayFooter = true
|
||||
isChannel = true
|
||||
if channel.addressName == nil {
|
||||
canShare = false
|
||||
}
|
||||
switch channel.info {
|
||||
case .broadcast:
|
||||
displayFooter = true
|
||||
case .group:
|
||||
if channel.flags.contains(.isCreator) || channel.hasPermission(.postStories) {
|
||||
displayFooter = true
|
||||
}
|
||||
}
|
||||
} else if component.slice.peer.id == component.context.account.peerId {
|
||||
displayFooter = true
|
||||
} else if component.slice.item.storyItem.isPending {
|
||||
@ -2752,6 +2759,24 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
disabledPlaceholder = .text(component.strings.Story_FooterReplyUnavailable)
|
||||
}
|
||||
|
||||
var isChannel = false
|
||||
var isGroup = false
|
||||
var showMessageInputPanel = true
|
||||
if case let .channel(channel) = component.slice.peer {
|
||||
switch channel.info {
|
||||
case .broadcast:
|
||||
isChannel = true
|
||||
showMessageInputPanel = false
|
||||
case .group:
|
||||
isGroup = true
|
||||
if channel.flags.contains(.isCreator) || channel.hasPermission(.postStories) {
|
||||
showMessageInputPanel = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showMessageInputPanel = component.slice.peer.id != component.context.account.peerId
|
||||
}
|
||||
|
||||
let inputPlaceholder: MessageInputPanelComponent.Placeholder
|
||||
if let stealthModeTimeout = component.stealthModeTimeout {
|
||||
let minutes = Int(stealthModeTimeout / 60)
|
||||
@ -2787,7 +2812,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
inputPlaceholder = .counter(items)
|
||||
} else {
|
||||
inputPlaceholder = .plain(component.strings.Story_InputPlaceholderReplyPrivately)
|
||||
inputPlaceholder = .plain(isGroup ? component.strings.Story_InputPlaceholderReplyInGroup : component.strings.Story_InputPlaceholderReplyPrivately)
|
||||
}
|
||||
|
||||
let startTime22 = CFAbsoluteTimeGetCurrent()
|
||||
@ -2811,13 +2836,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var inputPanelSize: CGSize?
|
||||
|
||||
let startTime23 = CFAbsoluteTimeGetCurrent()
|
||||
|
||||
var isChannel = false
|
||||
if case .channel = component.slice.peer {
|
||||
isChannel = true
|
||||
}
|
||||
|
||||
if component.slice.peer.id != component.context.account.peerId && !isChannel {
|
||||
|
||||
if showMessageInputPanel {
|
||||
var haveLikeOptions = false
|
||||
if case .user = component.slice.peer {
|
||||
haveLikeOptions = true
|
||||
@ -3998,6 +4018,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
strings: component.strings,
|
||||
peer: component.slice.peer,
|
||||
forwardInfo: component.slice.item.storyItem.forwardInfo,
|
||||
author: component.slice.item.storyItem.author,
|
||||
timestamp: component.slice.item.storyItem.timestamp,
|
||||
counters: counters,
|
||||
isEdited: component.slice.item.storyItem.isEdited
|
||||
@ -4031,6 +4052,12 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
} else {
|
||||
self.navigateToPeer(peer: peer, chat: false)
|
||||
}
|
||||
} else if let author = component.slice.item.storyItem.author {
|
||||
if author.id == component.context.account.peerId {
|
||||
self.navigateToMyStories()
|
||||
} else {
|
||||
self.navigateToPeer(peer: author, chat: false)
|
||||
}
|
||||
} else {
|
||||
if component.slice.peer.id == component.context.account.peerId {
|
||||
self.navigateToMyStories()
|
||||
@ -4381,7 +4408,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme),
|
||||
items: reactionItems.map(ReactionContextItem.reaction),
|
||||
selectedItems: component.slice.item.storyItem.myReaction.flatMap { Set([$0]) } ?? Set(),
|
||||
title: self.displayLikeReactions ? nil : component.strings.Story_SendReactionAsMessage,
|
||||
title: self.displayLikeReactions ? nil : (isGroup ? component.strings.Story_SendReactionAsGroupMessage : component.strings.Story_SendReactionAsMessage),
|
||||
reactionsLocked: false,
|
||||
alwaysAllowPremiumReactions: false,
|
||||
allPresetReactionsAreAvailable: false,
|
||||
@ -5356,6 +5383,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
let externalState = MediaEditorTransitionOutExternalState(
|
||||
storyTarget: nil,
|
||||
isForcedTarget: false,
|
||||
isPeerArchived: false,
|
||||
transitionOut: nil
|
||||
)
|
||||
@ -6394,11 +6422,16 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.slice.peer.id, ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).startStandalone()
|
||||
|
||||
var isGroup = false
|
||||
if case let .channel(channel) = component.slice.peer, case .group = channel.info {
|
||||
isGroup = true
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
if component.slice.item.storyItem.isPinned {
|
||||
self.scheduledStoryUnpinnedUndoOverlay = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: nil, text: presentationData.strings.Story_ToastRemovedFromChannelText, timeout: nil, customUndoText: nil),
|
||||
content: .info(title: nil, text: isGroup ? presentationData.strings.Story_ToastRemovedFromGroupText : presentationData.strings.Story_ToastRemovedFromChannelText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
@ -6407,7 +6440,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
} else {
|
||||
self.component?.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: presentationData.strings.Story_ToastSavedToChannelTitle, text: presentationData.strings.Story_ToastSavedToChannelText, timeout: nil, customUndoText: nil),
|
||||
content: .info(title: isGroup ? presentationData.strings.Story_ToastSavedToGroupTitle : presentationData.strings.Story_ToastSavedToChannelTitle, text: isGroup ? presentationData.strings.Story_ToastSavedToGroupText : presentationData.strings.Story_ToastSavedToChannelText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
|
||||
@ -1443,9 +1443,6 @@ public class VideoMessageCameraScreen: ViewController {
|
||||
|
||||
deinit {
|
||||
self.audioSessionDisposable?.dispose()
|
||||
if #available(iOS 13.0, *) {
|
||||
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(false)
|
||||
}
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
@ -1688,9 +1685,6 @@ public class VideoMessageCameraScreen: ViewController {
|
||||
}
|
||||
|
||||
self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: audioSessionType, activate: { [weak self] _ in
|
||||
if #available(iOS 13.0, *) {
|
||||
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true)
|
||||
}
|
||||
if let self {
|
||||
Queue.mainQueue().async {
|
||||
self.node.setupCamera()
|
||||
|
||||
@ -450,6 +450,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var mediaRestrictedTooltipControllerMode = true
|
||||
weak var checksTooltipController: TooltipController?
|
||||
weak var copyProtectionTooltipController: TooltipController?
|
||||
weak var emojiPackTooltipController: TooltipScreen?
|
||||
|
||||
var currentMessageTooltipScreens: [(TooltipScreen, ListViewItemNode)] = []
|
||||
|
||||
@ -965,14 +966,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return true
|
||||
}
|
||||
if let peer = peer as? TelegramChannel, peer.hasPermission(.changeInfo) {
|
||||
let _ = (context.engine.peers.getChannelBoostStatus(peerId: peer.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] boostStatus in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peer.id, boostStatus: boostStatus))
|
||||
})
|
||||
if let peer = peer as? TelegramChannel {
|
||||
if peer.flags.contains(.isCreator) || peer.adminRights?.rights.contains(.canChangeInfo) == true {
|
||||
let _ = (context.engine.peers.getChannelBoostStatus(peerId: peer.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] boostStatus in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peer.id, boostStatus: boostStatus))
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
guard message.effectivelyIncoming(strongSelf.context.account.peerId), let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||
@ -15714,42 +15717,53 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let rect = self.chatDisplayNode.frameForEmojiButton(), self.effectiveNavigationController?.topViewController === self else {
|
||||
return
|
||||
}
|
||||
guard let emojiPack = (self.peerView?.cachedData as? CachedChannelData)?.emojiPack, let thumbnailFileId = emojiPack.thumbnailFileId else {
|
||||
guard let peerId = self.chatLocation.peerId, let emojiPack = (self.peerView?.cachedData as? CachedChannelData)?.emojiPack, let thumbnailFileId = emojiPack.thumbnailFileId else {
|
||||
return
|
||||
}
|
||||
let _ = (self.context.engine.stickers.resolveInlineStickers(fileIds: [thumbnailFileId])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] files in
|
||||
guard let self, let emojiFile = files.values.first else {
|
||||
|
||||
let _ = (ApplicationSpecificNotice.groupEmojiPackSuggestion(accountManager: self.context.sharedContext.accountManager, peerId: peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] counter in
|
||||
guard let self, counter == 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
||||
let boldTextFont = Font.bold(self.presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
||||
let textColor = UIColor.white
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: textColor), linkAttribute: { _ in
|
||||
return nil
|
||||
})
|
||||
|
||||
let text = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(self.presentationData.strings.Chat_GroupEmojiTooltip(emojiPack.title).string, attributes: markdownAttributes))
|
||||
|
||||
let range = (text.string as NSString).range(of: "#")
|
||||
if range.location != NSNotFound {
|
||||
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: emojiFile.fileId.id, file: emojiFile), range: range)
|
||||
}
|
||||
|
||||
let tooltipScreen = TooltipScreen(
|
||||
context: self.context,
|
||||
account: self.context.account,
|
||||
sharedContext: self.context.sharedContext,
|
||||
text: .attributedString(text: text),
|
||||
location: .point(rect.offsetBy(dx: 0.0, dy: -3.0), .bottom),
|
||||
displayDuration: .default,
|
||||
cornerRadius: 10.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
let _ = (self.context.engine.stickers.resolveInlineStickers(fileIds: [thumbnailFileId])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] files in
|
||||
guard let self, let emojiFile = files.values.first else {
|
||||
return
|
||||
}
|
||||
)
|
||||
self.present(tooltipScreen, in: .current)
|
||||
|
||||
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
||||
let boldTextFont = Font.bold(self.presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
||||
let textColor = UIColor.white
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: textColor), linkAttribute: { _ in
|
||||
return nil
|
||||
})
|
||||
|
||||
let text = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(self.presentationData.strings.Chat_GroupEmojiTooltip(emojiPack.title).string, attributes: markdownAttributes))
|
||||
|
||||
let range = (text.string as NSString).range(of: "#")
|
||||
if range.location != NSNotFound {
|
||||
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: emojiFile.fileId.id, file: emojiFile), range: range)
|
||||
}
|
||||
|
||||
let tooltipScreen = TooltipScreen(
|
||||
context: self.context,
|
||||
account: self.context.account,
|
||||
sharedContext: self.context.sharedContext,
|
||||
text: .attributedString(text: text),
|
||||
location: .point(rect.offsetBy(dx: 0.0, dy: -3.0), .bottom),
|
||||
displayDuration: .default,
|
||||
cornerRadius: 10.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
self.present(tooltipScreen, in: .current)
|
||||
self.emojiPackTooltipController = tooltipScreen
|
||||
|
||||
let _ = ApplicationSpecificNotice.incrementGroupEmojiPackSuggestion(accountManager: self.context.sharedContext.accountManager, peerId: peerId).startStandalone()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -16694,6 +16708,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let externalState = MediaEditorTransitionOutExternalState(
|
||||
storyTarget: nil,
|
||||
isForcedTarget: false,
|
||||
isPeerArchived: false,
|
||||
transitionOut: nil
|
||||
)
|
||||
|
||||
@ -3705,6 +3705,17 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
strongSelf.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
|
||||
return (.media(mode: .other, expanded: nil, focused: false), state.interfaceState.messageActionsState.closedButtonKeyboardMessageId)
|
||||
})
|
||||
|
||||
if let emojiPackTooltipController = strongSelf.controller?.emojiPackTooltipController {
|
||||
strongSelf.controller?.emojiPackTooltipController = nil
|
||||
emojiPackTooltipController.dismiss()
|
||||
|
||||
Queue.mainQueue().after(0.1) {
|
||||
if let inputNode = strongSelf.inputNode as? ChatEntityKeyboardInputNode {
|
||||
inputNode.scrollToGroupEmoji()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1070,7 +1070,25 @@ extension ChatControllerImpl {
|
||||
if case .scheduledMessages = self.presentationInterfaceState.subject {
|
||||
isScheduledMessages = true
|
||||
}
|
||||
let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), threadTitle: self.threadInfo?.title, chatLocation: self.chatLocation, isScheduledMessages: isScheduledMessages, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, subject: subject, saveEditedPhotos: saveEditedPhotos)
|
||||
let controller = MediaPickerScreen(
|
||||
context: self.context,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
peer: EnginePeer(peer),
|
||||
threadTitle: self.threadInfo?.title,
|
||||
chatLocation: self.chatLocation,
|
||||
isScheduledMessages: isScheduledMessages,
|
||||
bannedSendPhotos: bannedSendPhotos,
|
||||
bannedSendVideos: bannedSendVideos,
|
||||
canBoostToUnrestrict: (self.presentationInterfaceState.boostsToUnrestrict ?? 0) > 0 && bannedSendPhotos?.1 != true && bannedSendVideos?.1 != true,
|
||||
subject: subject,
|
||||
saveEditedPhotos: saveEditedPhotos
|
||||
)
|
||||
controller.openBoost = { [weak self, weak controller] in
|
||||
if let self {
|
||||
controller?.dismiss()
|
||||
self.interfaceInteraction?.openBoostToUnrestrict()
|
||||
}
|
||||
}
|
||||
let mediaPickerContext = controller.mediaPickerContext
|
||||
controller.openCamera = { [weak self] cameraView in
|
||||
self?.openCamera(cameraView: cameraView)
|
||||
|
||||
@ -587,7 +587,7 @@ func openResolvedUrlImpl(
|
||||
}
|
||||
case let .premiumMultiGift(reference):
|
||||
dismissInput()
|
||||
let controller = context.sharedContext.makePremiumGiftController(context: context, source: .deeplink(reference))
|
||||
let controller = context.sharedContext.makePremiumGiftController(context: context, source: .deeplink(reference), completion: nil)
|
||||
if let navigationController = navigationController {
|
||||
navigationController.pushViewController(controller, animated: true)
|
||||
}
|
||||
|
||||
@ -2049,7 +2049,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return PremiumLimitScreen(context: context, subject: mappedSubject, count: count, forceDark: forceDark, cancel: cancel, action: action)
|
||||
}
|
||||
|
||||
public func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource) -> ViewController {
|
||||
public func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, completion: (() -> Void)?) -> ViewController {
|
||||
let options = Promise<[PremiumGiftCodeOption]>()
|
||||
options.set(context.engine.payments.premiumGiftCodeOptions(peerId: nil))
|
||||
|
||||
@ -2101,6 +2101,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
pushImpl?(c)
|
||||
}, completion: {
|
||||
filterImpl?()
|
||||
completion?()
|
||||
})
|
||||
pushImpl = { [weak giftController] c in
|
||||
giftController?.push(c)
|
||||
@ -2108,7 +2109,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
filterImpl = { [weak giftController] in
|
||||
if let navigationController = giftController?.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { !($0 is ContactMultiselectionController) }
|
||||
controllers = controllers.filter { !($0 is ContactMultiselectionController) && !($0 is PremiumGiftScreen) }
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,6 +277,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
|
||||
let externalState = MediaEditorTransitionOutExternalState(
|
||||
storyTarget: nil,
|
||||
isForcedTarget: customTarget != nil,
|
||||
isPeerArchived: false,
|
||||
transitionOut: nil
|
||||
)
|
||||
@ -522,13 +523,17 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
var viewControllers = self.viewControllers
|
||||
|
||||
let archiveController = ChatListControllerImpl(context: context, location: .chatList(groupId: .archive), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
|
||||
externalState.transitionOut = archiveController.storyCameraTransitionOut()
|
||||
if !externalState.isForcedTarget {
|
||||
externalState.transitionOut = archiveController.storyCameraTransitionOut()
|
||||
}
|
||||
chatListController = archiveController
|
||||
viewControllers.insert(archiveController, at: 1)
|
||||
self.setViewControllers(viewControllers, animated: false)
|
||||
} else {
|
||||
chatListController = self.chatListController as? ChatListControllerImpl
|
||||
externalState.transitionOut = chatListController?.storyCameraTransitionOut()
|
||||
if !externalState.isForcedTarget {
|
||||
externalState.transitionOut = chatListController?.storyCameraTransitionOut()
|
||||
}
|
||||
}
|
||||
|
||||
if let chatListController {
|
||||
|
||||
@ -291,6 +291,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
|
||||
private var paymentDisposable: Disposable?
|
||||
|
||||
private var lastExpansionTimestamp: Double?
|
||||
|
||||
private var didTransitionIn = false
|
||||
private var dismissed = false
|
||||
|
||||
@ -739,7 +741,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
guard let eventName = body["eventName"] as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
let currentTimestamp = CACurrentMediaTime()
|
||||
let eventData = (body["eventData"] as? String)?.data(using: .utf8)
|
||||
let json = try? JSONSerialization.jsonObject(with: eventData ?? Data(), options: []) as? [String: Any]
|
||||
|
||||
@ -804,7 +806,12 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
case "web_app_request_theme":
|
||||
self.sendThemeChangedEvent()
|
||||
case "web_app_expand":
|
||||
controller.requestAttachmentMenuExpansion()
|
||||
if let lastExpansionTimestamp = self.lastExpansionTimestamp, currentTimestamp < lastExpansionTimestamp + 1.0 {
|
||||
|
||||
} else {
|
||||
self.lastExpansionTimestamp = currentTimestamp
|
||||
controller.requestAttachmentMenuExpansion()
|
||||
}
|
||||
case "web_app_close":
|
||||
controller.dismiss()
|
||||
case "web_app_open_tg_link":
|
||||
@ -846,7 +853,6 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
case "web_app_open_link":
|
||||
if let json = json, let url = json["url"] as? String {
|
||||
let tryInstantView = json["try_instant_view"] as? Bool ?? false
|
||||
let currentTimestamp = CACurrentMediaTime()
|
||||
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
|
||||
self.webView?.lastTouchTimestamp = nil
|
||||
if tryInstantView {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user