mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-01 12:17:53 +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.MaxLevelReached.Text" = "**%1$@** reached **Level %2$@**.";
|
||||||
|
|
||||||
"ChannelBoost.MoreBoostsNeeded.Boosted.Text" = "%@ needed to unlock new features.";
|
"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.Delete" = "Delete Contact";
|
||||||
"ContactList.Context.Select" = "Select";
|
"ContactList.Context.Select" = "Select";
|
||||||
@ -11013,7 +11015,7 @@ Sorry for the inconvenience.";
|
|||||||
"GroupBoost.EnableStoriesText" = "Your group needs %1$@ to enable posting stories.";
|
"GroupBoost.EnableStoriesText" = "Your group needs %1$@ to enable posting stories.";
|
||||||
"GroupBoost.IncreaseLimitText" = "Your group needs %1$@ to post %2$@.";
|
"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.AllMembers" = "All members";
|
||||||
"BoostGift.Group.OnlyNewMembers" = "Only new 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_1" = "**%@** other listed channel and group";
|
||||||
"Chat.Giveaway.Info.OtherChannelsAndGroups_any" = "**%@** other listed channels and groups";
|
"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 class MediaEditorTransitionOutExternalState {
|
||||||
public var storyTarget: Stories.PendingTarget?
|
public var storyTarget: Stories.PendingTarget?
|
||||||
|
public var isForcedTarget: Bool
|
||||||
public var isPeerArchived: Bool
|
public var isPeerArchived: Bool
|
||||||
public var transitionOut: ((Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?)?
|
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.storyTarget = storyTarget
|
||||||
|
self.isForcedTarget = isForcedTarget
|
||||||
self.isPeerArchived = isPeerArchived
|
self.isPeerArchived = isPeerArchived
|
||||||
self.transitionOut = transitionOut
|
self.transitionOut = transitionOut
|
||||||
}
|
}
|
||||||
@ -959,7 +961,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController
|
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController
|
||||||
func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> 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 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 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
|
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)
|
self.metrics = Camera.Metrics(model: DeviceModel.current)
|
||||||
|
|
||||||
let session = CameraSession()
|
let session = CameraSession()
|
||||||
session.session.usesApplicationAudioSession = true
|
|
||||||
session.session.automaticallyConfiguresApplicationAudioSession = false
|
session.session.automaticallyConfiguresApplicationAudioSession = false
|
||||||
session.session.automaticallyConfiguresCaptureDeviceForWideColor = false
|
session.session.automaticallyConfiguresCaptureDeviceForWideColor = false
|
||||||
|
session.session.usesApplicationAudioSession = true
|
||||||
if let previewView {
|
if let previewView {
|
||||||
previewView.setSession(session.session, autoConnect: !session.hasMultiCam)
|
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 {
|
if let forwardInfo = message.forwardInfo, let author = forwardInfo.author {
|
||||||
messageText = strings.Message_GiveawayStartedOther(EnginePeer(author).compactDisplayTitle).string
|
messageText = strings.Message_GiveawayStartedOther(EnginePeer(author).compactDisplayTitle).string
|
||||||
} else {
|
} 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:
|
case let results as TelegramMediaGiveawayResults:
|
||||||
if results.winnersCount == 0 {
|
if results.winnersCount == 0 {
|
||||||
|
|||||||
@ -1683,7 +1683,7 @@ public final class ChatListNode: ListView {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
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)
|
self.push?(controller)
|
||||||
}, openActiveSessions: { [weak self] in
|
}, openActiveSessions: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
|
|||||||
@ -26,6 +26,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
|||||||
private var cameraTextNode: ImmediateTextNode
|
private var cameraTextNode: ImmediateTextNode
|
||||||
private var cameraIconNode: ASImageNode
|
private var cameraIconNode: ASImageNode
|
||||||
|
|
||||||
|
var boostPressed: () -> Void = {}
|
||||||
var settingsPressed: () -> Void = {}
|
var settingsPressed: () -> Void = {}
|
||||||
var cameraPressed: () -> Void = {}
|
var cameraPressed: () -> Void = {}
|
||||||
|
|
||||||
@ -76,7 +77,13 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.animationNode)
|
self.addSubnode(self.animationNode)
|
||||||
self.addSubnode(self.textNode)
|
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.titleNode)
|
||||||
self.addSubnode(self.buttonNode)
|
self.addSubnode(self.buttonNode)
|
||||||
|
|
||||||
@ -104,6 +111,8 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
|||||||
self.buttonNode.pressed = { [weak self] in
|
self.buttonNode.pressed = { [weak self] in
|
||||||
self?.settingsPressed()
|
self?.settingsPressed()
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +137,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
|||||||
|
|
||||||
let imageSpacing: CGFloat = 12.0
|
let imageSpacing: CGFloat = 12.0
|
||||||
let textSpacing: CGFloat = 12.0
|
let textSpacing: CGFloat = 12.0
|
||||||
let buttonSpacing: CGFloat = 15.0
|
let buttonSpacing: CGFloat = 20.0
|
||||||
let cameraSpacing: CGFloat = 13.0
|
let cameraSpacing: CGFloat = 13.0
|
||||||
|
|
||||||
let imageHeight = layout.size.width < layout.size.height ? imageSize.height + imageSpacing : 0.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.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: theme))
|
||||||
self.cameraIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/OpenCamera"), color: theme.list.itemAccentColor)
|
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 buttonWidth: CGFloat = 248.0
|
||||||
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
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 chatLocation: ChatLocation?
|
||||||
private let bannedSendPhotos: (Int32, Bool)?
|
private let bannedSendPhotos: (Int32, Bool)?
|
||||||
private let bannedSendVideos: (Int32, Bool)?
|
private let bannedSendVideos: (Int32, Bool)?
|
||||||
|
private let canBoostToUnrestrict: Bool
|
||||||
private let subject: Subject
|
private let subject: Subject
|
||||||
private let saveEditedPhotos: Bool
|
private let saveEditedPhotos: Bool
|
||||||
|
|
||||||
@ -187,6 +188,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in }
|
public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in }
|
||||||
public var presentWebSearch: (MediaGroupsScreen, Bool) -> Void = { _, _ in }
|
public var presentWebSearch: (MediaGroupsScreen, Bool) -> Void = { _, _ in }
|
||||||
public var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil }
|
public var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil }
|
||||||
|
public var openBoost: () -> Void = { }
|
||||||
|
|
||||||
public var customSelection: ((MediaPickerScreen, Any) -> Void)? = nil
|
public var customSelection: ((MediaPickerScreen, Any) -> Void)? = nil
|
||||||
|
|
||||||
@ -1324,7 +1326,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
if let current = self.placeholderNode {
|
if let current = self.placeholderNode {
|
||||||
placeholderNode = current
|
placeholderNode = current
|
||||||
} else {
|
} 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.containerNode.insertSubnode(placeholderNode, aboveSubnode: self.gridNode)
|
||||||
self.placeholderNode = placeholderNode
|
self.placeholderNode = placeholderNode
|
||||||
|
|
||||||
@ -1525,8 +1530,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
threadTitle: String?,
|
threadTitle: String?,
|
||||||
chatLocation: ChatLocation?,
|
chatLocation: ChatLocation?,
|
||||||
isScheduledMessages: Bool = false,
|
isScheduledMessages: Bool = false,
|
||||||
bannedSendPhotos: (Int32, Bool)?,
|
bannedSendPhotos: (Int32, Bool)? = nil,
|
||||||
bannedSendVideos: (Int32, Bool)?,
|
bannedSendVideos: (Int32, Bool)? = nil,
|
||||||
|
canBoostToUnrestrict: Bool = false,
|
||||||
subject: Subject,
|
subject: Subject,
|
||||||
editingContext: TGMediaEditingContext? = nil,
|
editingContext: TGMediaEditingContext? = nil,
|
||||||
selectionContext: TGMediaSelectionContext? = nil,
|
selectionContext: TGMediaSelectionContext? = nil,
|
||||||
@ -1545,6 +1551,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.isScheduledMessages = isScheduledMessages
|
self.isScheduledMessages = isScheduledMessages
|
||||||
self.bannedSendPhotos = bannedSendPhotos
|
self.bannedSendPhotos = bannedSendPhotos
|
||||||
self.bannedSendVideos = bannedSendVideos
|
self.bannedSendVideos = bannedSendVideos
|
||||||
|
self.canBoostToUnrestrict = canBoostToUnrestrict
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.saveEditedPhotos = saveEditedPhotos
|
self.saveEditedPhotos = saveEditedPhotos
|
||||||
self.mainButtonState = mainButtonState
|
self.mainButtonState = mainButtonState
|
||||||
|
|||||||
@ -1210,7 +1210,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
navigationController.setViewControllers(controllers, animated: true)
|
navigationController.setViewControllers(controllers, animated: true)
|
||||||
|
|
||||||
let title = presentationData.strings.BoostGift_GiveawayCreated_Title
|
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 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)
|
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 {
|
} else {
|
||||||
textString = strings.ChannelBoost_MaxLevelReached_Text(peerName, "\(level)").string
|
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):
|
case let .user(mode):
|
||||||
switch mode {
|
switch mode {
|
||||||
case let .groupPeer(_, peerBoostCount):
|
case let .groupPeer(_, peerBoostCount):
|
||||||
let memberName = state.memberPeer?.compactDisplayTitle ?? ""
|
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 myBoostCount > 0 {
|
||||||
if let remaining {
|
if let remaining, remaining != 0 {
|
||||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
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 {
|
} else {
|
||||||
textString = "**\(memberName)** boosted the group **\(peerBoostCount)** times."
|
textString = memberString
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
isCurrent = true
|
||||||
case let .unrestrict(unrestrictCount):
|
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
|
isCurrent = true
|
||||||
default:
|
default:
|
||||||
if let remaining {
|
if let remaining {
|
||||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
||||||
if myBoostCount > 0 {
|
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 {
|
} else {
|
||||||
textString = strings.ChannelBoost_MoreBoostsNeeded_Text(peerName, boostsString).string
|
textString = strings.ChannelBoost_MoreBoostsNeeded_Text(peerName, boostsString).string
|
||||||
}
|
}
|
||||||
@ -1612,25 +1616,26 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
self.wrappingView.addSubview(self.containerView)
|
self.wrappingView.addSubview(self.containerView)
|
||||||
self.containerView.addSubview(self.contentView)
|
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.containerView.addSubview(self.footerContainerView)
|
||||||
self.footerContainerView.addSubview(self.footerView)
|
self.footerContainerView.addSubview(self.footerView)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let status = controller.status, let myBoostStatus = controller.myBoostStatus {
|
||||||
var myBoostCount: Int32 = 0
|
var myBoostCount: Int32 = 0
|
||||||
var currentMyBoostCount: Int32 = 0
|
var currentMyBoostCount: Int32 = 0
|
||||||
var availableBoosts: [MyBoostStatus.Boost] = []
|
var availableBoosts: [MyBoostStatus.Boost] = []
|
||||||
var occupiedBoosts: [MyBoostStatus.Boost] = []
|
var occupiedBoosts: [MyBoostStatus.Boost] = []
|
||||||
if let myBoostStatus = controller.myBoostStatus {
|
|
||||||
for boost in myBoostStatus.boosts {
|
for boost in myBoostStatus.boosts {
|
||||||
if let boostPeer = boost.peer {
|
if let boostPeer = boost.peer {
|
||||||
if boostPeer.id == controller.peerId {
|
if boostPeer.id == controller.peerId {
|
||||||
myBoostCount += 1
|
myBoostCount += 1
|
||||||
} else {
|
|
||||||
occupiedBoosts.append(boost)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
availableBoosts.append(boost)
|
occupiedBoosts.append(boost)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
availableBoosts.append(boost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1868,23 +1873,10 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
status: controller.status,
|
status: controller.status,
|
||||||
boostState: self.boostState,
|
boostState: self.boostState,
|
||||||
boost: { [weak controller] in
|
boost: { [weak controller] in
|
||||||
guard let controller, let navigationController = controller.navigationController else {
|
guard let controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
controller.node.updateBoostState()
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
copyLink: { [weak self, weak controller] link in
|
copyLink: { [weak self, weak controller] link in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -2217,6 +2209,13 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot])
|
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot])
|
||||||
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
|
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
|
||||||
self?.updatedState.set(context.engine.peers.getChannelBoostStatus(peerId: peerId)
|
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
|
|> map { status in
|
||||||
if let status {
|
if let status {
|
||||||
return InternalBoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1))
|
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)
|
controller?.dismiss(animated: true, completion: nil)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.4) {
|
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)
|
navigationController.pushViewController(giftController, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2432,6 +2431,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
|
|
||||||
private var currentLayout: ContainerViewLayout?
|
private var currentLayout: ContainerViewLayout?
|
||||||
|
|
||||||
|
public var boostStatusUpdated: (ChannelBoostStatus) -> Void = { _ in }
|
||||||
public var disposed: () -> Void = {}
|
public var disposed: () -> Void = {}
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@ -2501,7 +2501,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
|
|
||||||
override open func viewDidDisappear(_ animated: Bool) {
|
override open func viewDidDisappear(_ animated: Bool) {
|
||||||
super.viewDidDisappear(animated)
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
self.node.updateIsVisible(isVisible: false)
|
self.node.updateIsVisible(isVisible: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -239,7 +239,7 @@ public func PremiumBoostScreen(
|
|||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
|
||||||
Queue.mainQueue().after(0.4) {
|
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)
|
pushController(controller)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -252,49 +252,53 @@ public class PremiumLimitDisplayComponent: Component {
|
|||||||
rotationAngle = 0.26
|
rotationAngle = 0.26
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let to: CGFloat = self.badgeView.center.x
|
||||||
|
|
||||||
let positionAnimation = CABasicAnimation(keyPath: "position.x")
|
let positionAnimation = CABasicAnimation(keyPath: "position.x")
|
||||||
positionAnimation.fromValue = NSValue(cgPoint: CGPoint(x: from ?? 0.0, y: 0.0))
|
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.duration = 0.5
|
||||||
positionAnimation.fillMode = .forwards
|
positionAnimation.fillMode = .forwards
|
||||||
positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
positionAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||||
self.badgeView.layer.add(positionAnimation, forKey: "appearance1")
|
self.badgeView.layer.add(positionAnimation, forKey: "appearance1")
|
||||||
|
|
||||||
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
if from != to {
|
||||||
rotateAnimation.fromValue = 0.0 as NSNumber
|
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||||
rotateAnimation.toValue = -rotationAngle as NSNumber
|
rotateAnimation.fromValue = 0.0 as NSNumber
|
||||||
rotateAnimation.duration = 0.15
|
rotateAnimation.toValue = -rotationAngle as NSNumber
|
||||||
rotateAnimation.fillMode = .forwards
|
rotateAnimation.duration = 0.15
|
||||||
rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
rotateAnimation.fillMode = .forwards
|
||||||
rotateAnimation.isRemovedOnCompletion = false
|
rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||||
self.badgeView.layer.add(rotateAnimation, forKey: "appearance2")
|
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 !self.badgeView.isHidden {
|
Queue.mainQueue().after(0.5, {
|
||||||
self.hapticFeedback.impact(.light)
|
let bounceAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||||
}
|
bounceAnimation.fromValue = -rotationAngle as NSNumber
|
||||||
|
bounceAnimation.toValue = 0.04 as NSNumber
|
||||||
Queue.mainQueue().after(0.2) {
|
bounceAnimation.duration = 0.2
|
||||||
let returnAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
bounceAnimation.fillMode = .forwards
|
||||||
returnAnimation.fromValue = 0.04 as NSNumber
|
bounceAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||||
returnAnimation.toValue = 0.0 as NSNumber
|
bounceAnimation.isRemovedOnCompletion = false
|
||||||
returnAnimation.duration = 0.15
|
self.badgeView.layer.add(bounceAnimation, forKey: "appearance3")
|
||||||
returnAnimation.fillMode = .forwards
|
self.badgeView.layer.removeAnimation(forKey: "appearance2")
|
||||||
returnAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)
|
|
||||||
self.badgeView.layer.add(returnAnimation, forKey: "appearance4")
|
if !self.badgeView.isHidden {
|
||||||
self.badgeView.layer.removeAnimation(forKey: "appearance3")
|
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 {
|
if from == nil {
|
||||||
self.badgeView.alpha = 1.0
|
self.badgeView.alpha = 1.0
|
||||||
|
|||||||
@ -880,7 +880,7 @@ public class ReplaceBoostScreen: ViewController {
|
|||||||
}
|
}
|
||||||
let navigationController = self.navigationController
|
let navigationController = self.navigationController
|
||||||
self.dismiss(animated: true, completion: {
|
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)
|
navigationController?.pushViewController(giftController, animated: true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ final class BoostHeaderItem: ItemListControllerHeaderItem {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let status: ChannelBoostStatus
|
let status: ChannelBoostStatus?
|
||||||
let title: String
|
let title: String
|
||||||
let text: String
|
let text: String
|
||||||
let openBoost: () -> Void
|
let openBoost: () -> Void
|
||||||
@ -28,7 +28,7 @@ final class BoostHeaderItem: ItemListControllerHeaderItem {
|
|||||||
let back: () -> Void
|
let back: () -> Void
|
||||||
let updateStatusBar: (StatusBarStyle) -> 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.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -44,7 +44,7 @@ final class BoostHeaderItem: ItemListControllerHeaderItem {
|
|||||||
|
|
||||||
func isEqual(to: ItemListControllerHeaderItem) -> Bool {
|
func isEqual(to: ItemListControllerHeaderItem) -> Bool {
|
||||||
if let item = to as? BoostHeaderItem {
|
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 {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
|||||||
didSet {
|
didSet {
|
||||||
self.updateItem()
|
self.updateItem()
|
||||||
if let layout = self.validLayout {
|
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,
|
strings: self.item.strings,
|
||||||
text: self.item.text,
|
text: self.item.text,
|
||||||
status: self.item.status,
|
status: self.item.status,
|
||||||
|
insets: layout.safeInsets,
|
||||||
openBoost: self.item.openBoost,
|
openBoost: self.item.openBoost,
|
||||||
createGiveaway: self.item.createGiveaway,
|
createGiveaway: self.item.createGiveaway,
|
||||||
openFeatures: self.item.openFeatures
|
openFeatures: self.item.openFeatures
|
||||||
@ -205,7 +206,7 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
|||||||
|
|
||||||
if let hostView = self.hostView {
|
if let hostView = self.hostView {
|
||||||
let size = hostView.update(
|
let size = hostView.update(
|
||||||
transition: .immediate,
|
transition: Transition(transition),
|
||||||
component: component,
|
component: component,
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: containerSize
|
containerSize: containerSize
|
||||||
@ -220,7 +221,7 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
|||||||
self.whiteTitleNode.position = self.titleNode.position
|
self.whiteTitleNode.position = self.titleNode.position
|
||||||
|
|
||||||
let backSize = self.backButton.update(key: .back, presentationData: self.item.context.sharedContext.currentPresentationData.with { $0 }, height: 44.0)
|
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.component = component
|
||||||
self.validLayout = layout
|
self.validLayout = layout
|
||||||
@ -244,7 +245,8 @@ final class BoostHeaderItemNode: ItemListControllerHeaderItemNode {
|
|||||||
private final class BoostHeaderComponent: CombinedComponent {
|
private final class BoostHeaderComponent: CombinedComponent {
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let text: String
|
let text: String
|
||||||
let status: ChannelBoostStatus
|
let status: ChannelBoostStatus?
|
||||||
|
let insets: UIEdgeInsets
|
||||||
let openBoost: () -> Void
|
let openBoost: () -> Void
|
||||||
let createGiveaway: () -> Void
|
let createGiveaway: () -> Void
|
||||||
let openFeatures: () -> Void
|
let openFeatures: () -> Void
|
||||||
@ -252,7 +254,8 @@ private final class BoostHeaderComponent: CombinedComponent {
|
|||||||
public init(
|
public init(
|
||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
text: String,
|
text: String,
|
||||||
status: ChannelBoostStatus,
|
status: ChannelBoostStatus?,
|
||||||
|
insets: UIEdgeInsets,
|
||||||
openBoost: @escaping () -> Void,
|
openBoost: @escaping () -> Void,
|
||||||
createGiveaway: @escaping () -> Void,
|
createGiveaway: @escaping () -> Void,
|
||||||
openFeatures: @escaping () -> Void
|
openFeatures: @escaping () -> Void
|
||||||
@ -260,6 +263,7 @@ private final class BoostHeaderComponent: CombinedComponent {
|
|||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.text = text
|
self.text = text
|
||||||
self.status = status
|
self.status = status
|
||||||
|
self.insets = insets
|
||||||
self.openBoost = openBoost
|
self.openBoost = openBoost
|
||||||
self.createGiveaway = createGiveaway
|
self.createGiveaway = createGiveaway
|
||||||
self.openFeatures = openFeatures
|
self.openFeatures = openFeatures
|
||||||
@ -275,6 +279,9 @@ private final class BoostHeaderComponent: CombinedComponent {
|
|||||||
if lhs.status != rhs.status {
|
if lhs.status != rhs.status {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.insets != rhs.insets {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,8 +297,9 @@ private final class BoostHeaderComponent: CombinedComponent {
|
|||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
let size = context.availableSize
|
let size = context.availableSize
|
||||||
let sideInset: CGFloat = 16.0
|
|
||||||
let component = context.component
|
let component = context.component
|
||||||
|
let sideInset: CGFloat = 16.0 + component.insets.left
|
||||||
|
|
||||||
let background = background.update(
|
let background = background.update(
|
||||||
component: PremiumGradientBackgroundComponent(
|
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))
|
.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
|
let position: CGFloat
|
||||||
if let nextLevelBoosts = component.status.nextLevelBoosts {
|
if let status = component.status {
|
||||||
position = CGFloat(component.status.boosts - component.status.currentLevelBoosts) / CGFloat(nextLevelBoosts - component.status.currentLevelBoosts)
|
if let nextLevelBoosts = status.nextLevelBoosts {
|
||||||
|
position = CGFloat(status.boosts - status.currentLevelBoosts) / CGFloat(nextLevelBoosts - status.currentLevelBoosts)
|
||||||
|
} else {
|
||||||
|
position = 1.0
|
||||||
|
}
|
||||||
|
boosts = status.boosts
|
||||||
} else {
|
} else {
|
||||||
position = 1.0
|
boosts = 0
|
||||||
|
position = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let inactiveText = component.strings.ChannelBoost_Level("\(level)").string
|
let inactiveText = component.strings.ChannelBoost_Level("\(level)").string
|
||||||
@ -343,7 +358,7 @@ private final class BoostHeaderComponent: CombinedComponent {
|
|||||||
activeValue: activeText,
|
activeValue: activeText,
|
||||||
activeTitleColor: UIColor(rgb: 0x6f8fff),
|
activeTitleColor: UIColor(rgb: 0x6f8fff),
|
||||||
badgeIconName: "Premium/Boost",
|
badgeIconName: "Premium/Boost",
|
||||||
badgeText: "\(component.status.boosts)",
|
badgeText: "\(boosts)",
|
||||||
badgePosition: position,
|
badgePosition: position,
|
||||||
badgeGraphPosition: position,
|
badgeGraphPosition: position,
|
||||||
invertProgress: true,
|
invertProgress: true,
|
||||||
|
|||||||
@ -1046,7 +1046,7 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
|
|||||||
return entries
|
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 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 stateValue = Atomic(value: ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false))
|
||||||
let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in
|
let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in
|
||||||
@ -1087,12 +1087,16 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
|||||||
})
|
})
|
||||||
dataPromise.set(.single(nil) |> then(dataSignal))
|
dataPromise.set(.single(nil) |> then(dataSignal))
|
||||||
|
|
||||||
let boostData: Signal<ChannelBoostStatus?, NoError>
|
let boostDataPromise = Promise<ChannelBoostStatus?>()
|
||||||
if let boostStatus {
|
boostDataPromise.set(.single(boostStatus) |> then(context.engine.peers.getChannelBoostStatus(peerId: peerId)))
|
||||||
boostData = .single(boostStatus)
|
|
||||||
} else {
|
actionsDisposable.add((boostDataPromise.get()
|
||||||
boostData = .single(nil) |> then(context.engine.peers.getChannelBoostStatus(peerId: peerId))
|
|> deliverOnMainQueue).start(next: { boostStatus in
|
||||||
}
|
if let boostStatus, let boostStatusUpdated {
|
||||||
|
boostStatusUpdated(boostStatus)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
let boostsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: false)
|
let boostsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: false)
|
||||||
let giftsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: true)
|
let giftsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: true)
|
||||||
|
|
||||||
@ -1253,7 +1257,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
|||||||
dataPromise.get(),
|
dataPromise.get(),
|
||||||
messagesPromise.get(),
|
messagesPromise.get(),
|
||||||
storiesPromise.get(),
|
storiesPromise.get(),
|
||||||
boostData,
|
boostDataPromise.get(),
|
||||||
boostsContext.state,
|
boostsContext.state,
|
||||||
giftsContext.state,
|
giftsContext.state,
|
||||||
longLoadingSignal
|
longLoadingSignal
|
||||||
@ -1307,9 +1311,9 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
|||||||
var headerItem: BoostHeaderItem?
|
var headerItem: BoostHeaderItem?
|
||||||
var leftNavigationButton: ItemListNavigationButton?
|
var leftNavigationButton: ItemListNavigationButton?
|
||||||
var boostsOnly = false
|
var boostsOnly = false
|
||||||
if isGroup, section == .boosts, let boostStatus {
|
if isGroup, section == .boosts {
|
||||||
title = .text("")
|
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)
|
openBoostImpl?(false)
|
||||||
}, createGiveaway: {
|
}, createGiveaway: {
|
||||||
arguments.openGifts()
|
arguments.openGifts()
|
||||||
@ -1508,13 +1512,18 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
|||||||
guard let boostStatus, let myBoostStatus else {
|
guard let boostStatus, let myBoostStatus else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
boostDataPromise.set(.single(boostStatus))
|
||||||
|
|
||||||
let boostController = PremiumBoostLevelsScreen(
|
let boostController = PremiumBoostLevelsScreen(
|
||||||
context: context,
|
context: context,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
mode: .user(mode: .current),
|
mode: .owner(subject: nil),
|
||||||
status: boostStatus,
|
status: boostStatus,
|
||||||
myBoostStatus: myBoostStatus
|
myBoostStatus: myBoostStatus
|
||||||
)
|
)
|
||||||
|
boostController.boostStatusUpdated = { boostStatus in
|
||||||
|
boostDataPromise.set(.single(boostStatus))
|
||||||
|
}
|
||||||
controller?.push(boostController)
|
controller?.push(boostController)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -566,7 +566,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1328256121] = { return Api.MessageReactions.parse_messageReactions($0) }
|
dict[1328256121] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||||
dict[-2083123262] = { return Api.MessageReplies.parse_messageReplies($0) }
|
dict[-2083123262] = { return Api.MessageReplies.parse_messageReplies($0) }
|
||||||
dict[-1346631205] = { return Api.MessageReplyHeader.parse_messageReplyHeader($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[1163625789] = { return Api.MessageViews.parse_messageViews($0) }
|
||||||
dict[975236280] = { return Api.MessagesFilter.parse_inputMessagesFilterChatPhotos($0) }
|
dict[975236280] = { return Api.MessagesFilter.parse_inputMessagesFilterChatPhotos($0) }
|
||||||
dict[-530392189] = { return Api.MessagesFilter.parse_inputMessagesFilterContacts($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[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) }
|
||||||
dict[1898850301] = { return Api.StoriesStealthMode.parse_storiesStealthMode($0) }
|
dict[1898850301] = { return Api.StoriesStealthMode.parse_storiesStealthMode($0) }
|
||||||
dict[-1205411504] = { return Api.StoryFwdHeader.parse_storyFwdHeader($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[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) }
|
||||||
dict[-5388013] = { return Api.StoryItem.parse_storyItemSkipped($0) }
|
dict[-5388013] = { return Api.StoryItem.parse_storyItemSkipped($0) }
|
||||||
dict[1620104917] = { return Api.StoryReaction.parse_storyReaction($0) }
|
dict[1620104917] = { return Api.StoryReaction.parse_storyReaction($0) }
|
||||||
|
|||||||
@ -323,7 +323,7 @@ public extension Api {
|
|||||||
public extension Api {
|
public extension Api {
|
||||||
indirect enum MessageReplyHeader: TypeConstructorDescription {
|
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 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) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -345,11 +345,11 @@ public extension Api {
|
|||||||
}}
|
}}
|
||||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .messageReplyStoryHeader(let userId, let storyId):
|
case .messageReplyStoryHeader(let peer, let storyId):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-1667711039)
|
buffer.appendInt32(240843065)
|
||||||
}
|
}
|
||||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
peer.serialize(buffer, true)
|
||||||
serializeInt32(storyId, buffer: buffer, boxed: false)
|
serializeInt32(storyId, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -359,8 +359,8 @@ public extension Api {
|
|||||||
switch self {
|
switch self {
|
||||||
case .messageReplyHeader(let flags, let replyToMsgId, let replyToPeerId, let replyFrom, let replyMedia, let replyToTopId, let quoteText, let quoteEntities, let quoteOffset):
|
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)])
|
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):
|
case .messageReplyStoryHeader(let peer, let storyId):
|
||||||
return ("messageReplyStoryHeader", [("userId", userId as Any), ("storyId", storyId as Any)])
|
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? {
|
public static func parse_messageReplyStoryHeader(_ reader: BufferReader) -> MessageReplyHeader? {
|
||||||
var _1: Int64?
|
var _1: Api.Peer?
|
||||||
_1 = reader.readInt64()
|
if let signature = reader.readInt32() {
|
||||||
|
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||||
|
}
|
||||||
var _2: Int32?
|
var _2: Int32?
|
||||||
_2 = reader.readInt32()
|
_2 = reader.readInt32()
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
if _c1 && _c2 {
|
if _c1 && _c2 {
|
||||||
return Api.MessageReplyHeader.messageReplyStoryHeader(userId: _1!, storyId: _2!)
|
return Api.MessageReplyHeader.messageReplyStoryHeader(peer: _1!, storyId: _2!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -1034,19 +1034,20 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
indirect enum StoryItem: TypeConstructorDescription {
|
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 storyItemDeleted(id: Int32)
|
||||||
case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32)
|
case storyItemSkipped(flags: Int32, id: Int32, date: Int32, expireDate: Int32)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
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 {
|
if boxed {
|
||||||
buffer.appendInt32(-1352440415)
|
buffer.appendInt32(2041735716)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(id, buffer: buffer, boxed: false)
|
serializeInt32(id, buffer: buffer, boxed: false)
|
||||||
serializeInt32(date, 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)}
|
if Int(flags) & Int(1 << 17) != 0 {fwdFrom!.serialize(buffer, true)}
|
||||||
serializeInt32(expireDate, buffer: buffer, boxed: false)
|
serializeInt32(expireDate, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, 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)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
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):
|
||||||
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)])
|
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):
|
case .storyItemDeleted(let id):
|
||||||
return ("storyItemDeleted", [("id", id as Any)])
|
return ("storyItemDeleted", [("id", id as Any)])
|
||||||
case .storyItemSkipped(let flags, let id, let date, let expireDate):
|
case .storyItemSkipped(let flags, let id, let date, let expireDate):
|
||||||
@ -1105,52 +1106,57 @@ public extension Api {
|
|||||||
_2 = reader.readInt32()
|
_2 = reader.readInt32()
|
||||||
var _3: Int32?
|
var _3: Int32?
|
||||||
_3 = reader.readInt32()
|
_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() {
|
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?
|
var _6: Int32?
|
||||||
_5 = reader.readInt32()
|
_6 = reader.readInt32()
|
||||||
var _6: String?
|
var _7: String?
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
|
||||||
var _7: [Api.MessageEntity]?
|
var _8: [Api.MessageEntity]?
|
||||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
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() {
|
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() {
|
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() {
|
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() {
|
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() {
|
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 _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
let _c4 = (Int(_1!) & Int(1 << 17) == 0) || _4 != nil
|
let _c4 = (Int(_1!) & Int(1 << 18) == 0) || _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = (Int(_1!) & Int(1 << 17) == 0) || _5 != nil
|
||||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
let _c6 = _6 != nil
|
||||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
|
||||||
let _c8 = _8 != nil
|
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil
|
||||||
let _c9 = (Int(_1!) & Int(1 << 14) == 0) || _9 != nil
|
let _c9 = _9 != nil
|
||||||
let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil
|
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
||||||
let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil
|
let _c11 = (Int(_1!) & Int(1 << 2) == 0) || _11 != nil
|
||||||
let _c12 = (Int(_1!) & Int(1 << 15) == 0) || _12 != nil
|
let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
|
||||||
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)
|
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 {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -200,8 +200,8 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
|||||||
|
|
||||||
if let replyTo = replyTo {
|
if let replyTo = replyTo {
|
||||||
switch replyTo {
|
switch replyTo {
|
||||||
case let .messageReplyStoryHeader(userId, _):
|
case let .messageReplyStoryHeader(peer, _):
|
||||||
let storyPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
let storyPeerId = peer.peerId
|
||||||
if !result.contains(storyPeerId) {
|
if !result.contains(storyPeerId) {
|
||||||
result.append(storyPeerId)
|
result.append(storyPeerId)
|
||||||
}
|
}
|
||||||
@ -668,8 +668,8 @@ extension StoreMessage {
|
|||||||
if let replyHeader = replyHeader {
|
if let replyHeader = replyHeader {
|
||||||
attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote))
|
attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote))
|
||||||
}
|
}
|
||||||
case let .messageReplyStoryHeader(userId, storyId):
|
case let .messageReplyStoryHeader(peer, storyId):
|
||||||
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), id: storyId)))
|
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: peer.peerId, id: storyId)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,8 +952,8 @@ extension StoreMessage {
|
|||||||
} else if let replyHeader = replyHeader {
|
} else if let replyHeader = replyHeader {
|
||||||
attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote))
|
attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote))
|
||||||
}
|
}
|
||||||
case let .messageReplyStoryHeader(userId, storyId):
|
case let .messageReplyStoryHeader(peer, storyId):
|
||||||
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), id: storyId)))
|
attributes.append(ReplyStoryAttribute(storyId: StoryId(peerId: peer.peerId, id: storyId)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch action {
|
switch action {
|
||||||
|
|||||||
@ -4712,7 +4712,8 @@ func replayFinalState(
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: updatedReaction,
|
myReaction: updatedReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
updatedPeerEntries[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: item.expirationTimestamp, isCloseFriends: item.isCloseFriends)
|
updatedPeerEntries[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: item.expirationTimestamp, isCloseFriends: item.isCloseFriends)
|
||||||
@ -4745,7 +4746,8 @@ func replayFinalState(
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: MessageReaction.Reaction(apiReaction: reaction),
|
myReaction: MessageReaction.Reaction(apiReaction: reaction),
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
transaction.setStory(id: StoryId(peerId: peerId, id: id), value: entry)
|
transaction.setStory(id: StoryId(peerId: peerId, id: id), value: entry)
|
||||||
|
|||||||
@ -349,7 +349,8 @@ private final class StoryStatsPublicForwardsContextImpl {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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))
|
resultForwards.append(.story(EnginePeer(peer), mappedItem))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -556,7 +556,8 @@ public final class EngineStoryViewListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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)
|
storyStats: transaction.getPeerStoryStats(peerId: peer.id)
|
||||||
)))
|
)))
|
||||||
@ -595,7 +596,8 @@ public final class EngineStoryViewListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry)
|
transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry)
|
||||||
@ -634,7 +636,8 @@ public final class EngineStoryViewListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
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,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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)
|
storyStats: transaction.getPeerStoryStats(peerId: peer.id)
|
||||||
)))
|
)))
|
||||||
|
|||||||
@ -261,6 +261,7 @@ public enum Stories {
|
|||||||
case isMy
|
case isMy
|
||||||
case myReaction
|
case myReaction
|
||||||
case forwardInfo
|
case forwardInfo
|
||||||
|
case authorId
|
||||||
}
|
}
|
||||||
|
|
||||||
public let id: Int32
|
public let id: Int32
|
||||||
@ -284,6 +285,7 @@ public enum Stories {
|
|||||||
public let isMy: Bool
|
public let isMy: Bool
|
||||||
public let myReaction: MessageReaction.Reaction?
|
public let myReaction: MessageReaction.Reaction?
|
||||||
public let forwardInfo: ForwardInfo?
|
public let forwardInfo: ForwardInfo?
|
||||||
|
public let authorId: PeerId?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: Int32,
|
id: Int32,
|
||||||
@ -306,7 +308,8 @@ public enum Stories {
|
|||||||
isEdited: Bool,
|
isEdited: Bool,
|
||||||
isMy: Bool,
|
isMy: Bool,
|
||||||
myReaction: MessageReaction.Reaction?,
|
myReaction: MessageReaction.Reaction?,
|
||||||
forwardInfo: ForwardInfo?
|
forwardInfo: ForwardInfo?,
|
||||||
|
authorId: PeerId?
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
@ -329,6 +332,7 @@ public enum Stories {
|
|||||||
self.isMy = isMy
|
self.isMy = isMy
|
||||||
self.myReaction = myReaction
|
self.myReaction = myReaction
|
||||||
self.forwardInfo = forwardInfo
|
self.forwardInfo = forwardInfo
|
||||||
|
self.authorId = authorId
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -367,6 +371,7 @@ public enum Stories {
|
|||||||
self.isMy = try container.decodeIfPresent(Bool.self, forKey: .isMy) ?? false
|
self.isMy = try container.decodeIfPresent(Bool.self, forKey: .isMy) ?? false
|
||||||
self.myReaction = try container.decodeIfPresent(MessageReaction.Reaction.self, forKey: .myReaction)
|
self.myReaction = try container.decodeIfPresent(MessageReaction.Reaction.self, forKey: .myReaction)
|
||||||
self.forwardInfo = try container.decodeIfPresent(ForwardInfo.self, forKey: .forwardInfo)
|
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 {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -407,6 +412,7 @@ public enum Stories {
|
|||||||
try container.encode(self.isMy, forKey: .isMy)
|
try container.encode(self.isMy, forKey: .isMy)
|
||||||
try container.encodeIfPresent(self.myReaction, forKey: .myReaction)
|
try container.encodeIfPresent(self.myReaction, forKey: .myReaction)
|
||||||
try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo)
|
try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo)
|
||||||
|
try container.encodeIfPresent(self.authorId?.toInt64(), forKey: .authorId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||||
@ -485,6 +491,9 @@ public enum Stories {
|
|||||||
if lhs.forwardInfo != rhs.forwardInfo {
|
if lhs.forwardInfo != rhs.forwardInfo {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.authorId != rhs.authorId {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1173,7 +1182,7 @@ func _internal_uploadStoryImpl(
|
|||||||
for update in updates.allUpdates {
|
for update in updates.allUpdates {
|
||||||
if case let .updateStory(_, story) = update {
|
if case let .updateStory(_, story) = update {
|
||||||
switch story {
|
switch story {
|
||||||
case let .storyItem(_, idValue, _, _, _, _, _, media, _, _, _, _):
|
case let .storyItem(_, idValue, _, fromId, _, _, _, _, media, _, _, _, _):
|
||||||
if let parsedStory = Stories.StoredItem(apiStoryItem: story, peerId: toPeerId, transaction: transaction) {
|
if let parsedStory = Stories.StoredItem(apiStoryItem: story, peerId: toPeerId, transaction: transaction) {
|
||||||
var items = transaction.getStoryItems(peerId: toPeerId)
|
var items = transaction.getStoryItems(peerId: toPeerId)
|
||||||
var updatedItems: [Stories.Item] = []
|
var updatedItems: [Stories.Item] = []
|
||||||
@ -1199,7 +1208,8 @@ func _internal_uploadStoryImpl(
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: fromId?.peerId
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||||
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends))
|
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 {
|
for update in updates.allUpdates {
|
||||||
if case let .updateStory(_, story) = update {
|
if case let .updateStory(_, story) = update {
|
||||||
switch story {
|
switch story {
|
||||||
case let .storyItem(_, _, _, _, _, _, _, media, _, _, _, _):
|
case let .storyItem(_, _, _, _, _, _, _, _, media, _, _, _, _):
|
||||||
let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
|
let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
|
||||||
if let parsedMedia = parsedMedia, let originalMedia = originalMedia {
|
if let parsedMedia = parsedMedia, let originalMedia = originalMedia {
|
||||||
applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false)
|
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,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||||
transaction.setStory(id: storyId, value: entry)
|
transaction.setStory(id: storyId, value: entry)
|
||||||
@ -1412,7 +1423,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
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,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
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,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
)
|
)
|
||||||
updatedItems.append(updatedItem)
|
updatedItems.append(updatedItem)
|
||||||
}
|
}
|
||||||
@ -1668,7 +1682,7 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
|||||||
extension Api.StoryItem {
|
extension Api.StoryItem {
|
||||||
var id: Int32 {
|
var id: Int32 {
|
||||||
switch self {
|
switch self {
|
||||||
case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _):
|
case let .storyItem(_, id, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case let .storyItemDeleted(id):
|
case let .storyItemDeleted(id):
|
||||||
return id
|
return id
|
||||||
@ -1731,7 +1745,7 @@ extension Stories.Item.ForwardInfo {
|
|||||||
extension Stories.StoredItem {
|
extension Stories.StoredItem {
|
||||||
init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) {
|
init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) {
|
||||||
switch apiStoryItem {
|
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)
|
let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
||||||
if let parsedMedia = parsedMedia {
|
if let parsedMedia = parsedMedia {
|
||||||
var parsedPrivacy: Stories.Item.Privacy?
|
var parsedPrivacy: Stories.Item.Privacy?
|
||||||
@ -1840,7 +1854,8 @@ extension Stories.StoredItem {
|
|||||||
isEdited: isEdited,
|
isEdited: isEdited,
|
||||||
isMy: mergedIsMy,
|
isMy: mergedIsMy,
|
||||||
myReaction: mergedMyReaction,
|
myReaction: mergedMyReaction,
|
||||||
forwardInfo: mergedForwardInfo
|
forwardInfo: mergedForwardInfo,
|
||||||
|
authorId: fromId?.peerId
|
||||||
)
|
)
|
||||||
self = .item(item)
|
self = .item(item)
|
||||||
} else {
|
} else {
|
||||||
@ -1916,7 +1931,8 @@ func _internal_getStoryById(accountPeerId: PeerId, postbox: Postbox, network: Ne
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: reaction,
|
myReaction: reaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
))
|
))
|
||||||
updatedItemValue = updatedItem
|
updatedItemValue = updatedItem
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
@ -2419,7 +2436,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: reaction,
|
myReaction: reaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
))
|
))
|
||||||
updatedItemValue = updatedItem
|
updatedItemValue = updatedItem
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
|
|||||||
@ -78,8 +78,9 @@ public final class EngineStoryItem: Equatable {
|
|||||||
public let isMy: Bool
|
public let isMy: Bool
|
||||||
public let myReaction: MessageReaction.Reaction?
|
public let myReaction: MessageReaction.Reaction?
|
||||||
public let forwardInfo: ForwardInfo?
|
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.id = id
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.expirationTimestamp = expirationTimestamp
|
self.expirationTimestamp = expirationTimestamp
|
||||||
@ -102,6 +103,7 @@ public final class EngineStoryItem: Equatable {
|
|||||||
self.isMy = isMy
|
self.isMy = isMy
|
||||||
self.myReaction = myReaction
|
self.myReaction = myReaction
|
||||||
self.forwardInfo = forwardInfo
|
self.forwardInfo = forwardInfo
|
||||||
|
self.author = author
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool {
|
public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool {
|
||||||
@ -171,6 +173,9 @@ public final class EngineStoryItem: Equatable {
|
|||||||
if lhs.forwardInfo != rhs.forwardInfo {
|
if lhs.forwardInfo != rhs.forwardInfo {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.author != rhs.author {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,7 +228,8 @@ public extension EngineStoryItem {
|
|||||||
isEdited: self.isEdited,
|
isEdited: self.isEdited,
|
||||||
isMy: self.isMy,
|
isMy: self.isMy,
|
||||||
myReaction: self.myReaction,
|
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,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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)
|
items.append(mappedItem)
|
||||||
|
|
||||||
@ -745,7 +752,8 @@ public final class PeerStoryListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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)
|
storyItems.append(mappedItem)
|
||||||
}
|
}
|
||||||
@ -839,6 +847,11 @@ public final class PeerStoryListContext {
|
|||||||
peers[peer.id] = peer
|
peers[peer.id] = peer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let peerId = item.authorId {
|
||||||
|
if let peer = transaction.getPeer(peerId) {
|
||||||
|
peers[peer.id] = peer
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -907,7 +920,8 @@ public final class PeerStoryListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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
|
finalUpdatedState = updatedState
|
||||||
}
|
}
|
||||||
@ -955,7 +969,8 @@ public final class PeerStoryListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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
|
finalUpdatedState = updatedState
|
||||||
} else {
|
} else {
|
||||||
@ -1005,7 +1020,8 @@ public final class PeerStoryListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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
|
updatedState.items.sort(by: { lhs, rhs in
|
||||||
return lhs.timestamp > rhs.timestamp
|
return lhs.timestamp > rhs.timestamp
|
||||||
@ -1051,7 +1067,8 @@ public final class PeerStoryListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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
|
updatedState.items.sort(by: { lhs, rhs in
|
||||||
return lhs.timestamp > rhs.timestamp
|
return lhs.timestamp > rhs.timestamp
|
||||||
@ -1221,7 +1238,8 @@ public final class PeerExpiringStoryListContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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))
|
items.append(.item(mappedItem))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1249,7 +1249,8 @@ public extension TelegramEngine {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: item.forwardInfo
|
forwardInfo: item.forwardInfo,
|
||||||
|
authorId: item.authorId
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
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 botGameNoticeNamespace: Int32 = 7
|
||||||
private static let peerInviteRequestsNamespace: Int32 = 8
|
private static let peerInviteRequestsNamespace: Int32 = 8
|
||||||
private static let dismissedPremiumGiftNamespace: Int32 = 9
|
private static let dismissedPremiumGiftNamespace: Int32 = 9
|
||||||
|
private static let groupEmojiPackNamespace: Int32 = 9
|
||||||
|
|
||||||
static func inlineBotLocationRequestNotice(peerId: PeerId) -> NoticeEntryKey {
|
static func inlineBotLocationRequestNotice(peerId: PeerId) -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: inlineBotLocationRequestNamespace), key: noticeKey(peerId: peerId, key: 0))
|
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))
|
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 {
|
static func forcedPasswordSetup() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.secretChatInlineBotUsage.key)
|
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> {
|
public static func getSendWhenOnlineTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||||
return accountManager.transaction { transaction -> Int32 in
|
return accountManager.transaction { transaction -> Int32 in
|
||||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.sendWhenOnlineTip())?.get(ApplicationSpecificCounterNotice.self) {
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.sendWhenOnlineTip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||||
|
|||||||
@ -2274,7 +2274,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
|
|
||||||
nameNodeOriginY = headerSize.height
|
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
|
headerSize.height += nameNodeSizeApply.0.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2129,14 +2129,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||||||
var entities: [MessageTextEntity] = []
|
var entities: [MessageTextEntity] = []
|
||||||
|
|
||||||
if new != nil {
|
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 {
|
if index == 0, let author = author {
|
||||||
return [.TextMention(peerId: author.id)]
|
return [.TextMention(peerId: author.id)]
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}, to: &text, entities: &entities)
|
}, to: &text, entities: &entities)
|
||||||
} else {
|
} 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 {
|
if index == 0, let author = author {
|
||||||
return [.TextMention(peerId: author.id)]
|
return [.TextMention(peerId: author.id)]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -390,7 +390,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
public var useExternalSearchContainer: Bool = false
|
public var useExternalSearchContainer: Bool = false
|
||||||
|
|
||||||
private var gifContext: GifContext? {
|
private var gifContext: GifContext? {
|
||||||
@ -2133,6 +2133,12 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
strongSelf.interaction?.presentGlobalOverlayController(contextController, nil)
|
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 {
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ swift_library(
|
|||||||
"//submodules/SearchBarNode:SearchBarNode",
|
"//submodules/SearchBarNode:SearchBarNode",
|
||||||
"//submodules/SearchUI:SearchUI",
|
"//submodules/SearchUI:SearchUI",
|
||||||
"//submodules/MergeLists:MergeLists",
|
"//submodules/MergeLists:MergeLists",
|
||||||
|
"//submodules/UndoUI:UndoUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -344,6 +344,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
if case .searching = item.content {
|
if case .searching = item.content {
|
||||||
strongSelf.activityIndicator.isHidden = false
|
strongSelf.activityIndicator.isHidden = false
|
||||||
|
strongSelf.imageNode.isHidden = true
|
||||||
} else {
|
} else {
|
||||||
strongSelf.activityIndicator.isHidden = true
|
strongSelf.activityIndicator.isHidden = true
|
||||||
}
|
}
|
||||||
@ -351,6 +352,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if case .found = item.content {
|
if case .found = item.content {
|
||||||
strongSelf.removeButtonIcon.isHidden = false
|
strongSelf.removeButtonIcon.isHidden = false
|
||||||
strongSelf.removeButton.isHidden = false
|
strongSelf.removeButton.isHidden = false
|
||||||
|
strongSelf.imageNode.isHidden = false
|
||||||
} else {
|
} else {
|
||||||
strongSelf.removeButtonIcon.isHidden = true
|
strongSelf.removeButtonIcon.isHidden = true
|
||||||
strongSelf.removeButton.isHidden = true
|
strongSelf.removeButton.isHidden = true
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import PresentationDataUtils
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import StickerPackPreviewUI
|
import StickerPackPreviewUI
|
||||||
import ItemListStickerPackItem
|
import ItemListStickerPackItem
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
private final class GroupStickerPackSetupControllerArguments {
|
private final class GroupStickerPackSetupControllerArguments {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -343,6 +344,20 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
|||||||
initialData.set(.single(.noData))
|
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>()
|
let stickerPacks = Promise<CombinedView>()
|
||||||
stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [isEmoji ? Namespaces.ItemCollection.CloudEmojiPacks : Namespaces.ItemCollection.CloudStickerPacks])]))
|
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 {
|
if searchText.isEmpty {
|
||||||
return .single((searchText, .none))
|
return .single((searchText, .none))
|
||||||
} else if case let .data(data) = initialData, searchText.lowercased() == data.info.shortName {
|
} 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))))
|
return .single((searchText, .found(StickerPackData(info: data.info, item: data.item))))
|
||||||
} else {
|
} else {
|
||||||
let namespace = isEmoji ? Namespaces.ItemCollection.CloudEmojiPacks : Namespaces.ItemCollection.CloudStickerPacks
|
let namespace = isEmoji ? Namespaces.ItemCollection.CloudEmojiPacks : Namespaces.ItemCollection.CloudStickerPacks
|
||||||
@ -378,6 +396,13 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
|||||||
case let .result(info, items, _):
|
case let .result(info, items, _):
|
||||||
return .single((searchText, .found(StickerPackData(info: info, item: items.first))))
|
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 {
|
} else {
|
||||||
@ -385,7 +410,6 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
|
||||||
var navigateToChatControllerImpl: ((PeerId) -> Void)?
|
var navigateToChatControllerImpl: ((PeerId) -> Void)?
|
||||||
var dismissInputImpl: (() -> Void)?
|
var dismissInputImpl: (() -> Void)?
|
||||||
var dismissImpl: (() -> Void)?
|
var dismissImpl: (() -> Void)?
|
||||||
@ -402,15 +426,13 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
|||||||
|
|
||||||
let arguments = GroupStickerPackSetupControllerArguments(context: context, selectStickerPack: { info in
|
let arguments = GroupStickerPackSetupControllerArguments(context: context, selectStickerPack: { info in
|
||||||
searchText.set(info.shortName)
|
searchText.set(info.shortName)
|
||||||
if let completion {
|
completionImpl?(info)
|
||||||
completion(info)
|
|
||||||
}
|
|
||||||
}, openStickerPack: { info in
|
}, openStickerPack: { info in
|
||||||
presentStickerPackController?(info)
|
presentStickerPackController?(info)
|
||||||
}, updateSearchText: { text in
|
}, updateSearchText: { text in
|
||||||
searchText.set(text)
|
searchText.set(text)
|
||||||
if text == "", let completion {
|
if text == "" {
|
||||||
completion(nil)
|
completionImpl?(nil)
|
||||||
}
|
}
|
||||||
}, openStickersBot: {
|
}, openStickersBot: {
|
||||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers")
|
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers")
|
||||||
@ -473,29 +495,24 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
|||||||
info = data.info
|
info = data.info
|
||||||
}
|
}
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: enabled, action: {
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: enabled, action: {
|
||||||
if let completion {
|
if info?.id == currentPackInfo?.id {
|
||||||
completion(info)
|
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
} else {
|
} else {
|
||||||
if info?.id == currentPackInfo?.id {
|
updateState { state in
|
||||||
dismissImpl?()
|
var state = state
|
||||||
} else {
|
state.isSaving = true
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
saveDisposable.set((context.engine.peers.updateGroupSpecificStickerset(peerId: peerId, info: info)
|
||||||
|
|> deliverOnMainQueue).start(error: { _ in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.isSaving = true
|
state.isSaving = false
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
saveDisposable.set((context.engine.peers.updateGroupSpecificStickerset(peerId: peerId, info: info)
|
}, completed: {
|
||||||
|> deliverOnMainQueue).start(error: { _ in
|
dismissImpl?()
|
||||||
updateState { state in
|
}))
|
||||||
var state = state
|
|
||||||
state.isSaving = false
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}, completed: {
|
|
||||||
dismissImpl?()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -537,6 +554,14 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
|||||||
|
|
||||||
presentControllerImpl = { [weak controller] c, p in
|
presentControllerImpl = { [weak controller] c, p in
|
||||||
if let controller = controller {
|
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)
|
controller.present(c, in: .window(.root), with: p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -208,6 +208,7 @@ final class PeerInfoScreenData {
|
|||||||
let isPowerSavingEnabled: Bool?
|
let isPowerSavingEnabled: Bool?
|
||||||
let accountIsPremium: Bool
|
let accountIsPremium: Bool
|
||||||
let hasSavedMessageTags: Bool
|
let hasSavedMessageTags: Bool
|
||||||
|
let isPremiumRequiredForStoryPosting: Bool
|
||||||
|
|
||||||
let _isContact: Bool
|
let _isContact: Bool
|
||||||
var forceIsContact: Bool = false
|
var forceIsContact: Bool = false
|
||||||
@ -244,7 +245,8 @@ final class PeerInfoScreenData {
|
|||||||
appConfiguration: AppConfiguration?,
|
appConfiguration: AppConfiguration?,
|
||||||
isPowerSavingEnabled: Bool?,
|
isPowerSavingEnabled: Bool?,
|
||||||
accountIsPremium: Bool,
|
accountIsPremium: Bool,
|
||||||
hasSavedMessageTags: Bool
|
hasSavedMessageTags: Bool,
|
||||||
|
isPremiumRequiredForStoryPosting: Bool
|
||||||
) {
|
) {
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.chatPeer = chatPeer
|
self.chatPeer = chatPeer
|
||||||
@ -270,6 +272,7 @@ final class PeerInfoScreenData {
|
|||||||
self.isPowerSavingEnabled = isPowerSavingEnabled
|
self.isPowerSavingEnabled = isPowerSavingEnabled
|
||||||
self.accountIsPremium = accountIsPremium
|
self.accountIsPremium = accountIsPremium
|
||||||
self.hasSavedMessageTags = hasSavedMessageTags
|
self.hasSavedMessageTags = hasSavedMessageTags
|
||||||
|
self.isPremiumRequiredForStoryPosting = isPremiumRequiredForStoryPosting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,7 +669,8 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
|
|||||||
appConfiguration: appConfiguration,
|
appConfiguration: appConfiguration,
|
||||||
isPowerSavingEnabled: isPowerSavingEnabled,
|
isPowerSavingEnabled: isPowerSavingEnabled,
|
||||||
accountIsPremium: peer?.isPremium ?? false,
|
accountIsPremium: peer?.isPremium ?? false,
|
||||||
hasSavedMessageTags: false
|
hasSavedMessageTags: false,
|
||||||
|
isPremiumRequiredForStoryPosting: true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -702,7 +706,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
appConfiguration: nil,
|
appConfiguration: nil,
|
||||||
isPowerSavingEnabled: nil,
|
isPowerSavingEnabled: nil,
|
||||||
accountIsPremium: false,
|
accountIsPremium: false,
|
||||||
hasSavedMessageTags: false
|
hasSavedMessageTags: false,
|
||||||
|
isPremiumRequiredForStoryPosting: true
|
||||||
))
|
))
|
||||||
case let .user(userPeerId, secretChatId, kind):
|
case let .user(userPeerId, secretChatId, kind):
|
||||||
let groupsInCommon: GroupsInCommonContext?
|
let groupsInCommon: GroupsInCommonContext?
|
||||||
@ -973,7 +978,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
appConfiguration: nil,
|
appConfiguration: nil,
|
||||||
isPowerSavingEnabled: nil,
|
isPowerSavingEnabled: nil,
|
||||||
accountIsPremium: accountIsPremium,
|
accountIsPremium: accountIsPremium,
|
||||||
hasSavedMessageTags: hasSavedMessageTags
|
hasSavedMessageTags: hasSavedMessageTags,
|
||||||
|
isPremiumRequiredForStoryPosting: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .channel:
|
case .channel:
|
||||||
@ -1047,6 +1053,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
hasSavedMessageTags = .single(false)
|
hasSavedMessageTags = .single(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isPremiumRequiredForStoryPosting: Signal<Bool, NoError> = isPremiumRequiredForStoryPosting(context: context)
|
||||||
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
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),
|
context.engine.peers.recommendedChannels(peerId: peerId),
|
||||||
hasSavedMessages,
|
hasSavedMessages,
|
||||||
hasSavedMessagesChats,
|
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
|
var availablePanes = availablePanes
|
||||||
if let hasStories {
|
if let hasStories {
|
||||||
if hasStories {
|
if hasStories {
|
||||||
@ -1138,7 +1147,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
appConfiguration: nil,
|
appConfiguration: nil,
|
||||||
isPowerSavingEnabled: nil,
|
isPowerSavingEnabled: nil,
|
||||||
accountIsPremium: accountIsPremium,
|
accountIsPremium: accountIsPremium,
|
||||||
hasSavedMessageTags: hasSavedMessageTags
|
hasSavedMessageTags: hasSavedMessageTags,
|
||||||
|
isPremiumRequiredForStoryPosting: isPremiumRequiredForStoryPosting
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case let .group(groupId):
|
case let .group(groupId):
|
||||||
@ -1315,6 +1325,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
hasSavedMessageTags = .single(false)
|
hasSavedMessageTags = .single(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isPremiumRequiredForStoryPosting: Signal<Bool, NoError> = isPremiumRequiredForStoryPosting(context: context)
|
||||||
|
|
||||||
return combineLatest(queue: .mainQueue(),
|
return combineLatest(queue: .mainQueue(),
|
||||||
context.account.viewTracker.peerView(groupId, updateData: true),
|
context.account.viewTracker.peerView(groupId, updateData: true),
|
||||||
peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
||||||
@ -1331,9 +1343,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
accountIsPremium,
|
accountIsPremium,
|
||||||
hasSavedMessages,
|
hasSavedMessages,
|
||||||
hasSavedMessagesChats,
|
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?
|
var discussionPeer: Peer?
|
||||||
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
||||||
discussionPeer = peer
|
discussionPeer = peer
|
||||||
@ -1426,7 +1439,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
appConfiguration: appConfiguration,
|
appConfiguration: appConfiguration,
|
||||||
isPowerSavingEnabled: nil,
|
isPowerSavingEnabled: nil,
|
||||||
accountIsPremium: accountIsPremium,
|
accountIsPremium: accountIsPremium,
|
||||||
hasSavedMessageTags: hasSavedMessageTags
|
hasSavedMessageTags: hasSavedMessageTags,
|
||||||
|
isPremiumRequiredForStoryPosting: isPremiumRequiredForStoryPosting
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1812,3 +1826,25 @@ func peerInfoIsChatMuted(peer: Peer?, peerNotificationSettings: TelegramPeerNoti
|
|||||||
}
|
}
|
||||||
return chatIsMuted
|
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
|
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)
|
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, 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) {
|
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)
|
controller.push(boostsController)
|
||||||
} else {
|
} else {
|
||||||
let _ = combineLatest(
|
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)
|
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .settings, forceDark: false, dismissed: nil)
|
||||||
self.controller?.push(controller)
|
self.controller?.push(controller)
|
||||||
case .premiumGift:
|
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)
|
self.controller?.push(controller)
|
||||||
case .stickers:
|
case .stickers:
|
||||||
if let settings = self.data?.globalSettings {
|
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) {
|
} 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))
|
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)
|
rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -348,50 +348,61 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch component.scope {
|
let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId))
|
||||||
case .saved:
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
let selectedCount = paneNode.selectedItems.count
|
guard let self, let peer else {
|
||||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.peerId, ids: paneNode.selectedItems, isPinned: false).start()
|
return
|
||||||
|
}
|
||||||
paneNode.setIsSelectionModeActive(false)
|
var isGroup = false
|
||||||
(self.environment?.controller() as? PeerInfoStoryGridScreen)?.updateTitle()
|
if case let .channel(channel) = peer, case .group = channel.info {
|
||||||
|
isGroup = true
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
environment.controller()?.present(UndoOverlayController(
|
switch component.scope {
|
||||||
presentationData: presentationData,
|
case .saved:
|
||||||
content: .info(title: title, text: text, timeout: nil, customUndoText: nil),
|
let selectedCount = paneNode.selectedItems.count
|
||||||
elevatedLayout: false,
|
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.peerId, ids: paneNode.selectedItems, isPinned: false).start()
|
||||||
animateInAsReplacement: false,
|
|
||||||
action: { _ in return false }
|
paneNode.setIsSelectionModeActive(false)
|
||||||
), in: .current)
|
(self.environment?.controller() as? PeerInfoStoryGridScreen)?.updateTitle()
|
||||||
|
|
||||||
paneNode.clearSelection()
|
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: {},
|
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.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 self.backButton.view.superview == nil {
|
||||||
if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar {
|
if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar {
|
||||||
navigationBar.view.addSubview(self.backButton.view)
|
navigationBar.view.addSubview(self.backButton.view)
|
||||||
|
|||||||
@ -19,15 +19,17 @@ final class StoryAuthorInfoComponent: Component {
|
|||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let peer: EnginePeer?
|
let peer: EnginePeer?
|
||||||
let forwardInfo: EngineStoryItem.ForwardInfo?
|
let forwardInfo: EngineStoryItem.ForwardInfo?
|
||||||
|
let author: EnginePeer?
|
||||||
let timestamp: Int32
|
let timestamp: Int32
|
||||||
let counters: Counters?
|
let counters: Counters?
|
||||||
let isEdited: Bool
|
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.context = context
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.forwardInfo = forwardInfo
|
self.forwardInfo = forwardInfo
|
||||||
|
self.author = author
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.counters = counters
|
self.counters = counters
|
||||||
self.isEdited = isEdited
|
self.isEdited = isEdited
|
||||||
@ -46,6 +48,9 @@ final class StoryAuthorInfoComponent: Component {
|
|||||||
if lhs.forwardInfo != rhs.forwardInfo {
|
if lhs.forwardInfo != rhs.forwardInfo {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.author != rhs.author {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.timestamp != rhs.timestamp {
|
if lhs.timestamp != rhs.timestamp {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -121,6 +126,16 @@ final class StoryAuthorInfoComponent: Component {
|
|||||||
}
|
}
|
||||||
subtitle = combinedString
|
subtitle = combinedString
|
||||||
subtitleTruncationType = .middle
|
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 {
|
} else {
|
||||||
var subtitleString = stringForStoryActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, preciseTime: true, relativeTimestamp: component.timestamp, relativeTo: timestamp)
|
var subtitleString = stringForStoryActivityTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, preciseTime: true, relativeTimestamp: component.timestamp, relativeTo: timestamp)
|
||||||
if component.isEdited {
|
if component.isEdited {
|
||||||
@ -176,7 +191,16 @@ final class StoryAuthorInfoComponent: Component {
|
|||||||
self.repostIconView = nil
|
self.repostIconView = nil
|
||||||
repostIconView.removeFromSuperview()
|
repostIconView.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var authorPeer: EnginePeer?
|
||||||
if let forwardInfo = component.forwardInfo, case let .known(peer, _, _) = forwardInfo {
|
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
|
let avatarNode: AvatarNode
|
||||||
if let current = self.avatarNode {
|
if let current = self.avatarNode {
|
||||||
avatarNode = current
|
avatarNode = current
|
||||||
|
|||||||
@ -114,6 +114,11 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
forwardInfoStories.updateValue(nil, forKey: storyId)
|
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 {
|
for entity in itemValue.entities {
|
||||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
||||||
@ -296,7 +301,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
myReaction: item.myReaction,
|
||||||
forwardInfo: forwardInfo
|
forwardInfo: forwardInfo,
|
||||||
|
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var totalCount = peerStoryItemsView.items.count
|
var totalCount = peerStoryItemsView.items.count
|
||||||
@ -332,7 +338,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
isEdited: false,
|
isEdited: false,
|
||||||
isMy: true,
|
isMy: true,
|
||||||
myReaction: nil,
|
myReaction: nil,
|
||||||
forwardInfo: pendingForwardsInfo[item.randomId]
|
forwardInfo: pendingForwardsInfo[item.randomId],
|
||||||
|
author: nil
|
||||||
))
|
))
|
||||||
totalCount += 1
|
totalCount += 1
|
||||||
}
|
}
|
||||||
@ -1187,6 +1194,11 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
stories.updateValue(nil, forKey: storyId)
|
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 {
|
for entity in item.entities {
|
||||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
||||||
@ -1312,7 +1324,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
isEdited: itemValue.isEdited,
|
isEdited: itemValue.isEdited,
|
||||||
isMy: itemValue.isMy,
|
isMy: itemValue.isMy,
|
||||||
myReaction: itemValue.myReaction,
|
myReaction: itemValue.myReaction,
|
||||||
forwardInfo: forwardInfo
|
forwardInfo: forwardInfo,
|
||||||
|
author: itemValue.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||||
)
|
)
|
||||||
|
|
||||||
let mainItem = StoryContentItem(
|
let mainItem = StoryContentItem(
|
||||||
@ -2191,7 +2204,8 @@ private func getCachedStory(storyId: StoryId, transaction: Transaction) -> Engin
|
|||||||
isEdited: item.isEdited,
|
isEdited: item.isEdited,
|
||||||
isMy: item.isMy,
|
isMy: item.isMy,
|
||||||
myReaction: item.myReaction,
|
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 {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -1660,11 +1660,18 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
var canShare = true
|
var canShare = true
|
||||||
var displayFooter = false
|
var displayFooter = false
|
||||||
if case let .channel(channel) = component.slice.peer {
|
if case let .channel(channel) = component.slice.peer {
|
||||||
displayFooter = true
|
|
||||||
isChannel = true
|
isChannel = true
|
||||||
if channel.addressName == nil {
|
if channel.addressName == nil {
|
||||||
canShare = false
|
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 {
|
} else if component.slice.peer.id == component.context.account.peerId {
|
||||||
displayFooter = true
|
displayFooter = true
|
||||||
} else if component.slice.item.storyItem.isPending {
|
} else if component.slice.item.storyItem.isPending {
|
||||||
@ -2752,6 +2759,24 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
disabledPlaceholder = .text(component.strings.Story_FooterReplyUnavailable)
|
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
|
let inputPlaceholder: MessageInputPanelComponent.Placeholder
|
||||||
if let stealthModeTimeout = component.stealthModeTimeout {
|
if let stealthModeTimeout = component.stealthModeTimeout {
|
||||||
let minutes = Int(stealthModeTimeout / 60)
|
let minutes = Int(stealthModeTimeout / 60)
|
||||||
@ -2787,7 +2812,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
inputPlaceholder = .counter(items)
|
inputPlaceholder = .counter(items)
|
||||||
} else {
|
} else {
|
||||||
inputPlaceholder = .plain(component.strings.Story_InputPlaceholderReplyPrivately)
|
inputPlaceholder = .plain(isGroup ? component.strings.Story_InputPlaceholderReplyInGroup : component.strings.Story_InputPlaceholderReplyPrivately)
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime22 = CFAbsoluteTimeGetCurrent()
|
let startTime22 = CFAbsoluteTimeGetCurrent()
|
||||||
@ -2811,13 +2836,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
var inputPanelSize: CGSize?
|
var inputPanelSize: CGSize?
|
||||||
|
|
||||||
let startTime23 = CFAbsoluteTimeGetCurrent()
|
let startTime23 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
var isChannel = false
|
if showMessageInputPanel {
|
||||||
if case .channel = component.slice.peer {
|
|
||||||
isChannel = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if component.slice.peer.id != component.context.account.peerId && !isChannel {
|
|
||||||
var haveLikeOptions = false
|
var haveLikeOptions = false
|
||||||
if case .user = component.slice.peer {
|
if case .user = component.slice.peer {
|
||||||
haveLikeOptions = true
|
haveLikeOptions = true
|
||||||
@ -3998,6 +4018,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
strings: component.strings,
|
strings: component.strings,
|
||||||
peer: component.slice.peer,
|
peer: component.slice.peer,
|
||||||
forwardInfo: component.slice.item.storyItem.forwardInfo,
|
forwardInfo: component.slice.item.storyItem.forwardInfo,
|
||||||
|
author: component.slice.item.storyItem.author,
|
||||||
timestamp: component.slice.item.storyItem.timestamp,
|
timestamp: component.slice.item.storyItem.timestamp,
|
||||||
counters: counters,
|
counters: counters,
|
||||||
isEdited: component.slice.item.storyItem.isEdited
|
isEdited: component.slice.item.storyItem.isEdited
|
||||||
@ -4031,6 +4052,12 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
self.navigateToPeer(peer: peer, chat: false)
|
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 {
|
} else {
|
||||||
if component.slice.peer.id == component.context.account.peerId {
|
if component.slice.peer.id == component.context.account.peerId {
|
||||||
self.navigateToMyStories()
|
self.navigateToMyStories()
|
||||||
@ -4381,7 +4408,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme),
|
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme),
|
||||||
items: reactionItems.map(ReactionContextItem.reaction),
|
items: reactionItems.map(ReactionContextItem.reaction),
|
||||||
selectedItems: component.slice.item.storyItem.myReaction.flatMap { Set([$0]) } ?? Set(),
|
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,
|
reactionsLocked: false,
|
||||||
alwaysAllowPremiumReactions: false,
|
alwaysAllowPremiumReactions: false,
|
||||||
allPresetReactionsAreAvailable: false,
|
allPresetReactionsAreAvailable: false,
|
||||||
@ -5356,6 +5383,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
let externalState = MediaEditorTransitionOutExternalState(
|
let externalState = MediaEditorTransitionOutExternalState(
|
||||||
storyTarget: nil,
|
storyTarget: nil,
|
||||||
|
isForcedTarget: false,
|
||||||
isPeerArchived: false,
|
isPeerArchived: false,
|
||||||
transitionOut: nil
|
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()
|
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)
|
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||||
if component.slice.item.storyItem.isPinned {
|
if component.slice.item.storyItem.isPinned {
|
||||||
self.scheduledStoryUnpinnedUndoOverlay = UndoOverlayController(
|
self.scheduledStoryUnpinnedUndoOverlay = UndoOverlayController(
|
||||||
presentationData: presentationData,
|
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,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
blurred: true,
|
||||||
@ -6407,7 +6440,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
self.component?.presentController(UndoOverlayController(
|
self.component?.presentController(UndoOverlayController(
|
||||||
presentationData: presentationData,
|
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,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
blurred: true,
|
||||||
|
|||||||
@ -1443,9 +1443,6 @@ public class VideoMessageCameraScreen: ViewController {
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.audioSessionDisposable?.dispose()
|
self.audioSessionDisposable?.dispose()
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
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
|
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 {
|
if let self {
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
self.node.setupCamera()
|
self.node.setupCamera()
|
||||||
|
|||||||
@ -450,6 +450,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var mediaRestrictedTooltipControllerMode = true
|
var mediaRestrictedTooltipControllerMode = true
|
||||||
weak var checksTooltipController: TooltipController?
|
weak var checksTooltipController: TooltipController?
|
||||||
weak var copyProtectionTooltipController: TooltipController?
|
weak var copyProtectionTooltipController: TooltipController?
|
||||||
|
weak var emojiPackTooltipController: TooltipScreen?
|
||||||
|
|
||||||
var currentMessageTooltipScreens: [(TooltipScreen, ListViewItemNode)] = []
|
var currentMessageTooltipScreens: [(TooltipScreen, ListViewItemNode)] = []
|
||||||
|
|
||||||
@ -965,14 +966,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if let peer = peer as? TelegramChannel, peer.hasPermission(.changeInfo) {
|
if let peer = peer as? TelegramChannel {
|
||||||
let _ = (context.engine.peers.getChannelBoostStatus(peerId: peer.id)
|
if peer.flags.contains(.isCreator) || peer.adminRights?.rights.contains(.canChangeInfo) == true {
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] boostStatus in
|
let _ = (context.engine.peers.getChannelBoostStatus(peerId: peer.id)
|
||||||
guard let self else {
|
|> deliverOnMainQueue).start(next: { [weak self] boostStatus in
|
||||||
return
|
guard let self else {
|
||||||
}
|
return
|
||||||
self.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peer.id, boostStatus: boostStatus))
|
}
|
||||||
})
|
self.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peer.id, boostStatus: boostStatus))
|
||||||
|
})
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
guard message.effectivelyIncoming(strongSelf.context.account.peerId), let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
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 {
|
guard let rect = self.chatDisplayNode.frameForEmojiButton(), self.effectiveNavigationController?.topViewController === self else {
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
let _ = (self.context.engine.stickers.resolveInlineStickers(fileIds: [thumbnailFileId])
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] files in
|
let _ = (ApplicationSpecificNotice.groupEmojiPackSuggestion(accountManager: self.context.sharedContext.accountManager, peerId: peerId)
|
||||||
guard let self, let emojiFile = files.values.first else {
|
|> deliverOnMainQueue).start(next: { [weak self] counter in
|
||||||
|
guard let self, counter == 0 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
let _ = (self.context.engine.stickers.resolveInlineStickers(fileIds: [thumbnailFileId])
|
||||||
let boldTextFont = Font.bold(self.presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
|> deliverOnMainQueue).start(next: { [weak self] files in
|
||||||
let textColor = UIColor.white
|
guard let self, let emojiFile = files.values.first else {
|
||||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: textColor), linkAttribute: { _ in
|
return
|
||||||
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)
|
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(
|
let externalState = MediaEditorTransitionOutExternalState(
|
||||||
storyTarget: nil,
|
storyTarget: nil,
|
||||||
|
isForcedTarget: false,
|
||||||
isPeerArchived: false,
|
isPeerArchived: false,
|
||||||
transitionOut: nil
|
transitionOut: nil
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3705,6 +3705,17 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
strongSelf.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
|
strongSelf.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
|
||||||
return (.media(mode: .other, expanded: nil, focused: false), state.interfaceState.messageActionsState.closedButtonKeyboardMessageId)
|
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 {
|
if case .scheduledMessages = self.presentationInterfaceState.subject {
|
||||||
isScheduledMessages = true
|
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
|
let mediaPickerContext = controller.mediaPickerContext
|
||||||
controller.openCamera = { [weak self] cameraView in
|
controller.openCamera = { [weak self] cameraView in
|
||||||
self?.openCamera(cameraView: cameraView)
|
self?.openCamera(cameraView: cameraView)
|
||||||
|
|||||||
@ -587,7 +587,7 @@ func openResolvedUrlImpl(
|
|||||||
}
|
}
|
||||||
case let .premiumMultiGift(reference):
|
case let .premiumMultiGift(reference):
|
||||||
dismissInput()
|
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 {
|
if let navigationController = navigationController {
|
||||||
navigationController.pushViewController(controller, animated: true)
|
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)
|
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]>()
|
let options = Promise<[PremiumGiftCodeOption]>()
|
||||||
options.set(context.engine.payments.premiumGiftCodeOptions(peerId: nil))
|
options.set(context.engine.payments.premiumGiftCodeOptions(peerId: nil))
|
||||||
|
|
||||||
@ -2101,6 +2101,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
pushImpl?(c)
|
pushImpl?(c)
|
||||||
}, completion: {
|
}, completion: {
|
||||||
filterImpl?()
|
filterImpl?()
|
||||||
|
completion?()
|
||||||
})
|
})
|
||||||
pushImpl = { [weak giftController] c in
|
pushImpl = { [weak giftController] c in
|
||||||
giftController?.push(c)
|
giftController?.push(c)
|
||||||
@ -2108,7 +2109,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
filterImpl = { [weak giftController] in
|
filterImpl = { [weak giftController] in
|
||||||
if let navigationController = giftController?.navigationController as? NavigationController {
|
if let navigationController = giftController?.navigationController as? NavigationController {
|
||||||
var controllers = navigationController.viewControllers
|
var controllers = navigationController.viewControllers
|
||||||
controllers = controllers.filter { !($0 is ContactMultiselectionController) }
|
controllers = controllers.filter { !($0 is ContactMultiselectionController) && !($0 is PremiumGiftScreen) }
|
||||||
navigationController.setViewControllers(controllers, animated: true)
|
navigationController.setViewControllers(controllers, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -277,6 +277,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
|
|
||||||
let externalState = MediaEditorTransitionOutExternalState(
|
let externalState = MediaEditorTransitionOutExternalState(
|
||||||
storyTarget: nil,
|
storyTarget: nil,
|
||||||
|
isForcedTarget: customTarget != nil,
|
||||||
isPeerArchived: false,
|
isPeerArchived: false,
|
||||||
transitionOut: nil
|
transitionOut: nil
|
||||||
)
|
)
|
||||||
@ -522,13 +523,17 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
var viewControllers = self.viewControllers
|
var viewControllers = self.viewControllers
|
||||||
|
|
||||||
let archiveController = ChatListControllerImpl(context: context, location: .chatList(groupId: .archive), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
|
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
|
chatListController = archiveController
|
||||||
viewControllers.insert(archiveController, at: 1)
|
viewControllers.insert(archiveController, at: 1)
|
||||||
self.setViewControllers(viewControllers, animated: false)
|
self.setViewControllers(viewControllers, animated: false)
|
||||||
} else {
|
} else {
|
||||||
chatListController = self.chatListController as? ChatListControllerImpl
|
chatListController = self.chatListController as? ChatListControllerImpl
|
||||||
externalState.transitionOut = chatListController?.storyCameraTransitionOut()
|
if !externalState.isForcedTarget {
|
||||||
|
externalState.transitionOut = chatListController?.storyCameraTransitionOut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let chatListController {
|
if let chatListController {
|
||||||
|
|||||||
@ -291,6 +291,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
private var paymentDisposable: Disposable?
|
private var paymentDisposable: Disposable?
|
||||||
|
|
||||||
|
private var lastExpansionTimestamp: Double?
|
||||||
|
|
||||||
private var didTransitionIn = false
|
private var didTransitionIn = false
|
||||||
private var dismissed = false
|
private var dismissed = false
|
||||||
|
|
||||||
@ -739,7 +741,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
guard let eventName = body["eventName"] as? String else {
|
guard let eventName = body["eventName"] as? String else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let currentTimestamp = CACurrentMediaTime()
|
||||||
let eventData = (body["eventData"] as? String)?.data(using: .utf8)
|
let eventData = (body["eventData"] as? String)?.data(using: .utf8)
|
||||||
let json = try? JSONSerialization.jsonObject(with: eventData ?? Data(), options: []) as? [String: Any]
|
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":
|
case "web_app_request_theme":
|
||||||
self.sendThemeChangedEvent()
|
self.sendThemeChangedEvent()
|
||||||
case "web_app_expand":
|
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":
|
case "web_app_close":
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
case "web_app_open_tg_link":
|
case "web_app_open_tg_link":
|
||||||
@ -846,7 +853,6 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
case "web_app_open_link":
|
case "web_app_open_link":
|
||||||
if let json = json, let url = json["url"] as? String {
|
if let json = json, let url = json["url"] as? String {
|
||||||
let tryInstantView = json["try_instant_view"] as? Bool ?? false
|
let tryInstantView = json["try_instant_view"] as? Bool ?? false
|
||||||
let currentTimestamp = CACurrentMediaTime()
|
|
||||||
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
|
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
|
||||||
self.webView?.lastTouchTimestamp = nil
|
self.webView?.lastTouchTimestamp = nil
|
||||||
if tryInstantView {
|
if tryInstantView {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user