mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
Merge commit 'ac9098839d721ad683e0845deac86d34cc201542'
This commit is contained in:
commit
e35711d5c1
@ -10222,6 +10222,10 @@ Sorry for the inconvenience.";
|
|||||||
"ReassignBoost.Boosts_any" = "%@ boosts are reassigned";
|
"ReassignBoost.Boosts_any" = "%@ boosts are reassigned";
|
||||||
"ReassignBoost.OtherChannels_1" = "%@ other channel";
|
"ReassignBoost.OtherChannels_1" = "%@ other channel";
|
||||||
"ReassignBoost.OtherChannels_any" = "%@ other channels";
|
"ReassignBoost.OtherChannels_any" = "%@ other channels";
|
||||||
|
"ReassignBoost.OtherGroups_1" = "%@ other group";
|
||||||
|
"ReassignBoost.OtherGroups_any" = "%@ other groups";
|
||||||
|
"ReassignBoost.OtherGroupsAndChannels_1" = "%@ other group and channel";
|
||||||
|
"ReassignBoost.OtherGroupsAndChannels_any" = "%@ other groups and channels";
|
||||||
|
|
||||||
"ChannelBoost.MoreBoosts.Title" = "More Boosts Needed";
|
"ChannelBoost.MoreBoosts.Title" = "More Boosts Needed";
|
||||||
"ChannelBoost.MoreBoosts.Text" = "To boost **%1$@** again, gift **Telegram Premium** to a friend and get **%2$@** additional boosts.";
|
"ChannelBoost.MoreBoosts.Text" = "To boost **%1$@** again, gift **Telegram Premium** to a friend and get **%2$@** additional boosts.";
|
||||||
@ -11006,8 +11010,8 @@ Sorry for the inconvenience.";
|
|||||||
"GroupBoost.Title.Other" = "Help Upgrade This Group";
|
"GroupBoost.Title.Other" = "Help Upgrade This Group";
|
||||||
"GroupBoost.Title.Current" = "Boost Group";
|
"GroupBoost.Title.Current" = "Boost Group";
|
||||||
|
|
||||||
"GroupBoost.EnableStoriesText" = "Your group needs %1$@ to enable posting stories.\n\nAsk your **Premium** members to boost your group with this link:";
|
"GroupBoost.EnableStoriesText" = "Your group needs %1$@ to enable posting stories.";
|
||||||
"GroupBoost.IncreaseLimitText" = "Your group needs %1$@ to post %2$@.\n\nAsk your **Premium** members to boost your group with this link:";
|
"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 subscribers.";
|
||||||
|
|
||||||
@ -11101,8 +11105,6 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Notification.GiveawayStartedGroup" = "%1$@ just started a giveaway of Telegram Premium subscriptions for its members.";
|
"Notification.GiveawayStartedGroup" = "%1$@ just started a giveaway of Telegram Premium subscriptions for its members.";
|
||||||
|
|
||||||
"Chat.Giveaway.Message.Group.Participants" = "All subscribers of this group:";
|
|
||||||
|
|
||||||
"Chat.Giveaway.Info.Group.RandomMembers_1" = "**%@** random member";
|
"Chat.Giveaway.Info.Group.RandomMembers_1" = "**%@** random member";
|
||||||
"Chat.Giveaway.Info.Group.RandomMembers_any" = "**%@** random members";
|
"Chat.Giveaway.Info.Group.RandomMembers_any" = "**%@** random members";
|
||||||
|
|
||||||
@ -11238,3 +11240,39 @@ Sorry for the inconvenience.";
|
|||||||
"GroupBoost.Error.PremiumNeededText" = "Only **Telegram Premium** subscribers can boost groups. Do you want to subscribe to **Telegram Premium**?";
|
"GroupBoost.Error.PremiumNeededText" = "Only **Telegram Premium** subscribers can boost groups. Do you want to subscribe to **Telegram Premium**?";
|
||||||
|
|
||||||
"Notification.GroupChangedWallpaper" = "Group set a new wallpaper";
|
"Notification.GroupChangedWallpaper" = "Group set a new wallpaper";
|
||||||
|
"Notification.YouChangedGroupWallpaper" = "You set a new wallpaper for this group";
|
||||||
|
|
||||||
|
"WallpaperPreview.GroupHeader" = "All members will see this wallpaper";
|
||||||
|
|
||||||
|
"GroupBoost.BoostGroup" = "Boost Group";
|
||||||
|
|
||||||
|
"Chat.Giveaway.Info.NotAllowedAdminGroup" = "You are not eligible to participate in this giveaway, because you are an admin of participating group (**%@**).";
|
||||||
|
|
||||||
|
"Chat.Giveaway.Message.Group.Participants" = "All members of this group:";
|
||||||
|
"Chat.Giveaway.Message.Group.ParticipantsNew" = "All users who join this group after this date:";
|
||||||
|
|
||||||
|
"Chat.Giveaway.Message.Group.ParticipantsMany" = "All members of the groups below:";
|
||||||
|
"Chat.Giveaway.Message.Group.ParticipantsNewMany" = "All users who join the groups below after this date:";
|
||||||
|
|
||||||
|
"Story.Privacy.KeepOnGroupPage" = "Post to Group Page";
|
||||||
|
"Story.Privacy.KeepOnGroupPageInfo" = "Keep this story on group page even after it expires in %@.";
|
||||||
|
"Story.Privacy.TooltipStoryArchivedGroup" = "Users will see this story on the group page even after it expires.";
|
||||||
|
|
||||||
|
"BoostGift.Members.Subtitle" = "select up to %@ members";
|
||||||
|
"BoostGift.Members.SectionTitle" = "MEMBERS";
|
||||||
|
"BoostGift.Members.Search" = "Search Members";
|
||||||
|
"BoostGift.Members.MaximumReached" = "You can select maximum %@ members.";
|
||||||
|
|
||||||
|
"Chat.ErrorCantBoost" = "Sorry, you can't boost this group or channel.";
|
||||||
|
|
||||||
|
"ChannelBoost.Boost" = "Boost";
|
||||||
|
"ChannelBoost.Copy" = "Copy";
|
||||||
|
|
||||||
|
"Chat.Giveaway.Info.OtherGroups_1" = "**%@** other listed group";
|
||||||
|
"Chat.Giveaway.Info.OtherGroups_any" = "**%@** other listed groups";
|
||||||
|
|
||||||
|
"Chat.Giveaway.Info.OtherGroupsAndChannels_1" = "**%@** other listed group and channel";
|
||||||
|
"Chat.Giveaway.Info.OtherGroupsAndChannels_any" = "**%@** other listed groups and channels";
|
||||||
|
|
||||||
|
"Chat.Giveaway.Info.OtherChannelsAndGroups_1" = "**%@** other listed channel and group";
|
||||||
|
"Chat.Giveaway.Info.OtherChannelsAndGroups_any" = "**%@** other listed channels and groups";
|
||||||
|
|||||||
@ -304,7 +304,7 @@ public enum ResolvedUrl {
|
|||||||
case premiumOffer(reference: String?)
|
case premiumOffer(reference: String?)
|
||||||
case chatFolder(slug: String)
|
case chatFolder(slug: String)
|
||||||
case story(peerId: PeerId, id: Int32)
|
case story(peerId: PeerId, id: Int32)
|
||||||
case boost(peerId: PeerId, status: ChannelBoostStatus?, myBoostStatus: MyBoostStatus?)
|
case boost(peerId: PeerId?, status: ChannelBoostStatus?, myBoostStatus: MyBoostStatus?)
|
||||||
case premiumGiftCode(slug: String)
|
case premiumGiftCode(slug: String)
|
||||||
case premiumMultiGift(reference: String?)
|
case premiumMultiGift(reference: String?)
|
||||||
}
|
}
|
||||||
@ -961,6 +961,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
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) -> 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 makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
||||||
|
|
||||||
|
|||||||
@ -124,7 +124,7 @@ public final class ItemListStickerPackItem: ListViewItem, ItemListItem {
|
|||||||
|
|
||||||
public enum StickerPackThumbnailItem: Equatable {
|
public enum StickerPackThumbnailItem: Equatable {
|
||||||
case still(TelegramMediaImageRepresentation)
|
case still(TelegramMediaImageRepresentation)
|
||||||
case animated(MediaResource, PixelDimensions, Bool)
|
case animated(MediaResource, PixelDimensions, Bool, Bool)
|
||||||
|
|
||||||
public static func ==(lhs: StickerPackThumbnailItem, rhs: StickerPackThumbnailItem) -> Bool {
|
public static func ==(lhs: StickerPackThumbnailItem, rhs: StickerPackThumbnailItem) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
@ -134,8 +134,8 @@ public enum StickerPackThumbnailItem: Equatable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .animated(lhsResource, lhsDimensions, lhsIsVideo):
|
case let .animated(lhsResource, lhsDimensions, lhsIsVideo, lhsTinted):
|
||||||
if case let .animated(rhsResource, rhsDimensions, rhsIsVideo) = rhs, lhsResource.isEqual(to: rhsResource), lhsDimensions == rhsDimensions, lhsIsVideo == rhsIsVideo {
|
if case let .animated(rhsResource, rhsDimensions, rhsIsVideo, rhsTinted) = rhs, lhsResource.isEqual(to: rhsResource), lhsDimensions == rhsDimensions, lhsIsVideo == rhsIsVideo, lhsTinted == rhsTinted {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -489,14 +489,14 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
var resourceReference: MediaResourceReference?
|
var resourceReference: MediaResourceReference?
|
||||||
if let thumbnail = item.packInfo.thumbnail {
|
if let thumbnail = item.packInfo.thumbnail {
|
||||||
if item.packInfo.flags.contains(.isAnimated) || item.packInfo.flags.contains(.isVideo) {
|
if item.packInfo.flags.contains(.isAnimated) || item.packInfo.flags.contains(.isVideo) {
|
||||||
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, item.packInfo.flags.contains(.isVideo))
|
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, item.packInfo.flags.contains(.isVideo), item.packInfo.flags.contains(.isCustomTemplateEmoji))
|
||||||
} else {
|
} else {
|
||||||
thumbnailItem = .still(thumbnail)
|
thumbnailItem = .still(thumbnail)
|
||||||
}
|
}
|
||||||
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
|
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
|
||||||
} else if let item = item.topItem {
|
} else if let item = item.topItem {
|
||||||
if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
||||||
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker)
|
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker, item.file.isCustomTemplateEmoji)
|
||||||
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
|
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
|
||||||
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
|
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
|
||||||
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||||
@ -523,7 +523,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stillImageSize, boundingSize: stillImageSize, intrinsicInsets: UIEdgeInsets()))
|
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stillImageSize, boundingSize: stillImageSize, intrinsicInsets: UIEdgeInsets()))
|
||||||
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.context.account.postbox, resource: representation.resource, nilIfEmpty: true)
|
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.context.account.postbox, resource: representation.resource, nilIfEmpty: true)
|
||||||
}
|
}
|
||||||
case let .animated(resource, dimensions, _):
|
case let .animated(resource, dimensions, _, _):
|
||||||
imageSize = dimensions.cgSize.aspectFitted(imageBoundingSize)
|
imageSize = dimensions.cgSize.aspectFitted(imageBoundingSize)
|
||||||
|
|
||||||
if fileUpdated {
|
if fileUpdated {
|
||||||
@ -806,7 +806,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
switch thumbnailItem {
|
switch thumbnailItem {
|
||||||
case .still:
|
case .still:
|
||||||
break
|
break
|
||||||
case let .animated(resource, _, isVideo):
|
case let .animated(resource, _, isVideo, isTinted):
|
||||||
let animationNode: AnimatedStickerNode
|
let animationNode: AnimatedStickerNode
|
||||||
if let current = strongSelf.animationNode {
|
if let current = strongSelf.animationNode {
|
||||||
animationNode = current
|
animationNode = current
|
||||||
@ -824,6 +824,15 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
|
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
|
||||||
animationNode.isHidden = !item.playAnimatedStickers
|
animationNode.isHidden = !item.playAnimatedStickers
|
||||||
strongSelf.imageNode.isHidden = item.playAnimatedStickers
|
strongSelf.imageNode.isHidden = item.playAnimatedStickers
|
||||||
|
|
||||||
|
if let animationNode = animationNode as? DefaultAnimatedStickerNodeImpl {
|
||||||
|
if isTinted {
|
||||||
|
animationNode.dynamicColor = item.presentationData.theme.list.itemPrimaryTextColor
|
||||||
|
} else {
|
||||||
|
animationNode.dynamicColor = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let animationNode = strongSelf.animationNode {
|
if let animationNode = strongSelf.animationNode {
|
||||||
animationNode.updateLayout(size: imageFrame.size)
|
animationNode.updateLayout(size: imageFrame.size)
|
||||||
transition.updateFrame(node: animationNode, frame: imageFrame)
|
transition.updateFrame(node: animationNode, frame: imageFrame)
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import PresentationDataUtils
|
|||||||
final class MediaPickerPlaceholderNode: ASDisplayNode {
|
final class MediaPickerPlaceholderNode: ASDisplayNode {
|
||||||
enum Content {
|
enum Content {
|
||||||
case intro(story: Bool)
|
case intro(story: Bool)
|
||||||
case bannedSendMedia(String)
|
case bannedSendMedia(text: String, canBoost: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let content: Content
|
private let content: Content
|
||||||
@ -147,7 +147,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
|||||||
case let .intro(story):
|
case let .intro(story):
|
||||||
title = strings.Attachment_MediaAccessTitle
|
title = strings.Attachment_MediaAccessTitle
|
||||||
text = story ? strings.Attachment_MediaAccessStoryText : strings.Attachment_MediaAccessText
|
text = story ? strings.Attachment_MediaAccessStoryText : strings.Attachment_MediaAccessText
|
||||||
case let .bannedSendMedia(banDescription):
|
case let .bannedSendMedia(banDescription, _):
|
||||||
title = ""
|
title = ""
|
||||||
text = banDescription
|
text = banDescription
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1308,6 +1308,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
if let (untilDate, personal) = bannedSendMedia {
|
if let (untilDate, personal) = bannedSendMedia {
|
||||||
self.gridNode.isHidden = true
|
self.gridNode.isHidden = true
|
||||||
|
self.controller?.titleView.isEnabled = false
|
||||||
|
|
||||||
let banDescription: String
|
let banDescription: String
|
||||||
if untilDate != 0 && untilDate != Int32.max {
|
if untilDate != 0 && untilDate != Int32.max {
|
||||||
@ -1323,7 +1324,7 @@ 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(banDescription))
|
placeholderNode = MediaPickerPlaceholderNode(content: .bannedSendMedia(text: banDescription, canBoost: false))
|
||||||
self.containerNode.insertSubnode(placeholderNode, aboveSubnode: self.gridNode)
|
self.containerNode.insertSubnode(placeholderNode, aboveSubnode: self.gridNode)
|
||||||
self.placeholderNode = placeholderNode
|
self.placeholderNode = placeholderNode
|
||||||
|
|
||||||
|
|||||||
@ -677,6 +677,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
|||||||
.direct(.canBanUsers),
|
.direct(.canBanUsers),
|
||||||
.direct(.canInviteUsers),
|
.direct(.canInviteUsers),
|
||||||
.direct(.canPinMessages),
|
.direct(.canPinMessages),
|
||||||
|
.sub(.stories, storiesRelatedFlags),
|
||||||
.direct(.canManageCalls),
|
.direct(.canManageCalls),
|
||||||
.direct(.canBeAnonymous),
|
.direct(.canBeAnonymous),
|
||||||
.direct(.canAddAdmins)
|
.direct(.canAddAdmins)
|
||||||
@ -1230,12 +1231,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var focusItemTag: ItemListItemTag?
|
|
||||||
if let initialParticipant = initialParticipant, case .creator = initialParticipant {
|
|
||||||
focusItemTag = ChannelAdminEntryTag.rank
|
|
||||||
}
|
|
||||||
|
|
||||||
let rightButtonActionImpl = {
|
let rightButtonActionImpl = {
|
||||||
if invite && !state.adminRights {
|
if invite && !state.adminRights {
|
||||||
updateState { current in
|
updateState { current in
|
||||||
@ -1657,7 +1653,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||||
|
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelPeer: channelPeer, adminPeer: adminPeer, adminPresence: adminPresence, initialParticipant: initialParticipant, invite: invite, canEdit: canEdit), style: .blocks, focusItemTag: focusItemTag, ensureVisibleItemTag: nil, emptyStateItem: nil, footerItem: footerItem, animateChanges: true)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelPeer: channelPeer, adminPeer: adminPeer, adminPresence: adminPresence, initialParticipant: initialParticipant, invite: invite, canEdit: canEdit), style: .blocks, focusItemTag: nil, ensureVisibleItemTag: nil, emptyStateItem: nil, footerItem: footerItem, animateChanges: true)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,8 +24,9 @@ private final class ChannelAdminsControllerArguments {
|
|||||||
let addAdmin: () -> Void
|
let addAdmin: () -> Void
|
||||||
let openAdmin: (ChannelParticipant) -> Void
|
let openAdmin: (ChannelParticipant) -> Void
|
||||||
let updateAntiSpamEnabled: (Bool) -> Void
|
let updateAntiSpamEnabled: (Bool) -> Void
|
||||||
|
let updateSignMessagesEnabled: (Bool) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, openRecentActions: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeAdmin: @escaping (EnginePeer.Id) -> Void, addAdmin: @escaping () -> Void, openAdmin: @escaping (ChannelParticipant) -> Void, updateAntiSpamEnabled: @escaping (Bool) -> Void) {
|
init(context: AccountContext, openRecentActions: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeAdmin: @escaping (EnginePeer.Id) -> Void, addAdmin: @escaping () -> Void, openAdmin: @escaping (ChannelParticipant) -> Void, updateAntiSpamEnabled: @escaping (Bool) -> Void, updateSignMessagesEnabled: @escaping (Bool) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.openRecentActions = openRecentActions
|
self.openRecentActions = openRecentActions
|
||||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||||
@ -33,12 +34,14 @@ private final class ChannelAdminsControllerArguments {
|
|||||||
self.addAdmin = addAdmin
|
self.addAdmin = addAdmin
|
||||||
self.openAdmin = openAdmin
|
self.openAdmin = openAdmin
|
||||||
self.updateAntiSpamEnabled = updateAntiSpamEnabled
|
self.updateAntiSpamEnabled = updateAntiSpamEnabled
|
||||||
|
self.updateSignMessagesEnabled = updateSignMessagesEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ChannelAdminsSection: Int32 {
|
private enum ChannelAdminsSection: Int32 {
|
||||||
case administration
|
case administration
|
||||||
case admins
|
case admins
|
||||||
|
case signMessages
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ChannelAdminsEntryStableId: Hashable {
|
private enum ChannelAdminsEntryStableId: Hashable {
|
||||||
@ -56,31 +59,40 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
|||||||
case addAdmin(PresentationTheme, String, Bool)
|
case addAdmin(PresentationTheme, String, Bool)
|
||||||
case adminsInfo(PresentationTheme, String)
|
case adminsInfo(PresentationTheme, String)
|
||||||
|
|
||||||
|
case signMessages(PresentationTheme, String, Bool)
|
||||||
|
case signMessagesInfo(PresentationTheme, String)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
case .recentActions, .antiSpam, .antiSpamInfo:
|
case .recentActions, .antiSpam, .antiSpamInfo:
|
||||||
return ChannelAdminsSection.administration.rawValue
|
return ChannelAdminsSection.administration.rawValue
|
||||||
case .adminsHeader, .adminPeerItem, .addAdmin, .adminsInfo:
|
case .adminsHeader, .adminPeerItem, .addAdmin, .adminsInfo:
|
||||||
return ChannelAdminsSection.admins.rawValue
|
return ChannelAdminsSection.admins.rawValue
|
||||||
|
case .signMessages, .signMessagesInfo:
|
||||||
|
return ChannelAdminsSection.signMessages.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stableId: ChannelAdminsEntryStableId {
|
var stableId: ChannelAdminsEntryStableId {
|
||||||
switch self {
|
switch self {
|
||||||
case .recentActions:
|
case .recentActions:
|
||||||
return .index(0)
|
return .index(0)
|
||||||
case .antiSpam:
|
case .antiSpam:
|
||||||
return .index(1)
|
return .index(1)
|
||||||
case .antiSpamInfo:
|
case .antiSpamInfo:
|
||||||
return .index(2)
|
return .index(2)
|
||||||
case .adminsHeader:
|
case .adminsHeader:
|
||||||
return .index(3)
|
return .index(3)
|
||||||
case .addAdmin:
|
case .addAdmin:
|
||||||
return .index(4)
|
return .index(4)
|
||||||
case .adminsInfo:
|
case .adminsInfo:
|
||||||
return .index(5)
|
return .index(5)
|
||||||
case let .adminPeerItem(_, _, _, _, _, _, participant, _, _, _):
|
case let .adminPeerItem(_, _, _, _, _, _, participant, _, _, _):
|
||||||
return .peer(participant.peer.id)
|
return .peer(participant.peer.id)
|
||||||
|
case .signMessages:
|
||||||
|
return .index(6)
|
||||||
|
case .signMessagesInfo:
|
||||||
|
return .index(7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +170,18 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .signMessages(lhsTheme, lhsText, lhsValue):
|
||||||
|
if case let .signMessages(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .signMessagesInfo(lhsTheme, lhsText):
|
||||||
|
if case let .signMessagesInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +227,26 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case .adminsInfo:
|
case .adminsInfo:
|
||||||
return false
|
switch rhs {
|
||||||
|
case .recentActions, .antiSpam, .antiSpamInfo, .adminsHeader, .addAdmin, .adminsInfo:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case .signMessages:
|
||||||
|
switch rhs {
|
||||||
|
case .recentActions, .antiSpam, .antiSpamInfo, .adminsHeader, .addAdmin, .adminsInfo, .signMessages:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case .signMessagesInfo:
|
||||||
|
switch rhs {
|
||||||
|
case .recentActions, .antiSpam, .antiSpamInfo, .adminsHeader, .addAdmin, .adminsInfo, .signMessages, .signMessagesInfo:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +302,12 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
case let .adminsInfo(_, text):
|
case let .adminsInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
|
case let .signMessages(_, text, value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
|
arguments.updateSignMessagesEnabled(value)
|
||||||
|
})
|
||||||
|
case let .signMessagesInfo(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,7 +386,7 @@ private struct ChannelAdminsControllerState: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: EnginePeer.Id, peer: EnginePeer?, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamAvailable: Bool, antiSpamEnabled: Bool) -> [ChannelAdminsEntry] {
|
private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: EnginePeer.Id, peer: EnginePeer?, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamAvailable: Bool, antiSpamEnabled: Bool, signMessagesEnabled: Bool) -> [ChannelAdminsEntry] {
|
||||||
if participants == nil || participants?.count == nil {
|
if participants == nil || participants?.count == nil {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -429,6 +478,11 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
|||||||
let info = isGroup ? presentationData.strings.Group_Management_AddModeratorHelp : presentationData.strings.Channel_Management_AddModeratorHelp
|
let info = isGroup ? presentationData.strings.Group_Management_AddModeratorHelp : presentationData.strings.Channel_Management_AddModeratorHelp
|
||||||
entries.append(.adminsInfo(presentationData.theme, info))
|
entries.append(.adminsInfo(presentationData.theme, info))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isGroup && peer.hasPermission(.sendSomething) {
|
||||||
|
entries.append(.signMessages(presentationData.theme, presentationData.strings.Channel_SignMessages, signMessagesEnabled))
|
||||||
|
entries.append(.signMessagesInfo(presentationData.theme, presentationData.strings.Channel_SignMessages_Help))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if case let .legacyGroup(peer) = peer {
|
} else if case let .legacyGroup(peer) = peer {
|
||||||
let isGroup = true
|
let isGroup = true
|
||||||
@ -536,6 +590,9 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
|||||||
let updateAntiSpamDisposable = MetaDisposable()
|
let updateAntiSpamDisposable = MetaDisposable()
|
||||||
actionsDisposable.add(updateAntiSpamDisposable)
|
actionsDisposable.add(updateAntiSpamDisposable)
|
||||||
|
|
||||||
|
let updateSignMessagesDisposable = MetaDisposable()
|
||||||
|
actionsDisposable.add(updateSignMessagesDisposable)
|
||||||
|
|
||||||
let adminsPromise = Promise<[RenderedChannelParticipant]?>(nil)
|
let adminsPromise = Promise<[RenderedChannelParticipant]?>(nil)
|
||||||
|
|
||||||
let antiSpamConfiguration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let antiSpamConfiguration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
@ -732,6 +789,12 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
|||||||
|> deliverOnMainQueue).start(next: { peerId in
|
|> deliverOnMainQueue).start(next: { peerId in
|
||||||
updateAntiSpamDisposable.set(context.engine.peers.toggleAntiSpamProtection(peerId: peerId, enabled: value).start())
|
updateAntiSpamDisposable.set(context.engine.peers.toggleAntiSpamProtection(peerId: peerId, enabled: value).start())
|
||||||
})
|
})
|
||||||
|
}, updateSignMessagesEnabled: { value in
|
||||||
|
let _ = (currentPeerId.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { peerId in
|
||||||
|
updateSignMessagesDisposable.set(context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: peerId, enabled: value).start())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
let membersAndLoadMoreControlValue = Atomic<(Disposable, PeerChannelMemberCategoryControl?)?>(value: nil)
|
let membersAndLoadMoreControlValue = Atomic<(Disposable, PeerChannelMemberCategoryControl?)?>(value: nil)
|
||||||
@ -852,6 +915,11 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
|||||||
antiSpamAvailable = true
|
antiSpamAvailable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var signMessagesEnabled = false
|
||||||
|
if case let .channel(channel) = view.peer, case let .broadcast(info) = channel.info {
|
||||||
|
signMessagesEnabled = info.flags.contains(.messagesShouldHaveSignatures)
|
||||||
|
}
|
||||||
|
|
||||||
var rightNavigationButton: ItemListNavigationButton?
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
var secondaryRightNavigationButton: ItemListNavigationButton?
|
var secondaryRightNavigationButton: ItemListNavigationButton?
|
||||||
if let admins = admins, admins.count > 1 {
|
if let admins = admins, admins.count > 1 {
|
||||||
@ -923,7 +991,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, peer: view.peer, state: state, participants: admins, antiSpamAvailable: antiSpamAvailable, antiSpamEnabled: antiSpamEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, peer: view.peer, state: state, participants: admins, antiSpamAvailable: antiSpamAvailable, antiSpamEnabled: antiSpamEnabled, signMessagesEnabled: signMessagesEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
} |> afterDisposed {
|
} |> afterDisposed {
|
||||||
|
|||||||
@ -134,6 +134,7 @@ final class BadgeLabelView: UIView {
|
|||||||
} else {
|
} else {
|
||||||
itemTransition = transition.withAnimation(.none)
|
itemTransition = transition.withAnimation(.none)
|
||||||
itemView = StackView()
|
itemView = StackView()
|
||||||
|
itemView.color = self.color
|
||||||
self.itemViews[i] = itemView
|
self.itemViews[i] = itemView
|
||||||
self.addSubview(itemView)
|
self.addSubview(itemView)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1051,6 +1051,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
buyActionImpl = { [weak controller] in
|
buyActionImpl = { [weak controller] in
|
||||||
|
let isGroup = isGroupValue.with { $0 }
|
||||||
|
|
||||||
let state = stateValue.with { $0 }
|
let state = stateValue.with { $0 }
|
||||||
|
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
@ -1131,10 +1133,10 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
switch state.mode {
|
switch state.mode {
|
||||||
case .giveaway:
|
case .giveaway:
|
||||||
title = presentationData.strings.BoostGift_GiveawayCreated_Title
|
title = presentationData.strings.BoostGift_GiveawayCreated_Title
|
||||||
text = presentationData.strings.BoostGift_GiveawayCreated_Text
|
text = isGroup ? presentationData.strings.BoostGift_Group_GiveawayCreated_Text : presentationData.strings.BoostGift_GiveawayCreated_Text
|
||||||
case .gift:
|
case .gift:
|
||||||
title = presentationData.strings.BoostGift_PremiumGifted_Title
|
title = presentationData.strings.BoostGift_PremiumGifted_Title
|
||||||
text = presentationData.strings.BoostGift_PremiumGifted_Text
|
text = isGroup ? presentationData.strings.BoostGift_Group_PremiumGifted_Text : presentationData.strings.BoostGift_PremiumGifted_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
|
||||||
@ -1224,11 +1226,11 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
openPeersSelectionImpl = {
|
openPeersSelectionImpl = {
|
||||||
|
let isGroup = isGroupValue.with { $0 }
|
||||||
let state = stateValue.with { $0 }
|
let state = stateValue.with { $0 }
|
||||||
|
|
||||||
let stateContext = ShareWithPeersScreen.StateContext(
|
let stateContext = ShareWithPeersScreen.StateContext(
|
||||||
context: context,
|
context: context,
|
||||||
subject: .members(peerId: peerId, searchQuery: nil),
|
subject: .members(isGroup: isGroup, peerId: peerId, searchQuery: nil),
|
||||||
initialPeerIds: Set(state.peers)
|
initialPeerIds: Set(state.peers)
|
||||||
)
|
)
|
||||||
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).startStandalone(next: { _ in
|
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).startStandalone(next: { _ in
|
||||||
|
|||||||
@ -35,15 +35,6 @@ public func presentGiveawayInfoController(
|
|||||||
let giveaway = message.media.first(where: { $0 is TelegramMediaGiveaway }) as? TelegramMediaGiveaway
|
let giveaway = message.media.first(where: { $0 is TelegramMediaGiveaway }) as? TelegramMediaGiveaway
|
||||||
let giveawayResults = message.media.first(where: { $0 is TelegramMediaGiveawayResults }) as? TelegramMediaGiveawayResults
|
let giveawayResults = message.media.first(where: { $0 is TelegramMediaGiveawayResults }) as? TelegramMediaGiveawayResults
|
||||||
|
|
||||||
// var channelPeerId: EnginePeer.Id?
|
|
||||||
// if let giveaway {
|
|
||||||
// if let peerId = giveaway.channelPeerIds.first {
|
|
||||||
// channelPeerId = peerId
|
|
||||||
// }
|
|
||||||
// } else if let _ = giveawayResults {
|
|
||||||
// channelPeerId = message.author?.id
|
|
||||||
// }
|
|
||||||
|
|
||||||
var quantity: Int32 = 0
|
var quantity: Int32 = 0
|
||||||
if let giveaway {
|
if let giveaway {
|
||||||
quantity = giveaway.quantity
|
quantity = giveaway.quantity
|
||||||
@ -79,21 +70,38 @@ public func presentGiveawayInfoController(
|
|||||||
onlyNewSubscribers = true
|
onlyNewSubscribers = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var channelsCount: Int32 = 1
|
|
||||||
if let giveaway {
|
|
||||||
channelsCount = Int32(giveaway.channelPeerIds.count)
|
|
||||||
} else if let giveawayResults {
|
|
||||||
channelsCount = 1 + giveawayResults.additionalChannelsCount
|
|
||||||
}
|
|
||||||
|
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
|
|
||||||
let author = message.forwardInfo?.author ?? message.author?._asPeer()
|
let author = message.forwardInfo?.author ?? message.author?._asPeer()
|
||||||
var isGroup = false
|
var isGroup = false
|
||||||
if let channel = author as? TelegramChannel, case .group = channel.info {
|
if let channel = author as? TelegramChannel, case .group = channel.info {
|
||||||
isGroup = true
|
isGroup = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var groupsAndChannels = false
|
||||||
|
var channelsCount: Int32 = 1
|
||||||
|
if let giveaway {
|
||||||
|
channelsCount = Int32(giveaway.channelPeerIds.count)
|
||||||
|
|
||||||
|
var channelCount = 0
|
||||||
|
var groupCount = 0
|
||||||
|
for peerId in giveaway.channelPeerIds {
|
||||||
|
if let peer = message.peers[peerId] as? TelegramChannel {
|
||||||
|
switch peer.info {
|
||||||
|
case .broadcast:
|
||||||
|
channelCount += 1
|
||||||
|
case .group:
|
||||||
|
groupCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if groupCount > 0 && channelCount > 0 {
|
||||||
|
groupsAndChannels = true
|
||||||
|
}
|
||||||
|
} else if let giveawayResults {
|
||||||
|
channelsCount = 1 + giveawayResults.additionalChannelsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
var peerName = ""
|
var peerName = ""
|
||||||
if let channel = author as? TelegramChannel {
|
if let channel = author as? TelegramChannel {
|
||||||
peerName = EnginePeer(channel).compactDisplayTitle
|
peerName = EnginePeer(channel).compactDisplayTitle
|
||||||
@ -141,18 +149,43 @@ public func presentGiveawayInfoController(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var otherText: String = ""
|
||||||
|
if channelsCount > 1 {
|
||||||
|
if isGroup {
|
||||||
|
if groupsAndChannels {
|
||||||
|
if channelsCount == 2 {
|
||||||
|
otherText = presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))
|
||||||
|
} else {
|
||||||
|
otherText = presentationData.strings.Chat_Giveaway_Info_OtherGroupsAndChannels(Int32(channelsCount - 1))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
otherText = presentationData.strings.Chat_Giveaway_Info_OtherGroups(Int32(channelsCount - 1))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if groupsAndChannels {
|
||||||
|
if channelsCount == 2 {
|
||||||
|
otherText = presentationData.strings.Chat_Giveaway_Info_OtherGroups(Int32(channelsCount - 1))
|
||||||
|
} else {
|
||||||
|
otherText = presentationData.strings.Chat_Giveaway_Info_OtherChannelsAndGroups(Int32(channelsCount - 1))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
otherText = presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let ending: String
|
let ending: String
|
||||||
if onlyNewSubscribers {
|
if onlyNewSubscribers {
|
||||||
let randomUsers = presentationData.strings.Chat_Giveaway_Info_RandomUsers(quantity)
|
let randomUsers = presentationData.strings.Chat_Giveaway_Info_RandomUsers(quantity)
|
||||||
if channelsCount > 1 {
|
if channelsCount > 1 {
|
||||||
ending = presentationData.strings.Chat_Giveaway_Info_OngoingNewMany(untilDate, randomUsers, peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1)), startDate).string
|
ending = presentationData.strings.Chat_Giveaway_Info_OngoingNewMany(untilDate, randomUsers, peerName, otherText, startDate).string
|
||||||
} else {
|
} else {
|
||||||
ending = presentationData.strings.Chat_Giveaway_Info_OngoingNew(untilDate, randomUsers, peerName, startDate).string
|
ending = presentationData.strings.Chat_Giveaway_Info_OngoingNew(untilDate, randomUsers, peerName, startDate).string
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let randomSubscribers = isGroup ? presentationData.strings.Chat_Giveaway_Info_Group_RandomMembers(quantity) : presentationData.strings.Chat_Giveaway_Info_RandomSubscribers(quantity)
|
let randomSubscribers = isGroup ? presentationData.strings.Chat_Giveaway_Info_Group_RandomMembers(quantity) : presentationData.strings.Chat_Giveaway_Info_RandomSubscribers(quantity)
|
||||||
if channelsCount > 1 {
|
if channelsCount > 1 {
|
||||||
ending = presentationData.strings.Chat_Giveaway_Info_OngoingMany(untilDate, randomSubscribers, peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))).string
|
ending = presentationData.strings.Chat_Giveaway_Info_OngoingMany(untilDate, randomSubscribers, peerName, otherText).string
|
||||||
} else {
|
} else {
|
||||||
ending = presentationData.strings.Chat_Giveaway_Info_Ongoing(untilDate, randomSubscribers, peerName).string
|
ending = presentationData.strings.Chat_Giveaway_Info_Ongoing(untilDate, randomSubscribers, peerName).string
|
||||||
}
|
}
|
||||||
@ -162,7 +195,7 @@ public func presentGiveawayInfoController(
|
|||||||
switch status {
|
switch status {
|
||||||
case .notQualified:
|
case .notQualified:
|
||||||
if channelsCount > 1 {
|
if channelsCount > 1 {
|
||||||
participation = presentationData.strings.Chat_Giveaway_Info_NotQualifiedMany(peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1)), untilDate).string
|
participation = presentationData.strings.Chat_Giveaway_Info_NotQualifiedMany(peerName, otherText, untilDate).string
|
||||||
} else {
|
} else {
|
||||||
participation = presentationData.strings.Chat_Giveaway_Info_NotQualified(peerName, untilDate).string
|
participation = presentationData.strings.Chat_Giveaway_Info_NotQualified(peerName, untilDate).string
|
||||||
}
|
}
|
||||||
@ -173,16 +206,20 @@ public func presentGiveawayInfoController(
|
|||||||
participation = presentationData.strings.Chat_Giveaway_Info_NotAllowedJoinedEarly(joinDate).string
|
participation = presentationData.strings.Chat_Giveaway_Info_NotAllowedJoinedEarly(joinDate).string
|
||||||
case let .channelAdmin(adminId):
|
case let .channelAdmin(adminId):
|
||||||
var channelName = peerName
|
var channelName = peerName
|
||||||
|
var isGroup = false
|
||||||
if let maybePeer = peerMap[adminId], let peer = maybePeer {
|
if let maybePeer = peerMap[adminId], let peer = maybePeer {
|
||||||
channelName = peer.compactDisplayTitle
|
channelName = peer.compactDisplayTitle
|
||||||
|
if case let .channel(channel) = peer, case .group = channel.info {
|
||||||
|
isGroup = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
participation = presentationData.strings.Chat_Giveaway_Info_NotAllowedAdmin(channelName).string
|
participation = isGroup ? presentationData.strings.Chat_Giveaway_Info_NotAllowedAdminGroup(channelName).string : presentationData.strings.Chat_Giveaway_Info_NotAllowedAdmin(channelName).string
|
||||||
case .disallowedCountry:
|
case .disallowedCountry:
|
||||||
participation = presentationData.strings.Chat_Giveaway_Info_NotAllowedCountry
|
participation = presentationData.strings.Chat_Giveaway_Info_NotAllowedCountry
|
||||||
}
|
}
|
||||||
case .participating:
|
case .participating:
|
||||||
if channelsCount > 1 {
|
if channelsCount > 1 {
|
||||||
participation = presentationData.strings.Chat_Giveaway_Info_ParticipatingMany(peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))).string
|
participation = presentationData.strings.Chat_Giveaway_Info_ParticipatingMany(peerName, otherText).string
|
||||||
} else {
|
} else {
|
||||||
participation = presentationData.strings.Chat_Giveaway_Info_Participating(peerName).string
|
participation = presentationData.strings.Chat_Giveaway_Info_Participating(peerName).string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -564,8 +564,8 @@ private final class SheetContent: CombinedComponent {
|
|||||||
level = Int(boostState.level)
|
level = Int(boostState.level)
|
||||||
boosts = Int(boostState.boosts)
|
boosts = Int(boostState.boosts)
|
||||||
if let nextLevelBoosts = boostState.nextLevelBoosts {
|
if let nextLevelBoosts = boostState.nextLevelBoosts {
|
||||||
remaining = Int(nextLevelBoosts - boostState.boosts)
|
remaining = max(0, Int(nextLevelBoosts - boostState.boosts))
|
||||||
progress = CGFloat(boostState.boosts - boostState.currentLevelBoosts) / CGFloat(nextLevelBoosts - boostState.currentLevelBoosts)
|
progress = max(0.0, min(1.0, CGFloat(boostState.boosts - boostState.currentLevelBoosts) / CGFloat(nextLevelBoosts - boostState.currentLevelBoosts)))
|
||||||
} else {
|
} else {
|
||||||
remaining = nil
|
remaining = nil
|
||||||
progress = 1.0
|
progress = 1.0
|
||||||
@ -575,8 +575,8 @@ private final class SheetContent: CombinedComponent {
|
|||||||
level = status.level
|
level = status.level
|
||||||
boosts = status.boosts
|
boosts = status.boosts
|
||||||
if let nextLevelBoosts = status.nextLevelBoosts {
|
if let nextLevelBoosts = status.nextLevelBoosts {
|
||||||
remaining = nextLevelBoosts - status.boosts
|
remaining = max(0, nextLevelBoosts - status.boosts)
|
||||||
progress = CGFloat(status.boosts - status.currentLevelBoosts) / CGFloat(nextLevelBoosts - status.currentLevelBoosts)
|
progress = max(0.0, min(1.0, CGFloat(status.boosts - status.currentLevelBoosts) / CGFloat(nextLevelBoosts - status.currentLevelBoosts)))
|
||||||
} else {
|
} else {
|
||||||
remaining = nil
|
remaining = nil
|
||||||
progress = 1.0
|
progress = 1.0
|
||||||
@ -589,9 +589,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
progress = 0.0
|
progress = 0.0
|
||||||
myBoostCount = 0
|
myBoostCount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
let badgeText = "\(boosts)"
|
|
||||||
|
|
||||||
var textString = ""
|
var textString = ""
|
||||||
|
|
||||||
var isCurrent = false
|
var isCurrent = false
|
||||||
@ -601,38 +599,39 @@ private final class SheetContent: CombinedComponent {
|
|||||||
var needsSecondParagraph = true
|
var needsSecondParagraph = true
|
||||||
|
|
||||||
if let subject {
|
if let subject {
|
||||||
|
let requiredLevel = subject.requiredLevel(group: isGroup, context: context.component.context, configuration: premiumConfiguration)
|
||||||
|
|
||||||
let storiesString = strings.ChannelBoost_StoriesPerDay(Int32(level) + 1)
|
let storiesString = strings.ChannelBoost_StoriesPerDay(Int32(level) + 1)
|
||||||
let valueString = strings.ChannelBoost_MoreBoosts(Int32(remaining))
|
let valueString = strings.ChannelBoost_MoreBoosts(Int32(remaining))
|
||||||
switch subject {
|
switch subject {
|
||||||
case .stories:
|
case .stories:
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
textString = strings.ChannelBoost_EnableStoriesText(valueString).string
|
textString = isGroup ? strings.GroupBoost_EnableStoriesText(valueString).string : strings.ChannelBoost_EnableStoriesText(valueString).string
|
||||||
} else {
|
} else {
|
||||||
textString = strings.ChannelBoost_IncreaseLimitText(valueString, storiesString).string
|
textString = isGroup ? strings.GroupBoost_IncreaseLimitText(valueString, storiesString).string : strings.ChannelBoost_IncreaseLimitText(valueString, storiesString).string
|
||||||
}
|
}
|
||||||
needsSecondParagraph = false
|
needsSecondParagraph = isGroup
|
||||||
case let .channelReactions(reactionCount):
|
case let .channelReactions(reactionCount):
|
||||||
textString = strings.ChannelBoost_CustomReactionsText("\(reactionCount)", "\(reactionCount)").string
|
textString = strings.ChannelBoost_CustomReactionsText("\(reactionCount)", "\(reactionCount)").string
|
||||||
needsSecondParagraph = false
|
needsSecondParagraph = false
|
||||||
case .nameColors:
|
case .nameColors:
|
||||||
let colorLevel = subject.requiredLevel(group: isGroup, context: context.component.context, configuration: premiumConfiguration)
|
textString = strings.ChannelBoost_EnableNameColorLevelText("\(requiredLevel)").string
|
||||||
textString = strings.ChannelBoost_EnableNameColorLevelText("\(colorLevel)").string
|
|
||||||
case .nameIcon:
|
case .nameIcon:
|
||||||
textString = strings.ChannelBoost_EnableNameIconLevelText("\(premiumConfiguration.minChannelNameIconLevel)").string
|
textString = strings.ChannelBoost_EnableNameIconLevelText("\(requiredLevel)").string
|
||||||
case .profileColors:
|
case .profileColors:
|
||||||
textString = isGroup ? strings.GroupBoost_EnableProfileColorLevelText("\(premiumConfiguration.minChannelProfileColorLevel)").string : strings.ChannelBoost_EnableProfileColorLevelText("\(premiumConfiguration.minChannelProfileColorLevel)").string
|
textString = isGroup ? strings.GroupBoost_EnableProfileColorLevelText("\(requiredLevel)").string : strings.ChannelBoost_EnableProfileColorLevelText("\(requiredLevel)").string
|
||||||
case .profileIcon:
|
case .profileIcon:
|
||||||
textString = isGroup ? strings.GroupBoost_EnableProfileIconLevelText("\(premiumConfiguration.minChannelProfileIconLevel)").string : strings.ChannelBoost_EnableProfileIconLevelText("\(premiumConfiguration.minChannelProfileIconLevel)").string
|
textString = isGroup ? strings.GroupBoost_EnableProfileIconLevelText("\(requiredLevel)").string : strings.ChannelBoost_EnableProfileIconLevelText("\(premiumConfiguration.minChannelProfileIconLevel)").string
|
||||||
case .emojiStatus:
|
case .emojiStatus:
|
||||||
textString = isGroup ? strings.GroupBoost_EnableEmojiStatusLevelText("\(premiumConfiguration.minChannelEmojiStatusLevel)").string : strings.ChannelBoost_EnableEmojiStatusLevelText("\(premiumConfiguration.minChannelEmojiStatusLevel)").string
|
textString = isGroup ? strings.GroupBoost_EnableEmojiStatusLevelText("\(requiredLevel)").string : strings.ChannelBoost_EnableEmojiStatusLevelText("\(requiredLevel)").string
|
||||||
case .wallpaper:
|
case .wallpaper:
|
||||||
textString = isGroup ? strings.GroupBoost_EnableWallpaperLevelText("\(premiumConfiguration.minChannelWallpaperLevel)").string : strings.ChannelBoost_EnableWallpaperLevelText("\(premiumConfiguration.minChannelWallpaperLevel)").string
|
textString = isGroup ? strings.GroupBoost_EnableWallpaperLevelText("\(requiredLevel)").string : strings.ChannelBoost_EnableWallpaperLevelText("\(requiredLevel)").string
|
||||||
case .customWallpaper:
|
case .customWallpaper:
|
||||||
textString = isGroup ? strings.GroupBoost_EnableCustomWallpaperLevelText("\(premiumConfiguration.minChannelCustomWallpaperLevel)").string : strings.ChannelBoost_EnableCustomWallpaperLevelText("\(premiumConfiguration.minChannelCustomWallpaperLevel)").string
|
textString = isGroup ? strings.GroupBoost_EnableCustomWallpaperLevelText("\(requiredLevel)").string : strings.ChannelBoost_EnableCustomWallpaperLevelText("\(requiredLevel)").string
|
||||||
case .audioTranscription:
|
case .audioTranscription:
|
||||||
textString = ""
|
textString = ""
|
||||||
case .emojiPack:
|
case .emojiPack:
|
||||||
textString = strings.GroupBoost_EnableEmojiPackLevelText("\(premiumConfiguration.minGroupEmojiPackLevel)").string
|
textString = strings.GroupBoost_EnableEmojiPackLevelText("\(requiredLevel)").string
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
||||||
@ -797,7 +796,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
activeValue: premiumValue,
|
activeValue: premiumValue,
|
||||||
activeTitleColor: .white,
|
activeTitleColor: .white,
|
||||||
badgeIconName: iconName,
|
badgeIconName: iconName,
|
||||||
badgeText: badgeText,
|
badgeText: "\(boosts)",
|
||||||
badgePosition: progress,
|
badgePosition: progress,
|
||||||
badgeGraphPosition: progress,
|
badgeGraphPosition: progress,
|
||||||
invertProgress: true,
|
invertProgress: true,
|
||||||
@ -918,10 +917,9 @@ private final class SheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
contentSize.height += linkButton.size.height + 16.0
|
contentSize.height += linkButton.size.height + 16.0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let boostButton = boostButton.update(
|
let boostButton = boostButton.update(
|
||||||
component: SolidRoundedButtonComponent(
|
component: SolidRoundedButtonComponent(
|
||||||
title: "Boost",
|
title: strings.ChannelBoost_Boost,
|
||||||
theme: SolidRoundedButtonComponent.Theme(
|
theme: SolidRoundedButtonComponent.Theme(
|
||||||
backgroundColor: .black,
|
backgroundColor: .black,
|
||||||
backgroundColors: buttonGradientColors,
|
backgroundColors: buttonGradientColors,
|
||||||
@ -945,7 +943,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
|
|
||||||
let copyButton = copyButton.update(
|
let copyButton = copyButton.update(
|
||||||
component: SolidRoundedButtonComponent(
|
component: SolidRoundedButtonComponent(
|
||||||
title: "Copy",
|
title: strings.ChannelBoost_Copy,
|
||||||
theme: SolidRoundedButtonComponent.Theme(
|
theme: SolidRoundedButtonComponent.Theme(
|
||||||
backgroundColor: .black,
|
backgroundColor: .black,
|
||||||
backgroundColors: buttonGradientColors,
|
backgroundColors: buttonGradientColors,
|
||||||
@ -1183,10 +1181,15 @@ private final class SheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class BoostLevelsContainerComponent: CombinedComponent {
|
private final class BoostLevelsContainerComponent: CombinedComponent {
|
||||||
|
class ExternalState {
|
||||||
|
var isGroup: Bool = false
|
||||||
|
var contentHeight: CGFloat = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
|
let externalState: ExternalState
|
||||||
let peerId: EnginePeer.Id
|
let peerId: EnginePeer.Id
|
||||||
let mode: PremiumBoostLevelsScreen.Mode
|
let mode: PremiumBoostLevelsScreen.Mode
|
||||||
let status: ChannelBoostStatus?
|
let status: ChannelBoostStatus?
|
||||||
@ -1197,11 +1200,13 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
let openStats: (() -> Void)?
|
let openStats: (() -> Void)?
|
||||||
let openGift: (() -> Void)?
|
let openGift: (() -> Void)?
|
||||||
let openPeer: ((EnginePeer) -> Void)?
|
let openPeer: ((EnginePeer) -> Void)?
|
||||||
|
let updated: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
|
externalState: ExternalState,
|
||||||
peerId: EnginePeer.Id,
|
peerId: EnginePeer.Id,
|
||||||
mode: PremiumBoostLevelsScreen.Mode,
|
mode: PremiumBoostLevelsScreen.Mode,
|
||||||
status: ChannelBoostStatus?,
|
status: ChannelBoostStatus?,
|
||||||
@ -1211,11 +1216,13 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
dismiss: @escaping () -> Void,
|
dismiss: @escaping () -> Void,
|
||||||
openStats: (() -> Void)?,
|
openStats: (() -> Void)?,
|
||||||
openGift: (() -> Void)?,
|
openGift: (() -> Void)?,
|
||||||
openPeer: ((EnginePeer) -> Void)?
|
openPeer: ((EnginePeer) -> Void)?,
|
||||||
|
updated: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
self.externalState = externalState
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.status = status
|
self.status = status
|
||||||
@ -1226,6 +1233,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
self.openStats = openStats
|
self.openStats = openStats
|
||||||
self.openGift = openGift
|
self.openGift = openGift
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
|
self.updated = updated
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: BoostLevelsContainerComponent, rhs: BoostLevelsContainerComponent) -> Bool {
|
static func ==(lhs: BoostLevelsContainerComponent, rhs: BoostLevelsContainerComponent) -> Bool {
|
||||||
@ -1258,7 +1266,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
private var disposable: Disposable?
|
private var disposable: Disposable?
|
||||||
private(set) var peer: EnginePeer?
|
private(set) var peer: EnginePeer?
|
||||||
|
|
||||||
init(context: AccountContext, peerId: EnginePeer.Id) {
|
init(context: AccountContext, peerId: EnginePeer.Id, updated: @escaping () -> Void) {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.disposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
self.disposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
@ -1268,6 +1276,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.updated()
|
self.updated()
|
||||||
|
updated()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1277,7 +1286,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeState() -> State {
|
func makeState() -> State {
|
||||||
return State(context: self.context, peerId: self.peerId)
|
return State(context: self.context, peerId: self.peerId, updated: self.updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var body: Body {
|
static var body: Body {
|
||||||
@ -1289,6 +1298,8 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
let statsButton = Child(Button.self)
|
let statsButton = Child(Button.self)
|
||||||
let closeButton = Child(Button.self)
|
let closeButton = Child(Button.self)
|
||||||
|
|
||||||
|
let externalScrollState = ScrollComponent<Empty>.ExternalState()
|
||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
let state = context.state
|
let state = context.state
|
||||||
|
|
||||||
@ -1309,6 +1320,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let isGroup {
|
if let isGroup {
|
||||||
|
component.externalState.isGroup = isGroup
|
||||||
let scroll = scroll.update(
|
let scroll = scroll.update(
|
||||||
component: ScrollComponent<Empty>(
|
component: ScrollComponent<Empty>(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
@ -1330,6 +1342,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
openPeer: component.openPeer
|
openPeer: component.openPeer
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
externalState: externalScrollState,
|
||||||
contentInsets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0),
|
contentInsets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0),
|
||||||
contentOffsetUpdated: { [weak state] topContentOffset, _ in
|
contentOffsetUpdated: { [weak state] topContentOffset, _ in
|
||||||
state?.topContentOffset = topContentOffset
|
state?.topContentOffset = topContentOffset
|
||||||
@ -1342,6 +1355,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
availableSize: context.availableSize,
|
availableSize: context.availableSize,
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
)
|
)
|
||||||
|
component.externalState.contentHeight = externalScrollState.contentHeight
|
||||||
|
|
||||||
let background = background.update(
|
let background = background.update(
|
||||||
component: Rectangle(color: theme.overallDarkAppearance ? theme.list.blocksBackgroundColor : theme.list.plainBackgroundColor),
|
component: Rectangle(color: theme.overallDarkAppearance ? theme.list.blocksBackgroundColor : theme.list.plainBackgroundColor),
|
||||||
@ -1556,9 +1570,11 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
let footerContainerView: UIView
|
let footerContainerView: UIView
|
||||||
let footerView: ComponentHostView<Empty>
|
let footerView: ComponentHostView<Empty>
|
||||||
|
|
||||||
|
private let containerExternalState = BoostLevelsContainerComponent.ExternalState()
|
||||||
|
|
||||||
private(set) var isExpanded = false
|
private(set) var isExpanded = false
|
||||||
private var panGestureRecognizer: UIPanGestureRecognizer?
|
private var panGestureRecognizer: UIPanGestureRecognizer?
|
||||||
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)?
|
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?)?
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
|
|
||||||
@ -1568,7 +1584,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
init(context: AccountContext, controller: PremiumBoostLevelsScreen) {
|
init(context: AccountContext, controller: PremiumBoostLevelsScreen) {
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
if controller.forceDark {
|
if controller.forceDark {
|
||||||
self.presentationData = self.presentationData.withUpdated(theme: defaultDarkPresentationTheme)
|
self.presentationData = self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
}
|
}
|
||||||
self.presentationData = self.presentationData.withUpdated(theme: self.presentationData.theme.withModalBlocksBackground())
|
self.presentationData = self.presentationData.withUpdated(theme: self.presentationData.theme.withModalBlocksBackground())
|
||||||
|
|
||||||
@ -1624,7 +1640,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
|
|
||||||
self.updatedState.set(.single(InternalBoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: boosts + 1)))
|
self.updatedState.set(.single(InternalBoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: boosts + 1)))
|
||||||
|
|
||||||
if let (replacedBoosts, inChannels) = controller.replacedBoosts {
|
if let (replacedBoosts, sourcePeers) = controller.replacedBoosts {
|
||||||
currentMyBoostCount += 1
|
currentMyBoostCount += 1
|
||||||
|
|
||||||
self.boostState = initialState.displayData(myBoostCount: myBoostCount, currentMyBoostCount: 1)
|
self.boostState = initialState.displayData(myBoostCount: myBoostCount, currentMyBoostCount: 1)
|
||||||
@ -1634,7 +1650,29 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
|
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), presentationData.strings.ReassignBoost_OtherChannels(inChannels)).string, round: false, undoText: nil), elevatedLayout: false, position: .top, action: { _ in return true })
|
|
||||||
|
var groupCount: Int32 = 0
|
||||||
|
var channelCount: Int32 = 0
|
||||||
|
for peer in sourcePeers {
|
||||||
|
if case let .channel(channel) = peer {
|
||||||
|
switch channel.info {
|
||||||
|
case .broadcast:
|
||||||
|
channelCount += 1
|
||||||
|
case .group:
|
||||||
|
groupCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let otherText: String
|
||||||
|
if channelCount > 0 && groupCount == 0 {
|
||||||
|
otherText = presentationData.strings.ReassignBoost_OtherChannels(channelCount)
|
||||||
|
} else if groupCount > 0 && channelCount == 0 {
|
||||||
|
otherText = presentationData.strings.ReassignBoost_OtherGroups(groupCount)
|
||||||
|
} else {
|
||||||
|
otherText = presentationData.strings.ReassignBoost_OtherGroupsAndChannels(Int32(sourcePeers.count))
|
||||||
|
}
|
||||||
|
let text = presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), otherText).string
|
||||||
|
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: text, round: false, undoText: nil), elevatedLayout: false, position: .top, action: { _ in return true })
|
||||||
controller.present(undoController, in: .current)
|
controller.present(undoController, in: .current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1723,31 +1761,10 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
self.currentLayout = layout
|
self.currentLayout = layout
|
||||||
|
|
||||||
self.dim.frame = CGRect(origin: CGPoint(x: 0.0, y: -layout.size.height), size: CGSize(width: layout.size.width, height: layout.size.height * 3.0))
|
self.dim.frame = CGRect(origin: CGPoint(x: 0.0, y: -layout.size.height), size: CGSize(width: layout.size.width, height: layout.size.height * 3.0))
|
||||||
|
|
||||||
var effectiveExpanded = self.isExpanded
|
|
||||||
if case .regular = layout.metrics.widthClass {
|
|
||||||
effectiveExpanded = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let isLandscape = layout.orientation == .landscape
|
let isLandscape = layout.orientation == .landscape
|
||||||
let edgeTopInset = isLandscape ? 0.0 : self.defaultTopInset
|
|
||||||
let topInset: CGFloat
|
|
||||||
if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments {
|
|
||||||
if effectiveExpanded {
|
|
||||||
topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset))
|
|
||||||
} else {
|
|
||||||
topInset = max(0.0, panInitialTopInset + min(0.0, panOffset))
|
|
||||||
}
|
|
||||||
} else if let dismissOffset = self.dismissOffset, !dismissOffset.isZero {
|
|
||||||
topInset = edgeTopInset * dismissOffset
|
|
||||||
} else {
|
|
||||||
topInset = effectiveExpanded ? 0.0 : edgeTopInset
|
|
||||||
}
|
|
||||||
transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size), completion: nil)
|
|
||||||
|
|
||||||
let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / self.defaultTopInset)
|
|
||||||
self.controller?.updateModalStyleOverlayTransitionFactor(modalProgress, transition: transition.containedViewLayoutTransition)
|
|
||||||
|
|
||||||
|
var containerTopInset: CGFloat = 0.0
|
||||||
let clipFrame: CGRect
|
let clipFrame: CGRect
|
||||||
if layout.metrics.widthClass == .compact {
|
if layout.metrics.widthClass == .compact {
|
||||||
self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25)
|
self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25)
|
||||||
@ -1769,7 +1786,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
clipFrame = CGRect(origin: CGPoint(), size: layout.size)
|
clipFrame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
} else {
|
} else {
|
||||||
let coveredByModalTransition: CGFloat = 0.0
|
let coveredByModalTransition: CGFloat = 0.0
|
||||||
var containerTopInset: CGFloat = 10.0
|
containerTopInset = 10.0
|
||||||
if let statusBarHeight = layout.statusBarHeight {
|
if let statusBarHeight = layout.statusBarHeight {
|
||||||
containerTopInset += statusBarHeight
|
containerTopInset += statusBarHeight
|
||||||
}
|
}
|
||||||
@ -1797,19 +1814,45 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
|
|
||||||
transition.setFrame(view: self.containerView, frame: clipFrame)
|
transition.setFrame(view: self.containerView, frame: clipFrame)
|
||||||
|
|
||||||
|
var effectiveExpanded = self.isExpanded
|
||||||
|
if case .regular = layout.metrics.widthClass {
|
||||||
|
effectiveExpanded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updated(transition: transition)
|
||||||
|
|
||||||
|
let contentHeight = self.containerExternalState.contentHeight
|
||||||
|
if contentHeight > 0.0 && contentHeight < 400.0, let view = self.footerView.componentView as? FooterComponent.View {
|
||||||
|
view.backgroundView.alpha = 0.0
|
||||||
|
view.separator.opacity = 0.0
|
||||||
|
}
|
||||||
|
let edgeTopInset = isLandscape ? 0.0 : self.defaultTopInset
|
||||||
|
|
||||||
|
let topInset: CGFloat
|
||||||
|
if let (panInitialTopInset, panOffset, _) = self.panGestureArguments {
|
||||||
|
if effectiveExpanded {
|
||||||
|
topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset))
|
||||||
|
} else {
|
||||||
|
topInset = max(0.0, panInitialTopInset + min(0.0, panOffset))
|
||||||
|
}
|
||||||
|
} else if let dismissOffset = self.dismissOffset, !dismissOffset.isZero {
|
||||||
|
topInset = edgeTopInset * dismissOffset
|
||||||
|
} else {
|
||||||
|
topInset = effectiveExpanded ? 0.0 : edgeTopInset
|
||||||
|
}
|
||||||
|
transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size), completion: nil)
|
||||||
|
|
||||||
var footerHeight: CGFloat = 8.0 + 50.0
|
let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / self.defaultTopInset)
|
||||||
footerHeight += layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : 8.0
|
self.controller?.updateModalStyleOverlayTransitionFactor(modalProgress, transition: transition.containedViewLayoutTransition)
|
||||||
|
|
||||||
|
let footerHeight = self.footerHeight
|
||||||
let convertedFooterFrame = self.view.convert(CGRect(origin: CGPoint(x: clipFrame.minX, y: clipFrame.maxY - footerHeight), size: CGSize(width: clipFrame.width, height: footerHeight)), to: self.containerView)
|
let convertedFooterFrame = self.view.convert(CGRect(origin: CGPoint(x: clipFrame.minX, y: clipFrame.maxY - footerHeight), size: CGSize(width: clipFrame.width, height: footerHeight)), to: self.containerView)
|
||||||
transition.setFrame(view: self.footerContainerView, frame: convertedFooterFrame)
|
transition.setFrame(view: self.footerContainerView, frame: convertedFooterFrame)
|
||||||
|
|
||||||
self.updated(transition: transition)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var boostState: InternalBoostState.DisplayData?
|
private var boostState: InternalBoostState.DisplayData?
|
||||||
func updated(transition: Transition) {
|
func updated(transition: Transition) {
|
||||||
guard let controller = self.controller, let layout = self.currentLayout else {
|
guard let controller = self.controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let contentSize = self.contentView.update(
|
let contentSize = self.contentView.update(
|
||||||
@ -1819,6 +1862,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
context: controller.context,
|
context: controller.context,
|
||||||
theme: self.presentationData.theme,
|
theme: self.presentationData.theme,
|
||||||
strings: self.presentationData.strings,
|
strings: self.presentationData.strings,
|
||||||
|
externalState: self.containerExternalState,
|
||||||
peerId: controller.peerId,
|
peerId: controller.peerId,
|
||||||
mode: controller.mode,
|
mode: controller.mode,
|
||||||
status: controller.status,
|
status: controller.status,
|
||||||
@ -1857,7 +1901,10 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
},
|
},
|
||||||
openStats: controller.openStats,
|
openStats: controller.openStats,
|
||||||
openGift: controller.openGift,
|
openGift: controller.openGift,
|
||||||
openPeer: controller.openPeer
|
openPeer: controller.openPeer,
|
||||||
|
updated: { [weak self] in
|
||||||
|
self?.controller?.requestLayout(transition: .immediate)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
@ -1865,8 +1912,14 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
)
|
)
|
||||||
self.contentView.frame = CGRect(origin: .zero, size: contentSize)
|
self.contentView.frame = CGRect(origin: .zero, size: contentSize)
|
||||||
|
|
||||||
var footerHeight: CGFloat = 8.0 + 50.0
|
let footerHeight = self.footerHeight
|
||||||
footerHeight += layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : 8.0
|
|
||||||
|
let actionTitle: String
|
||||||
|
if self.currentMyBoostCount > 0 {
|
||||||
|
actionTitle = self.presentationData.strings.ChannelBoost_BoostAgain
|
||||||
|
} else {
|
||||||
|
actionTitle = self.containerExternalState.isGroup ? self.presentationData.strings.GroupBoost_BoostGroup : self.presentationData.strings.ChannelBoost_BoostChannel
|
||||||
|
}
|
||||||
|
|
||||||
let footerSize = self.footerView.update(
|
let footerSize = self.footerView.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
@ -1874,7 +1927,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
FooterComponent(
|
FooterComponent(
|
||||||
context: controller.context,
|
context: controller.context,
|
||||||
theme: self.presentationData.theme,
|
theme: self.presentationData.theme,
|
||||||
title: self.currentMyBoostCount > 0 ? "Boost Again" : "Boost Group",
|
title: actionTitle,
|
||||||
action: { [weak self] in
|
action: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -1907,6 +1960,20 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var footerHeight: CGFloat {
|
||||||
|
if let mode = self.controller?.mode, case .owner = mode {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let layout = self.currentLayout else {
|
||||||
|
return 58.0
|
||||||
|
}
|
||||||
|
|
||||||
|
var footerHeight: CGFloat = 8.0 + 50.0
|
||||||
|
footerHeight += layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : 8.0
|
||||||
|
return footerHeight
|
||||||
|
}
|
||||||
|
|
||||||
private var defaultTopInset: CGFloat {
|
private var defaultTopInset: CGFloat {
|
||||||
guard let layout = self.currentLayout else {
|
guard let layout = self.currentLayout else {
|
||||||
return 210.0
|
return 210.0
|
||||||
@ -1916,16 +1983,27 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
let bottomInset: CGFloat = layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : bottomPanelPadding
|
let bottomInset: CGFloat = layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : bottomPanelPadding
|
||||||
let panelHeight: CGFloat = bottomPanelPadding + 50.0 + bottomInset + 28.0
|
let panelHeight: CGFloat = bottomPanelPadding + 50.0 + bottomInset + 28.0
|
||||||
|
|
||||||
return layout.size.height - layout.size.width - 128.0 - panelHeight
|
var defaultTopInset = layout.size.height - layout.size.width - 128.0 - panelHeight
|
||||||
|
|
||||||
|
let containerTopInset = 10.0 + (layout.statusBarHeight ?? 0.0)
|
||||||
|
let contentHeight = self.containerExternalState.contentHeight
|
||||||
|
let footerHeight = self.footerHeight
|
||||||
|
if contentHeight > 0.0 {
|
||||||
|
let delta = (layout.size.height - defaultTopInset - containerTopInset) - contentHeight - footerHeight - 16.0
|
||||||
|
if delta > 0.0 {
|
||||||
|
defaultTopInset += delta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultTopInset
|
||||||
} else {
|
} else {
|
||||||
return 210.0
|
return 210.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func findVerticalScrollView(view: UIView?) -> (UIScrollView, ListView?)? {
|
private func findVerticalScrollView(view: UIView?) -> UIScrollView? {
|
||||||
if let view = view {
|
if let view = view {
|
||||||
if let view = view as? UIScrollView, view.contentSize.height > view.contentSize.width {
|
if let view = view as? UIScrollView, view.contentSize.height > view.contentSize.width {
|
||||||
return (view, nil)
|
return view
|
||||||
}
|
}
|
||||||
return findVerticalScrollView(view: view.superview)
|
return findVerticalScrollView(view: view.superview)
|
||||||
} else {
|
} else {
|
||||||
@ -1946,12 +2024,13 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
let point = recognizer.location(in: self.view)
|
let point = recognizer.location(in: self.view)
|
||||||
let currentHitView = self.hitTest(point, with: nil)
|
let currentHitView = self.hitTest(point, with: nil)
|
||||||
|
|
||||||
var scrollViewAndListNode = self.findVerticalScrollView(view: currentHitView)
|
var scrollView = self.findVerticalScrollView(view: currentHitView)
|
||||||
if scrollViewAndListNode?.0.frame.height == self.frame.width {
|
if scrollView?.frame.height == self.frame.width {
|
||||||
scrollViewAndListNode = nil
|
scrollView = nil
|
||||||
|
}
|
||||||
|
if scrollView?.isDescendant(of: self.view) == false {
|
||||||
|
scrollView = nil
|
||||||
}
|
}
|
||||||
let scrollView = scrollViewAndListNode?.0
|
|
||||||
let listNode = scrollViewAndListNode?.1
|
|
||||||
|
|
||||||
let topInset: CGFloat
|
let topInset: CGFloat
|
||||||
if self.isExpanded {
|
if self.isExpanded {
|
||||||
@ -1960,25 +2039,19 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
topInset = edgeTopInset
|
topInset = edgeTopInset
|
||||||
}
|
}
|
||||||
|
|
||||||
self.panGestureArguments = (topInset, 0.0, scrollView, listNode)
|
self.panGestureArguments = (topInset, 0.0, scrollView)
|
||||||
case .changed:
|
case .changed:
|
||||||
guard let (topInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
|
guard let (topInset, panOffset, scrollView) = self.panGestureArguments else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let visibleContentOffset = listNode?.visibleContentOffset()
|
|
||||||
let contentOffset = scrollView?.contentOffset.y ?? 0.0
|
let contentOffset = scrollView?.contentOffset.y ?? 0.0
|
||||||
|
|
||||||
var translation = recognizer.translation(in: self.view).y
|
var translation = recognizer.translation(in: self.view).y
|
||||||
|
|
||||||
var currentOffset = topInset + translation
|
var currentOffset = topInset + translation
|
||||||
|
|
||||||
let epsilon = 1.0
|
let epsilon = 1.0
|
||||||
if case let .known(value) = visibleContentOffset, value <= epsilon {
|
if let scrollView = scrollView, contentOffset <= -scrollView.contentInset.top + epsilon {
|
||||||
if let scrollView = scrollView {
|
|
||||||
scrollView.bounces = false
|
|
||||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false)
|
|
||||||
}
|
|
||||||
} else if let scrollView = scrollView, contentOffset <= -scrollView.contentInset.top + epsilon {
|
|
||||||
scrollView.bounces = false
|
scrollView.bounces = false
|
||||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
||||||
} else if let scrollView = scrollView {
|
} else if let scrollView = scrollView {
|
||||||
@ -1995,7 +2068,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
translation = max(0.0, translation)
|
translation = max(0.0, translation)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.panGestureArguments = (topInset, translation, scrollView, listNode)
|
self.panGestureArguments = (topInset, translation, scrollView)
|
||||||
|
|
||||||
if !self.isExpanded {
|
if !self.isExpanded {
|
||||||
if currentOffset > 0.0, let scrollView = scrollView {
|
if currentOffset > 0.0, let scrollView = scrollView {
|
||||||
@ -2014,23 +2087,18 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
|
|
||||||
self.containerLayoutUpdated(layout: layout, transition: .immediate)
|
self.containerLayoutUpdated(layout: layout, transition: .immediate)
|
||||||
case .ended:
|
case .ended:
|
||||||
guard let (currentTopInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
|
guard let (currentTopInset, panOffset, scrollView) = self.panGestureArguments else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.panGestureArguments = nil
|
self.panGestureArguments = nil
|
||||||
|
|
||||||
let visibleContentOffset = listNode?.visibleContentOffset()
|
|
||||||
let contentOffset = scrollView?.contentOffset.y ?? 0.0
|
let contentOffset = scrollView?.contentOffset.y ?? 0.0
|
||||||
|
|
||||||
let translation = recognizer.translation(in: self.view).y
|
let translation = recognizer.translation(in: self.view).y
|
||||||
var velocity = recognizer.velocity(in: self.view)
|
var velocity = recognizer.velocity(in: self.view)
|
||||||
|
|
||||||
if self.isExpanded {
|
if self.isExpanded {
|
||||||
if case let .known(value) = visibleContentOffset, value > 0.1 {
|
if contentOffset > 0.1 {
|
||||||
velocity = CGPoint()
|
|
||||||
} else if case .unknown = visibleContentOffset {
|
|
||||||
velocity = CGPoint()
|
|
||||||
} else if contentOffset > 0.1 {
|
|
||||||
velocity = CGPoint()
|
velocity = CGPoint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2055,9 +2123,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
} else if self.isExpanded {
|
} else if self.isExpanded {
|
||||||
if velocity.y > 300.0 || offset > topInset / 2.0 {
|
if velocity.y > 300.0 || offset > topInset / 2.0 {
|
||||||
self.isExpanded = false
|
self.isExpanded = false
|
||||||
if let listNode = listNode {
|
if let scrollView = scrollView {
|
||||||
listNode.scroller.setContentOffset(CGPoint(), animated: false)
|
|
||||||
} else if let scrollView = scrollView {
|
|
||||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2072,21 +2138,13 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
self.containerLayoutUpdated(layout: layout, transition: Transition(.animated(duration: 0.3, curve: .easeInOut)))
|
self.containerLayoutUpdated(layout: layout, transition: Transition(.animated(duration: 0.3, curve: .easeInOut)))
|
||||||
}
|
}
|
||||||
} else if scrollView != nil, (velocity.y < -300.0 || offset < topInset / 2.0) {
|
} else if scrollView != nil, (velocity.y < -300.0 || offset < topInset / 2.0) {
|
||||||
if velocity.y > -2200.0 && velocity.y < -300.0, let listNode = listNode {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset)
|
let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset)
|
||||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
|
||||||
self.isExpanded = true
|
self.isExpanded = true
|
||||||
|
|
||||||
self.containerLayoutUpdated(layout: layout, transition: Transition(transition))
|
self.containerLayoutUpdated(layout: layout, transition: Transition(transition))
|
||||||
} else {
|
} else {
|
||||||
if let listNode = listNode {
|
if let scrollView = scrollView {
|
||||||
listNode.scroller.setContentOffset(CGPoint(), animated: false)
|
|
||||||
} else if let scrollView = scrollView {
|
|
||||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2188,11 +2246,15 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
|
|
||||||
var dismissReplaceImpl: (() -> Void)?
|
var dismissReplaceImpl: (() -> Void)?
|
||||||
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
|
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
|
||||||
var channelIds = Set<EnginePeer.Id>()
|
var sourcePeerIds = Set<EnginePeer.Id>()
|
||||||
|
var sourcePeers: [EnginePeer] = []
|
||||||
for boost in myBoostStatus.boosts {
|
for boost in myBoostStatus.boosts {
|
||||||
if slots.contains(boost.slot) {
|
if slots.contains(boost.slot) {
|
||||||
if let peer = boost.peer {
|
if let peer = boost.peer {
|
||||||
channelIds.insert(peer.id)
|
if !sourcePeerIds.contains(peer.id) {
|
||||||
|
sourcePeerIds.insert(peer.id)
|
||||||
|
sourcePeers.append(peer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2211,7 +2273,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
mode: mode,
|
mode: mode,
|
||||||
status: status,
|
status: status,
|
||||||
myBoostStatus: myBoostStatus,
|
myBoostStatus: myBoostStatus,
|
||||||
replacedBoosts: (Int32(slots.count), Int32(channelIds.count)),
|
replacedBoosts: (Int32(slots.count), sourcePeers),
|
||||||
openStats: nil, openGift: nil, openPeer: openPeer, forceDark: forceDark
|
openStats: nil, openGift: nil, openPeer: openPeer, forceDark: forceDark
|
||||||
)
|
)
|
||||||
if let navigationController {
|
if let navigationController {
|
||||||
@ -2238,7 +2300,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
sharedContext: context.sharedContext,
|
sharedContext: context.sharedContext,
|
||||||
updatedPresentationData: nil,
|
updatedPresentationData: nil,
|
||||||
title: presentationData.strings.ChannelBoost_Error_BoostTooOftenTitle,
|
title: presentationData.strings.ChannelBoost_Error_BoostTooOftenTitle,
|
||||||
text: presentationData.strings.ChannelBoost_Error_BoostTooOftenText(valueText).string,
|
text: self.containerExternalState.isGroup ? presentationData.strings.GroupBoost_Error_BoostTooOftenText(valueText).string : presentationData.strings.ChannelBoost_Error_BoostTooOftenText(valueText).string,
|
||||||
actions: [
|
actions: [
|
||||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
||||||
],
|
],
|
||||||
@ -2317,7 +2379,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
sharedContext: context.sharedContext,
|
sharedContext: context.sharedContext,
|
||||||
updatedPresentationData: nil,
|
updatedPresentationData: nil,
|
||||||
title: presentationData.strings.ChannelBoost_Error_PremiumNeededTitle,
|
title: presentationData.strings.ChannelBoost_Error_PremiumNeededTitle,
|
||||||
text: presentationData.strings.ChannelBoost_Error_PremiumNeededText,
|
text: self.containerExternalState.isGroup ? presentationData.strings.GroupBoost_Error_PremiumNeededText : presentationData.strings.ChannelBoost_Error_PremiumNeededText,
|
||||||
actions: [
|
actions: [
|
||||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { [weak controller] in
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { [weak controller] in
|
||||||
@ -2362,7 +2424,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
private let mode: Mode
|
private let mode: Mode
|
||||||
private let status: ChannelBoostStatus?
|
private let status: ChannelBoostStatus?
|
||||||
private let myBoostStatus: MyBoostStatus?
|
private let myBoostStatus: MyBoostStatus?
|
||||||
private let replacedBoosts: (Int32, Int32)?
|
private let replacedBoosts: (Int32, [EnginePeer])?
|
||||||
private let openStats: (() -> Void)?
|
private let openStats: (() -> Void)?
|
||||||
private let openGift: (() -> Void)?
|
private let openGift: (() -> Void)?
|
||||||
private let openPeer: ((EnginePeer) -> Void)?
|
private let openPeer: ((EnginePeer) -> Void)?
|
||||||
@ -2378,7 +2440,7 @@ public class PremiumBoostLevelsScreen: ViewController {
|
|||||||
mode: Mode,
|
mode: Mode,
|
||||||
status: ChannelBoostStatus?,
|
status: ChannelBoostStatus?,
|
||||||
myBoostStatus: MyBoostStatus? = nil,
|
myBoostStatus: MyBoostStatus? = nil,
|
||||||
replacedBoosts: (Int32, Int32)? = nil,
|
replacedBoosts: (Int32, [EnginePeer])? = nil,
|
||||||
openStats: (() -> Void)? = nil,
|
openStats: (() -> Void)? = nil,
|
||||||
openGift: (() -> Void)? = nil,
|
openGift: (() -> Void)? = nil,
|
||||||
openPeer: ((EnginePeer) -> Void)? = nil,
|
openPeer: ((EnginePeer) -> Void)? = nil,
|
||||||
@ -2478,8 +2540,8 @@ private final class FooterComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class View: UIView {
|
final class View: UIView {
|
||||||
private let backgroundView: BlurredBackgroundView
|
let backgroundView: BlurredBackgroundView
|
||||||
private let separator = SimpleLayer()
|
let separator = SimpleLayer()
|
||||||
|
|
||||||
private let button = ComponentView<Empty>()
|
private let button = ComponentView<Empty>()
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,12 @@ final class ScrollChildEnvironment: Equatable {
|
|||||||
final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
||||||
typealias EnvironmentType = ChildEnvironment
|
typealias EnvironmentType = ChildEnvironment
|
||||||
|
|
||||||
|
class ExternalState {
|
||||||
|
var contentHeight: CGFloat = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
let content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>
|
let content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>
|
||||||
|
let externalState: ExternalState?
|
||||||
let contentInsets: UIEdgeInsets
|
let contentInsets: UIEdgeInsets
|
||||||
let contentOffsetUpdated: (_ top: CGFloat, _ bottom: CGFloat) -> Void
|
let contentOffsetUpdated: (_ top: CGFloat, _ bottom: CGFloat) -> Void
|
||||||
let contentOffsetWillCommit: (UnsafeMutablePointer<CGPoint>) -> Void
|
let contentOffsetWillCommit: (UnsafeMutablePointer<CGPoint>) -> Void
|
||||||
@ -30,12 +35,14 @@ final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>,
|
content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>,
|
||||||
|
externalState: ExternalState? = nil,
|
||||||
contentInsets: UIEdgeInsets,
|
contentInsets: UIEdgeInsets,
|
||||||
contentOffsetUpdated: @escaping (_ top: CGFloat, _ bottom: CGFloat) -> Void,
|
contentOffsetUpdated: @escaping (_ top: CGFloat, _ bottom: CGFloat) -> Void,
|
||||||
contentOffsetWillCommit: @escaping (UnsafeMutablePointer<CGPoint>) -> Void,
|
contentOffsetWillCommit: @escaping (UnsafeMutablePointer<CGPoint>) -> Void,
|
||||||
resetScroll: ActionSlot<Void> = ActionSlot()
|
resetScroll: ActionSlot<Void> = ActionSlot()
|
||||||
) {
|
) {
|
||||||
self.content = content
|
self.content = content
|
||||||
|
self.externalState = externalState
|
||||||
self.contentInsets = contentInsets
|
self.contentInsets = contentInsets
|
||||||
self.contentOffsetUpdated = contentOffsetUpdated
|
self.contentOffsetUpdated = contentOffsetUpdated
|
||||||
self.contentOffsetWillCommit = contentOffsetWillCommit
|
self.contentOffsetWillCommit = contentOffsetWillCommit
|
||||||
@ -121,6 +128,7 @@ final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
|||||||
if self.scrollIndicatorInsets != component.contentInsets {
|
if self.scrollIndicatorInsets != component.contentInsets {
|
||||||
self.scrollIndicatorInsets = component.contentInsets
|
self.scrollIndicatorInsets = component.contentInsets
|
||||||
}
|
}
|
||||||
|
component.externalState?.contentHeight = contentSize.height
|
||||||
|
|
||||||
self.component = component
|
self.component = component
|
||||||
|
|
||||||
|
|||||||
@ -279,7 +279,7 @@ final class PremiumStickerPackAccessoryNode: SparseNode, PeekControllerAccessory
|
|||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
self.textNode.textAlignment = .center
|
self.textNode.textAlignment = .center
|
||||||
self.textNode.maximumNumberOfLines = 0
|
self.textNode.maximumNumberOfLines = 0
|
||||||
self.textNode.attributedText = NSAttributedString(string: isEmoji ? strings.Premium_Stickers_Description : strings.Premium_Stickers_Description, font: Font.regular(17.0), textColor: theme.actionSheet.secondaryTextColor)
|
self.textNode.attributedText = NSAttributedString(string: isEmoji ? strings.Premium_Emoji_Description : strings.Premium_Stickers_Description, font: Font.regular(17.0), textColor: theme.actionSheet.secondaryTextColor)
|
||||||
self.textNode.lineSpacing = 0.1
|
self.textNode.lineSpacing = 0.1
|
||||||
|
|
||||||
self.proceedButton = SolidRoundedButtonNode(title: isEmoji ? strings.Premium_Emoji_Proceed: strings.Premium_Stickers_Proceed, theme: SolidRoundedButtonTheme(
|
self.proceedButton = SolidRoundedButtonNode(title: isEmoji ? strings.Premium_Emoji_Proceed: strings.Premium_Stickers_Proceed, theme: SolidRoundedButtonTheme(
|
||||||
|
|||||||
@ -397,7 +397,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[195371015] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowContacts($0) }
|
dict[195371015] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowContacts($0) }
|
||||||
dict[-1877932953] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowUsers($0) }
|
dict[-1877932953] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowUsers($0) }
|
||||||
dict[583071445] = { return Api.InputReplyTo.parse_inputReplyToMessage($0) }
|
dict[583071445] = { return Api.InputReplyTo.parse_inputReplyToMessage($0) }
|
||||||
dict[363917955] = { return Api.InputReplyTo.parse_inputReplyToStory($0) }
|
dict[1484862010] = { return Api.InputReplyTo.parse_inputReplyToStory($0) }
|
||||||
dict[1399317950] = { return Api.InputSecureFile.parse_inputSecureFile($0) }
|
dict[1399317950] = { return Api.InputSecureFile.parse_inputSecureFile($0) }
|
||||||
dict[859091184] = { return Api.InputSecureFile.parse_inputSecureFileUploaded($0) }
|
dict[859091184] = { return Api.InputSecureFile.parse_inputSecureFileUploaded($0) }
|
||||||
dict[-618540889] = { return Api.InputSecureValue.parse_inputSecureValue($0) }
|
dict[-618540889] = { return Api.InputSecureValue.parse_inputSecureValue($0) }
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
public extension Api {
|
public extension Api {
|
||||||
indirect enum InputReplyTo: TypeConstructorDescription {
|
indirect enum InputReplyTo: TypeConstructorDescription {
|
||||||
case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?)
|
case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?)
|
||||||
case inputReplyToStory(userId: Api.InputUser, storyId: Int32)
|
case inputReplyToStory(peer: Api.InputPeer, storyId: Int32)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -21,11 +21,11 @@ public extension Api {
|
|||||||
}}
|
}}
|
||||||
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .inputReplyToStory(let userId, let storyId):
|
case .inputReplyToStory(let peer, let storyId):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(363917955)
|
buffer.appendInt32(1484862010)
|
||||||
}
|
}
|
||||||
userId.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
serializeInt32(storyId, buffer: buffer, boxed: false)
|
serializeInt32(storyId, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -35,8 +35,8 @@ public extension Api {
|
|||||||
switch self {
|
switch self {
|
||||||
case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset):
|
case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset):
|
||||||
return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)])
|
return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)])
|
||||||
case .inputReplyToStory(let userId, let storyId):
|
case .inputReplyToStory(let peer, let storyId):
|
||||||
return ("inputReplyToStory", [("userId", userId as Any), ("storyId", storyId as Any)])
|
return ("inputReplyToStory", [("peer", peer as Any), ("storyId", storyId as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,16 +74,16 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static func parse_inputReplyToStory(_ reader: BufferReader) -> InputReplyTo? {
|
public static func parse_inputReplyToStory(_ reader: BufferReader) -> InputReplyTo? {
|
||||||
var _1: Api.InputUser?
|
var _1: Api.InputPeer?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
_1 = Api.parse(reader, signature: signature) as? Api.InputUser
|
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||||
}
|
}
|
||||||
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.InputReplyTo.inputReplyToStory(userId: _1!, storyId: _2!)
|
return Api.InputReplyTo.inputReplyToStory(peer: _1!, storyId: _2!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -752,7 +752,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
|
|
||||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
|
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
|
||||||
|
|
||||||
var constrainedWidth = size.width / 2.0 - 56.0
|
var constrainedWidth = size.width - leftInset - rightInset - 32.0 - joinButtonSize.width - 60.0
|
||||||
if isScheduled {
|
if isScheduled {
|
||||||
constrainedWidth = size.width - 100.0
|
constrainedWidth = size.width - 100.0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -404,9 +404,9 @@ private func sendUploadedMessageContent(
|
|||||||
}
|
}
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,9 +426,9 @@ private func sendUploadedMessageContent(
|
|||||||
}
|
}
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,9 +462,9 @@ private func sendUploadedMessageContent(
|
|||||||
}
|
}
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,9 +477,9 @@ private func sendUploadedMessageContent(
|
|||||||
let replyFlags: Int32 = 0
|
let replyFlags: Int32 = 0
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
} else {
|
} else {
|
||||||
let replyFlags: Int32 = 0
|
let replyFlags: Int32 = 0
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
@ -627,9 +627,9 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||||||
let replyFlags: Int32 = 0
|
let replyFlags: Int32 = 0
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,9 +645,9 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||||||
let replyFlags: Int32 = 0
|
let replyFlags: Int32 = 0
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -987,9 +987,9 @@ public final class PendingMessageManager {
|
|||||||
|
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1279,9 +1279,9 @@ public final class PendingMessageManager {
|
|||||||
|
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,9 +1344,9 @@ public final class PendingMessageManager {
|
|||||||
|
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1423,9 +1423,9 @@ public final class PendingMessageManager {
|
|||||||
|
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1438,9 +1438,9 @@ public final class PendingMessageManager {
|
|||||||
let replyFlags: Int32 = 0
|
let replyFlags: Int32 = 0
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
} else if let replyToStoryId = replyToStoryId {
|
} else if let replyToStoryId = replyToStoryId {
|
||||||
if let inputUser = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputUser) {
|
if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
replyTo = .inputReplyToStory(userId: inputUser, storyId: replyToStoryId.id)
|
replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id)
|
||||||
} else {
|
} else {
|
||||||
let replyFlags: Int32 = 0
|
let replyFlags: Int32 = 0
|
||||||
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
|
|||||||
@ -43,6 +43,7 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
|
|||||||
public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3)
|
public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3)
|
||||||
public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4)
|
public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4)
|
||||||
public static let isAvailableAsChannelStatus = StickerPackCollectionInfoFlags(rawValue: 1 << 5)
|
public static let isAvailableAsChannelStatus = StickerPackCollectionInfoFlags(rawValue: 1 << 5)
|
||||||
|
public static let isCustomTemplateEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,10 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable {
|
|||||||
.canPinMessages,
|
.canPinMessages,
|
||||||
.canManageCalls,
|
.canManageCalls,
|
||||||
.canBeAnonymous,
|
.canBeAnonymous,
|
||||||
.canAddAdmins
|
.canAddAdmins,
|
||||||
|
.canPostStories,
|
||||||
|
.canEditStories,
|
||||||
|
.canDeleteStories
|
||||||
]
|
]
|
||||||
|
|
||||||
public static let internal_broadcastSpecific: TelegramChatAdminRightsFlags = [
|
public static let internal_broadcastSpecific: TelegramChatAdminRightsFlags = [
|
||||||
|
|||||||
@ -49,6 +49,9 @@ extension StickerPackCollectionInfo {
|
|||||||
if (flags & (1 << 7)) != 0 {
|
if (flags & (1 << 7)) != 0 {
|
||||||
setFlags.insert(.isEmoji)
|
setFlags.insert(.isEmoji)
|
||||||
}
|
}
|
||||||
|
if (flags & (1 << 9)) != 0 {
|
||||||
|
setFlags.insert(.isCustomTemplateEmoji)
|
||||||
|
}
|
||||||
if (flags & (1 << 10)) != 0 {
|
if (flags & (1 << 10)) != 0 {
|
||||||
setFlags.insert(.isAvailableAsChannelStatus)
|
setFlags.insert(.isAvailableAsChannelStatus)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,7 @@ public enum PresentationResourceKey: Int32 {
|
|||||||
case itemListVerifiedPeerIcon
|
case itemListVerifiedPeerIcon
|
||||||
case itemListCloudFetchIcon
|
case itemListCloudFetchIcon
|
||||||
case itemListCloseIconImage
|
case itemListCloseIconImage
|
||||||
|
case itemListRemoveIconImage
|
||||||
case itemListMakeVisibleIcon
|
case itemListMakeVisibleIcon
|
||||||
case itemListMakeInvisibleIcon
|
case itemListMakeInvisibleIcon
|
||||||
case itemListEditThemeIcon
|
case itemListEditThemeIcon
|
||||||
|
|||||||
@ -211,6 +211,24 @@ public struct PresentationResourcesItemList {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func itemListRemoveIconImage(_ theme: PresentationTheme) -> UIImage? {
|
||||||
|
return theme.image(PresentationResourceKey.itemListRemoveIconImage.rawValue, { theme in
|
||||||
|
return generateImage(CGSize(width: 15.0, height: 15.0), contextGenerator: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setBlendMode(.copy)
|
||||||
|
context.setStrokeColor(theme.list.disclosureArrowColor.cgColor)
|
||||||
|
context.setLineWidth(2.0)
|
||||||
|
context.setLineCap(.round)
|
||||||
|
context.move(to: CGPoint(x: 1.0, y: 1.0))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0))
|
||||||
|
context.strokePath()
|
||||||
|
context.move(to: CGPoint(x: size.width - 1.0, y: 1.0))
|
||||||
|
context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0))
|
||||||
|
context.strokePath()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public static func makeVisibleIcon(_ theme: PresentationTheme) -> UIImage? {
|
public static func makeVisibleIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||||
return theme.image(PresentationResourceKey.itemListMakeVisibleIcon.rawValue, { theme in
|
return theme.image(PresentationResourceKey.itemListMakeVisibleIcon.rawValue, { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Contact List/MakeVisibleIcon"), color: theme.list.itemAccentColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Contact List/MakeVisibleIcon"), color: theme.list.itemAccentColor)
|
||||||
|
|||||||
@ -900,21 +900,21 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
}
|
}
|
||||||
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
|
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
|
||||||
case let .setChatWallpaper(_, forBoth):
|
case let .setChatWallpaper(_, forBoth):
|
||||||
|
var isGroup = false
|
||||||
|
let messagePeer = message.peers[message.id.peerId]
|
||||||
|
if let channel = messagePeer as? TelegramChannel, case .group = channel.info {
|
||||||
|
isGroup = true
|
||||||
|
}
|
||||||
if message.author?.id == accountPeerId {
|
if message.author?.id == accountPeerId {
|
||||||
if forBoth {
|
if forBoth {
|
||||||
let peerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
let peerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
||||||
let resultTitleString = strings.Notification_YouChangedWallpaperBoth(peerName)
|
let resultTitleString = strings.Notification_YouChangedWallpaperBoth(peerName)
|
||||||
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||||
} else {
|
} else {
|
||||||
attributedString = NSAttributedString(string: strings.Notification_YouChangedWallpaper, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: isGroup ? strings.Notification_YouChangedGroupWallpaper : strings.Notification_YouChangedWallpaper, font: titleFont, textColor: primaryTextColor)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if message.id.peerId.isGroupOrChannel {
|
if message.id.peerId.isGroupOrChannel {
|
||||||
var isGroup = false
|
|
||||||
let messagePeer = message.peers[message.id.peerId]
|
|
||||||
if let channel = messagePeer as? TelegramChannel, case .group = channel.info {
|
|
||||||
isGroup = true
|
|
||||||
}
|
|
||||||
attributedString = NSAttributedString(string: isGroup ? strings.Notification_GroupChangedWallpaper : strings.Notification_ChannelChangedWallpaper, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: isGroup ? strings.Notification_GroupChangedWallpaper : strings.Notification_ChannelChangedWallpaper, font: titleFont, textColor: primaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
let resultTitleString = strings.Notification_ChangedWallpaper(compactAuthorName)
|
let resultTitleString = strings.Notification_ChangedWallpaper(compactAuthorName)
|
||||||
|
|||||||
@ -371,13 +371,13 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode,
|
|||||||
if let giveaway {
|
if let giveaway {
|
||||||
if giveaway.flags.contains(.onlyNewSubscribers) {
|
if giveaway.flags.contains(.onlyNewSubscribers) {
|
||||||
if giveaway.channelPeerIds.count > 1 {
|
if giveaway.channelPeerIds.count > 1 {
|
||||||
participantsText = item.presentationData.strings.Chat_Giveaway_Message_ParticipantsNewMany
|
participantsText = isGroup ? item.presentationData.strings.Chat_Giveaway_Message_Group_ParticipantsNewMany : item.presentationData.strings.Chat_Giveaway_Message_ParticipantsNewMany
|
||||||
} else {
|
} else {
|
||||||
participantsText = item.presentationData.strings.Chat_Giveaway_Message_ParticipantsNew
|
participantsText = isGroup ? item.presentationData.strings.Chat_Giveaway_Message_Group_ParticipantsNew : item.presentationData.strings.Chat_Giveaway_Message_ParticipantsNew
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if giveaway.channelPeerIds.count > 1 {
|
if giveaway.channelPeerIds.count > 1 {
|
||||||
participantsText = item.presentationData.strings.Chat_Giveaway_Message_ParticipantsMany
|
participantsText = isGroup ? item.presentationData.strings.Chat_Giveaway_Message_Group_ParticipantsMany : item.presentationData.strings.Chat_Giveaway_Message_ParticipantsMany
|
||||||
} else {
|
} else {
|
||||||
participantsText = isGroup ? item.presentationData.strings.Chat_Giveaway_Message_Group_Participants : item.presentationData.strings.Chat_Giveaway_Message_Participants
|
participantsText = isGroup ? item.presentationData.strings.Chat_Giveaway_Message_Group_Participants : item.presentationData.strings.Chat_Giveaway_Message_Participants
|
||||||
}
|
}
|
||||||
|
|||||||
@ -353,7 +353,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: arguments.context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: arguments.context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
let transcriptionText = self.forcedAudioTranscriptionText ?? transcribedText(message: message)
|
let transcriptionText = self.forcedAudioTranscriptionText ?? transcribedText(message: message)
|
||||||
if transcriptionText == nil {
|
if transcriptionText == nil && !arguments.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost {
|
||||||
if premiumConfiguration.audioTransciptionTrialCount > 0 {
|
if premiumConfiguration.audioTransciptionTrialCount > 0 {
|
||||||
if !arguments.associatedData.isPremium {
|
if !arguments.associatedData.isPremium {
|
||||||
if self.presentAudioTranscriptionTooltip(finished: false) {
|
if self.presentAudioTranscriptionTooltip(finished: false) {
|
||||||
@ -361,7 +361,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
guard arguments.associatedData.isPremium || arguments.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost else {
|
guard arguments.associatedData.isPremium else {
|
||||||
if self.hapticFeedback == nil {
|
if self.hapticFeedback == nil {
|
||||||
self.hapticFeedback = HapticFeedback()
|
self.hapticFeedback = HapticFeedback()
|
||||||
}
|
}
|
||||||
@ -475,7 +475,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
strongSelf.transcribeDisposable?.dispose()
|
strongSelf.transcribeDisposable?.dispose()
|
||||||
strongSelf.transcribeDisposable = nil
|
strongSelf.transcribeDisposable = nil
|
||||||
|
|
||||||
if let arguments = strongSelf.arguments, !arguments.associatedData.isPremium {
|
if let arguments = strongSelf.arguments, !arguments.associatedData.isPremium && !arguments.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost {
|
||||||
Queue.mainQueue().after(0.1, {
|
Queue.mainQueue().after(0.1, {
|
||||||
let _ = strongSelf.presentAudioTranscriptionTooltip(finished: true)
|
let _ = strongSelf.presentAudioTranscriptionTooltip(finished: true)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1807,7 +1807,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
let transcriptionText = transcribedText(message: item.message)
|
let transcriptionText = transcribedText(message: item.message)
|
||||||
if transcriptionText == nil {
|
if transcriptionText == nil && !item.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost {
|
||||||
if premiumConfiguration.audioTransciptionTrialCount > 0 {
|
if premiumConfiguration.audioTransciptionTrialCount > 0 {
|
||||||
if !item.associatedData.isPremium {
|
if !item.associatedData.isPremium {
|
||||||
if self.presentAudioTranscriptionTooltip(finished: false) {
|
if self.presentAudioTranscriptionTooltip(finished: false) {
|
||||||
@ -1815,7 +1815,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
guard item.associatedData.isPremium || item.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost else {
|
guard item.associatedData.isPremium else {
|
||||||
if self.hapticFeedback == nil {
|
if self.hapticFeedback == nil {
|
||||||
self.hapticFeedback = HapticFeedback()
|
self.hapticFeedback = HapticFeedback()
|
||||||
}
|
}
|
||||||
@ -1875,7 +1875,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
strongSelf.transcribeDisposable?.dispose()
|
strongSelf.transcribeDisposable?.dispose()
|
||||||
strongSelf.transcribeDisposable = nil
|
strongSelf.transcribeDisposable = nil
|
||||||
|
|
||||||
if let item = strongSelf.item, !item.associatedData.isPremium {
|
if let item = strongSelf.item, !item.associatedData.isPremium && !item.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost {
|
||||||
Queue.mainQueue().after(0.1, {
|
Queue.mainQueue().after(0.1, {
|
||||||
let _ = strongSelf.presentAudioTranscriptionTooltip(finished: true)
|
let _ = strongSelf.presentAudioTranscriptionTooltip(finished: true)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -265,7 +265,12 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
let fromYou = item.message.author?.id == item.context.account.peerId
|
let fromYou = item.message.author?.id == item.context.account.peerId
|
||||||
let isChannel = item.message.id.peerId.isGroupOrChannel
|
let isGroupOrChannel = item.message.id.peerId.isGroupOrChannel
|
||||||
|
var isGroup = false
|
||||||
|
let messagePeer = item.message.peers[item.message.id.peerId]
|
||||||
|
if let channel = messagePeer as? TelegramChannel, case .group = channel.info {
|
||||||
|
isGroup = true
|
||||||
|
}
|
||||||
|
|
||||||
let peerName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0).compactDisplayTitle } ?? ""
|
let peerName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0).compactDisplayTitle } ?? ""
|
||||||
let text: String
|
let text: String
|
||||||
@ -278,19 +283,14 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
if forBoth {
|
if forBoth {
|
||||||
text = item.presentationData.strings.Notification_YouChangedWallpaperBoth(peerName).string
|
text = item.presentationData.strings.Notification_YouChangedWallpaperBoth(peerName).string
|
||||||
} else {
|
} else {
|
||||||
text = item.presentationData.strings.Notification_YouChangedWallpaper
|
text = isGroup ? item.presentationData.strings.Notification_YouChangedGroupWallpaper : item.presentationData.strings.Notification_YouChangedWallpaper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if item.associatedData.isRecentActions {
|
if item.associatedData.isRecentActions {
|
||||||
let authorName = item.message.author.flatMap { EnginePeer($0).compactDisplayTitle } ?? ""
|
let authorName = item.message.author.flatMap { EnginePeer($0).compactDisplayTitle } ?? ""
|
||||||
text = item.presentationData.strings.Channel_AdminLog_ChannelChangedWallpaper(authorName).string
|
text = item.presentationData.strings.Channel_AdminLog_ChannelChangedWallpaper(authorName).string
|
||||||
} else if item.message.id.peerId.isGroupOrChannel {
|
} else if isGroupOrChannel {
|
||||||
var isGroup = false
|
|
||||||
let messagePeer = item.message.peers[item.message.id.peerId]
|
|
||||||
if let channel = messagePeer as? TelegramChannel, case .group = channel.info {
|
|
||||||
isGroup = true
|
|
||||||
}
|
|
||||||
text = isGroup ? item.presentationData.strings.Notification_GroupChangedWallpaper : item.presentationData.strings.Notification_ChannelChangedWallpaper
|
text = isGroup ? item.presentationData.strings.Notification_GroupChangedWallpaper : item.presentationData.strings.Notification_ChannelChangedWallpaper
|
||||||
} else {
|
} else {
|
||||||
text = item.presentationData.strings.Notification_ChangedWallpaper(peerName).string
|
text = item.presentationData.strings.Notification_ChangedWallpaper(peerName).string
|
||||||
@ -324,15 +324,15 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
if displayTrailingAnimatedDots {
|
if displayTrailingAnimatedDots {
|
||||||
textHeight += subtitleLayout.size.height
|
textHeight += subtitleLayout.size.height
|
||||||
}
|
}
|
||||||
let backgroundSize = CGSize(width: width, height: textHeight + 140.0 + (fromYou || isChannel ? 0.0 : 42.0))
|
let backgroundSize = CGSize(width: width, height: textHeight + 140.0 + (fromYou || isGroupOrChannel ? 0.0 : 42.0))
|
||||||
|
|
||||||
return (backgroundSize.width, { boundingWidth in
|
return (backgroundSize.width, { boundingWidth in
|
||||||
return (backgroundSize, { [weak self] animation, synchronousLoads, _ in
|
return (backgroundSize, { [weak self] animation, synchronousLoads, _ in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
|
||||||
strongSelf.buttonNode.isHidden = fromYou || isChannel
|
strongSelf.buttonNode.isHidden = fromYou || isGroupOrChannel
|
||||||
strongSelf.buttonTitleNode.isHidden = fromYou || isChannel
|
strongSelf.buttonTitleNode.isHidden = fromYou || isGroupOrChannel
|
||||||
|
|
||||||
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: 13.0), size: imageSize)
|
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: 13.0), size: imageSize)
|
||||||
if let media, mediaUpdated {
|
if let media, mediaUpdated {
|
||||||
|
|||||||
@ -1698,11 +1698,15 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
var stickersEnabled = true
|
var stickersEnabled = true
|
||||||
var emojiEnabled = true
|
var emojiEnabled = true
|
||||||
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel {
|
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||||
if peer.hasBannedPermission(.banSendStickers) != nil {
|
if let boostsToUnrestrict = interfaceState.boostsToUnrestrict, boostsToUnrestrict > 0 {
|
||||||
stickersEnabled = false
|
|
||||||
}
|
} else {
|
||||||
if peer.hasBannedPermission(.banSendText) != nil {
|
if peer.hasBannedPermission(.banSendStickers) != nil {
|
||||||
emojiEnabled = false
|
stickersEnabled = false
|
||||||
|
}
|
||||||
|
if peer.hasBannedPermission(.banSendText) != nil {
|
||||||
|
emojiEnabled = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let peer = interfaceState.renderedPeer?.peer as? TelegramGroup {
|
} else if let peer = interfaceState.renderedPeer?.peer as? TelegramGroup {
|
||||||
if peer.hasBannedPermission(.banSendStickers) {
|
if peer.hasBannedPermission(.banSendStickers) {
|
||||||
|
|||||||
@ -10,6 +10,9 @@ import ItemListUI
|
|||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import ActivityIndicator
|
import ActivityIndicator
|
||||||
import StickerResources
|
import StickerResources
|
||||||
|
import AnimatedStickerNode
|
||||||
|
import TelegramAnimatedStickerNode
|
||||||
|
import ShimmerEffect
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
|
||||||
enum GroupStickerPackCurrentItemContent: Equatable {
|
enum GroupStickerPackCurrentItemContent: Equatable {
|
||||||
@ -94,6 +97,9 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
fileprivate let imageNode: TransformImageNode
|
fileprivate let imageNode: TransformImageNode
|
||||||
|
private var animationNode: AnimatedStickerNode?
|
||||||
|
private var placeholderNode: StickerShimmerEffectNode?
|
||||||
|
|
||||||
private let notFoundNode: ASImageNode
|
private let notFoundNode: ASImageNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
private let statusNode: TextNode
|
private let statusNode: TextNode
|
||||||
@ -133,6 +139,9 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
|
self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
|
||||||
|
|
||||||
|
self.placeholderNode = StickerShimmerEffectNode()
|
||||||
|
self.placeholderNode?.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.notFoundNode = ASImageNode()
|
self.notFoundNode = ASImageNode()
|
||||||
self.notFoundNode.isLayerBacked = true
|
self.notFoundNode.isLayerBacked = true
|
||||||
self.notFoundNode.displayWithoutProcessing = true
|
self.notFoundNode.displayWithoutProcessing = true
|
||||||
@ -161,19 +170,79 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
|
if let placeholderNode = self.placeholderNode {
|
||||||
|
self.addSubnode(placeholderNode)
|
||||||
|
}
|
||||||
|
|
||||||
self.addSubnode(self.imageNode)
|
self.addSubnode(self.imageNode)
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.statusNode)
|
self.addSubnode(self.statusNode)
|
||||||
self.addSubnode(self.notFoundNode)
|
self.addSubnode(self.notFoundNode)
|
||||||
self.addSubnode(self.activityIndicator)
|
self.addSubnode(self.activityIndicator)
|
||||||
|
|
||||||
|
self.addSubnode(self.removeButtonIcon)
|
||||||
self.addSubnode(self.removeButton)
|
self.addSubnode(self.removeButton)
|
||||||
|
|
||||||
|
var firstTime = true
|
||||||
|
self.imageNode.imageUpdated = { [weak self] image in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if image != nil {
|
||||||
|
strongSelf.removePlaceholder(animated: !firstTime)
|
||||||
|
if firstTime {
|
||||||
|
strongSelf.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstTime = false
|
||||||
|
}
|
||||||
|
|
||||||
|
self.removeButton.addTarget(self, action: #selector(self.removeButtonPressed), forControlEvents: .touchUpInside)
|
||||||
|
self.removeButton.highligthedChanged = { [weak self] highlighted in
|
||||||
|
if let self {
|
||||||
|
if highlighted {
|
||||||
|
self.removeButtonIcon.layer.removeAnimation(forKey: "opacity")
|
||||||
|
self.removeButtonIcon.alpha = 0.4
|
||||||
|
} else {
|
||||||
|
self.removeButtonIcon.alpha = 1.0
|
||||||
|
self.removeButtonIcon.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.fetchDisposable.dispose()
|
self.fetchDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func removeButtonPressed() {
|
||||||
|
self.item?.remove?()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removePlaceholder(animated: Bool) {
|
||||||
|
if let placeholderNode = self.placeholderNode {
|
||||||
|
self.placeholderNode = nil
|
||||||
|
if !animated {
|
||||||
|
placeholderNode.removeFromSupernode()
|
||||||
|
} else {
|
||||||
|
placeholderNode.allowsGroupOpacity = true
|
||||||
|
placeholderNode.alpha = 0.0
|
||||||
|
placeholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak placeholderNode] _ in
|
||||||
|
placeholderNode?.removeFromSupernode()
|
||||||
|
placeholderNode?.allowsGroupOpacity = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var absoluteLocation: (CGRect, CGSize)?
|
||||||
|
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||||
|
self.absoluteLocation = (rect, containerSize)
|
||||||
|
if let placeholderNode = placeholderNode {
|
||||||
|
placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + placeholderNode.frame.minX, y: rect.minY + placeholderNode.frame.minY), size: placeholderNode.frame.size), within: containerSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ item: GroupStickerPackCurrentItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
func asyncLayout() -> (_ item: GroupStickerPackCurrentItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||||
let makeImageLayout = self.imageNode.asyncLayout()
|
let makeImageLayout = self.imageNode.asyncLayout()
|
||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
@ -279,6 +348,14 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.activityIndicator.isHidden = true
|
strongSelf.activityIndicator.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case .found = item.content {
|
||||||
|
strongSelf.removeButtonIcon.isHidden = false
|
||||||
|
strongSelf.removeButton.isHidden = false
|
||||||
|
} else {
|
||||||
|
strongSelf.removeButtonIcon.isHidden = true
|
||||||
|
strongSelf.removeButton.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
let revealOffset = strongSelf.revealOffset
|
let revealOffset = strongSelf.revealOffset
|
||||||
|
|
||||||
let transition: ContainedViewLayoutTransition = .immediate
|
let transition: ContainedViewLayoutTransition = .immediate
|
||||||
@ -300,22 +377,6 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if strongSelf.maskNode.supernode == nil {
|
if strongSelf.maskNode.supernode == nil {
|
||||||
strongSelf.addSubnode(strongSelf.maskNode)
|
strongSelf.addSubnode(strongSelf.maskNode)
|
||||||
}
|
}
|
||||||
// switch neighbors.top {
|
|
||||||
// case .sameSection(false):
|
|
||||||
// strongSelf.topStripeNode.isHidden = true
|
|
||||||
// default:
|
|
||||||
// strongSelf.topStripeNode.isHidden = false
|
|
||||||
// }
|
|
||||||
// let bottomStripeInset: CGFloat
|
|
||||||
// let bottomStripeOffset: CGFloat
|
|
||||||
// switch neighbors.bottom {
|
|
||||||
// case .sameSection(false):
|
|
||||||
// bottomStripeInset = leftInset + editingOffset
|
|
||||||
// bottomStripeOffset = -separatorHeight
|
|
||||||
// default:
|
|
||||||
// bottomStripeInset = 0.0
|
|
||||||
// bottomStripeOffset = 0.0
|
|
||||||
// }
|
|
||||||
|
|
||||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||||
var hasTopCorners = false
|
var hasTopCorners = false
|
||||||
@ -342,6 +403,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
strongSelf.removeButtonIcon.image = PresentationResourcesItemList.itemListRemoveIconImage(item.theme)
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
@ -358,7 +420,6 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
transition.updateFrame(node: strongSelf.statusNode, frame: CGRect(origin: CGPoint(x: leftInset, y: 32.0), size: statusLayout.size))
|
transition.updateFrame(node: strongSelf.statusNode, frame: CGRect(origin: CGPoint(x: leftInset, y: 32.0), size: statusLayout.size))
|
||||||
|
|
||||||
let boundingSize = CGSize(width: 34.0, height: 34.0)
|
let boundingSize = CGSize(width: 34.0, height: 34.0)
|
||||||
transition.updateFrame(node: strongSelf.imageNode, frame: CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0 + floor((boundingSize.width - imageSize.width) / 2.0), y: 11.0 + floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize))
|
|
||||||
let indicatorSize = CGSize(width: 22.0, height: 22.0)
|
let indicatorSize = CGSize(width: 22.0, height: 22.0)
|
||||||
transition.updateFrame(node: strongSelf.activityIndicator, frame: CGRect(origin: CGPoint(x: params.leftInset + 15.0 + floor((boundingSize.width - indicatorSize.width) / 2.0), y: 11.0 + floor((boundingSize.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
transition.updateFrame(node: strongSelf.activityIndicator, frame: CGRect(origin: CGPoint(x: params.leftInset + 15.0 + floor((boundingSize.width - indicatorSize.width) / 2.0), y: 11.0 + floor((boundingSize.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
||||||
|
|
||||||
@ -372,6 +433,13 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if let updatedImageSignal = updatedImageSignal {
|
if let updatedImageSignal = updatedImageSignal {
|
||||||
strongSelf.imageNode.setSignal(updatedImageSignal)
|
strongSelf.imageNode.setSignal(updatedImageSignal)
|
||||||
}
|
}
|
||||||
|
let imageFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0 + floor((boundingSize.width - imageSize.width) / 2.0), y: 11.0 + floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||||
|
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
|
||||||
|
|
||||||
|
strongSelf.removeButton.frame = CGRect(origin: CGPoint(x: layoutSize.width - params.rightInset - layoutSize.height, y: 0.0), size: CGSize(width: layoutSize.height, height: layoutSize.height))
|
||||||
|
if let icon = strongSelf.removeButtonIcon.image {
|
||||||
|
strongSelf.removeButtonIcon.frame = CGRect(origin: CGPoint(x: layoutSize.width - params.rightInset - icon.size.width - 18.0, y: floor((layoutSize.height - icon.size.height) / 2.0)), size: icon.size)
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + UIScreenPixel + UIScreenPixel))
|
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + UIScreenPixel + UIScreenPixel))
|
||||||
|
|
||||||
|
|||||||
@ -282,11 +282,14 @@ private func groupStickerPackSetupControllerEntries(context: AccountContext, pre
|
|||||||
entries.append(.currentPack(0, presentationData.theme, presentationData.strings, .found(packInfo: data.info, topItem: data.item, subtitle: isEmoji ? presentationData.strings.StickerPack_EmojiCount(data.info.count) : presentationData.strings.StickerPack_StickerCount(data.info.count))))
|
entries.append(.currentPack(0, presentationData.theme, presentationData.strings, .found(packInfo: data.info, topItem: data.item, subtitle: isEmoji ? presentationData.strings.StickerPack_EmojiCount(data.info.count) : presentationData.strings.StickerPack_StickerCount(data.info.count))))
|
||||||
}
|
}
|
||||||
entries.append(.searchInfo(presentationData.theme, isEmoji ? presentationData.strings.Group_Emoji_Info : presentationData.strings.Channel_Stickers_CreateYourOwn))
|
entries.append(.searchInfo(presentationData.theme, isEmoji ? presentationData.strings.Group_Emoji_Info : presentationData.strings.Channel_Stickers_CreateYourOwn))
|
||||||
entries.append(.packsTitle(presentationData.theme, isEmoji ? presentationData.strings.Group_Emoji_YourEmoji : presentationData.strings.Channel_Stickers_YourStickers))
|
|
||||||
|
|
||||||
let namespace = isEmoji ? Namespaces.ItemCollection.CloudEmojiPacks : Namespaces.ItemCollection.CloudStickerPacks
|
let namespace = isEmoji ? Namespaces.ItemCollection.CloudEmojiPacks : Namespaces.ItemCollection.CloudStickerPacks
|
||||||
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespace])] as? ItemCollectionInfosView {
|
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespace])] as? ItemCollectionInfosView {
|
||||||
if let packsEntries = stickerPacksView.entriesByNamespace[namespace] {
|
if let packsEntries = stickerPacksView.entriesByNamespace[namespace] {
|
||||||
|
if !packsEntries.isEmpty {
|
||||||
|
entries.append(.packsTitle(presentationData.theme, isEmoji ? presentationData.strings.Group_Emoji_YourEmoji : presentationData.strings.Channel_Stickers_YourStickers))
|
||||||
|
}
|
||||||
|
|
||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
for entry in packsEntries {
|
for entry in packsEntries {
|
||||||
if let info = entry.info as? StickerPackCollectionInfo {
|
if let info = entry.info as? StickerPackCollectionInfo {
|
||||||
|
|||||||
@ -1077,6 +1077,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
if hasSavedMessages, hasSavedMessagesChats, var availablePanesValue = availablePanes {
|
if hasSavedMessages, hasSavedMessagesChats, var availablePanesValue = availablePanes {
|
||||||
if let index = availablePanesValue.firstIndex(of: .media) {
|
if let index = availablePanesValue.firstIndex(of: .media) {
|
||||||
availablePanesValue.insert(.savedMessages, at: index + 1)
|
availablePanesValue.insert(.savedMessages, at: index + 1)
|
||||||
|
} else if let index = availablePanesValue.firstIndex(of: .stories) {
|
||||||
|
availablePanesValue.insert(.savedMessages, at: index + 1)
|
||||||
} else {
|
} else {
|
||||||
availablePanesValue.insert(.savedMessages, at: 0)
|
availablePanesValue.insert(.savedMessages, at: 0)
|
||||||
}
|
}
|
||||||
@ -1240,6 +1242,23 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
let requestsContextPromise = Promise<PeerInvitationImportersContext?>(nil)
|
let requestsContextPromise = Promise<PeerInvitationImportersContext?>(nil)
|
||||||
let requestsStatePromise = Promise<PeerInvitationImportersState?>(nil)
|
let requestsStatePromise = Promise<PeerInvitationImportersState?>(nil)
|
||||||
|
|
||||||
|
let storyListContext: PeerStoryListContext?
|
||||||
|
let hasStories: Signal<Bool?, NoError>
|
||||||
|
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
storyListContext = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false)
|
||||||
|
hasStories = storyListContext!.state
|
||||||
|
|> map { state -> Bool? in
|
||||||
|
if !state.hasCache {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return !state.items.isEmpty
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
} else {
|
||||||
|
storyListContext = nil
|
||||||
|
hasStories = .single(false)
|
||||||
|
}
|
||||||
|
|
||||||
let threadData: Signal<MessageHistoryThreadData?, NoError>
|
let threadData: Signal<MessageHistoryThreadData?, NoError>
|
||||||
if case let .replyThread(message) = chatLocation {
|
if case let .replyThread(message) = chatLocation {
|
||||||
let threadId = message.threadId
|
let threadId = message.threadId
|
||||||
@ -1306,6 +1325,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
invitationsStatePromise.get(),
|
invitationsStatePromise.get(),
|
||||||
requestsContextPromise.get(),
|
requestsContextPromise.get(),
|
||||||
requestsStatePromise.get(),
|
requestsStatePromise.get(),
|
||||||
|
hasStories,
|
||||||
threadData,
|
threadData,
|
||||||
context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]),
|
context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]),
|
||||||
accountIsPremium,
|
accountIsPremium,
|
||||||
@ -1313,12 +1333,12 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
hasSavedMessagesChats,
|
hasSavedMessagesChats,
|
||||||
hasSavedMessageTags
|
hasSavedMessageTags
|
||||||
)
|
)
|
||||||
|> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, 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 -> 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
|
||||||
}
|
}
|
||||||
|
|
||||||
var availablePanes = availablePanes
|
var availablePanes = availablePanes
|
||||||
if let membersData = membersData, case .longList = membersData {
|
if let membersData = membersData, case .longList = membersData {
|
||||||
if availablePanes != nil {
|
if availablePanes != nil {
|
||||||
@ -1328,17 +1348,24 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .peer = chatLocation {
|
if let hasStories {
|
||||||
if hasSavedMessages, hasSavedMessagesChats, var availablePanesValue = availablePanes {
|
if hasStories {
|
||||||
if let index = availablePanesValue.firstIndex(of: .media) {
|
availablePanes?.insert(.stories, at: 0)
|
||||||
availablePanesValue.insert(.savedMessages, at: index + 1)
|
}
|
||||||
} else {
|
if case .peer = chatLocation {
|
||||||
availablePanesValue.insert(.savedMessages, at: 0)
|
if hasSavedMessages, hasSavedMessagesChats, var availablePanesValue = availablePanes {
|
||||||
|
if let index = availablePanesValue.firstIndex(of: .media) {
|
||||||
|
availablePanesValue.insert(.savedMessages, at: index + 1)
|
||||||
|
} else if let index = availablePanesValue.firstIndex(of: .stories) {
|
||||||
|
availablePanesValue.insert(.savedMessages, at: index + 1)
|
||||||
|
} else {
|
||||||
|
availablePanesValue.insert(.savedMessages, at: 0)
|
||||||
|
}
|
||||||
|
availablePanes = availablePanesValue
|
||||||
}
|
}
|
||||||
availablePanes = availablePanesValue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var canManageInvitations = false
|
var canManageInvitations = false
|
||||||
if let group = peerViewMainPeer(peerView) as? TelegramGroup {
|
if let group = peerViewMainPeer(peerView) as? TelegramGroup {
|
||||||
let previousValue = wasUpgradedGroup.swap(group.migrationReference != nil)
|
let previousValue = wasUpgradedGroup.swap(group.migrationReference != nil)
|
||||||
@ -1389,7 +1416,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
groupsInCommon: nil,
|
groupsInCommon: nil,
|
||||||
linkedDiscussionPeer: discussionPeer,
|
linkedDiscussionPeer: discussionPeer,
|
||||||
members: membersData,
|
members: membersData,
|
||||||
storyListContext: nil,
|
storyListContext: storyListContext,
|
||||||
encryptionKeyFingerprint: nil,
|
encryptionKeyFingerprint: nil,
|
||||||
globalSettings: nil,
|
globalSettings: nil,
|
||||||
invitations: invitations,
|
invitations: invitations,
|
||||||
|
|||||||
@ -1594,18 +1594,16 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
|
|||||||
case .broadcast:
|
case .broadcast:
|
||||||
let ItemUsername = 1
|
let ItemUsername = 1
|
||||||
let ItemPeerColor = 2
|
let ItemPeerColor = 2
|
||||||
let ItemInviteLinks = 4
|
let ItemInviteLinks = 3
|
||||||
let ItemDiscussionGroup = 5
|
let ItemDiscussionGroup = 4
|
||||||
let ItemSignMessages = 6
|
let ItemDeleteChannel = 5
|
||||||
let ItemSignMessagesHelp = 7
|
let ItemReactions = 6
|
||||||
let ItemDeleteChannel = 8
|
let ItemAdmins = 7
|
||||||
let ItemReactions = 9
|
let ItemMembers = 8
|
||||||
let ItemAdmins = 10
|
let ItemMemberRequests = 9
|
||||||
let ItemMembers = 11
|
let ItemStats = 10
|
||||||
let ItemMemberRequests = 12
|
let ItemBanned = 11
|
||||||
let ItemStats = 13
|
let ItemRecentActions = 12
|
||||||
let ItemBanned = 14
|
|
||||||
let ItemRecentActions = 15
|
|
||||||
|
|
||||||
let isCreator = channel.flags.contains(.isCreator)
|
let isCreator = channel.flags.contains(.isCreator)
|
||||||
|
|
||||||
@ -1714,20 +1712,6 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if isCreator || (channel.adminRights != nil && channel.hasPermission(.sendSomething)) {
|
|
||||||
let messagesShouldHaveSignatures: Bool
|
|
||||||
switch channel.info {
|
|
||||||
case let .broadcast(info):
|
|
||||||
messagesShouldHaveSignatures = info.flags.contains(.messagesShouldHaveSignatures)
|
|
||||||
default:
|
|
||||||
messagesShouldHaveSignatures = false
|
|
||||||
}
|
|
||||||
items[.peerSettings]!.append(PeerInfoScreenSwitchItem(id: ItemSignMessages, text: presentationData.strings.Channel_SignMessages, value: messagesShouldHaveSignatures, icon: UIImage(bundleImageName: "Chat/Info/GroupSignIcon"), toggled: { value in
|
|
||||||
interaction.editingToggleMessageSignatures(value)
|
|
||||||
}))
|
|
||||||
items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemSignMessagesHelp, text: presentationData.strings.Channel_SignMessages_Help))
|
|
||||||
}
|
|
||||||
|
|
||||||
var canEditMembers = false
|
var canEditMembers = false
|
||||||
if channel.hasPermission(.banMembers) && (channel.adminRights != nil || channel.flags.contains(.isCreator)) {
|
if channel.hasPermission(.banMembers) && (channel.adminRights != nil || channel.flags.contains(.isCreator)) {
|
||||||
canEditMembers = true
|
canEditMembers = true
|
||||||
@ -8667,39 +8651,25 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
case .channelBoostRequired:
|
case .channelBoostRequired:
|
||||||
self.postingAvailabilityDisposable?.dispose()
|
self.postingAvailabilityDisposable?.dispose()
|
||||||
|
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
|
||||||
|
|
||||||
self.postingAvailabilityDisposable = combineLatest(
|
self.postingAvailabilityDisposable = combineLatest(
|
||||||
queue: Queue.mainQueue(),
|
queue: Queue.mainQueue(),
|
||||||
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)),
|
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)),
|
||||||
self.context.engine.peers.getChannelBoostStatus(peerId: self.peerId)
|
self.context.engine.peers.getChannelBoostStatus(peerId: self.peerId),
|
||||||
).startStrict(next: { [weak self] peer, status in
|
self.context.engine.peers.getMyBoostStatus()
|
||||||
guard let self, let peer, let status else {
|
).startStrict(next: { [weak self] peer, boostStatus, myBoostStatus in
|
||||||
|
guard let self, let peer, let boostStatus, let myBoostStatus else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let link = status.url
|
|
||||||
if let navigationController = self.controller?.navigationController as? NavigationController {
|
if let navigationController = self.controller?.navigationController as? NavigationController {
|
||||||
if let previousController = navigationController.viewControllers.last as? ShareWithPeersScreen {
|
if let previousController = navigationController.viewControllers.last as? ShareWithPeersScreen {
|
||||||
previousController.dismiss()
|
previousController.dismiss()
|
||||||
}
|
}
|
||||||
let controller = PremiumLimitScreen(context: self.context, subject: .storiesChannelBoost(peer: peer, boostSubject: .stories, isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, myBoostCount: 0, canBoostAgain: false), count: Int32(status.boosts), action: { [weak self] in
|
let controller = self.context.sharedContext.makePremiumBoostLevelsController(context: self.context, peerId: peer.id, boostStatus: boostStatus, myBoostStatus: myBoostStatus, forceDark: false, openStats: { [weak self] in
|
||||||
UIPasteboard.general.string = link
|
|
||||||
|
|
||||||
if let self {
|
if let self {
|
||||||
self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .linkCopied(text: self.presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
self.openStats(boosts: true, boostStatus: boostStatus)
|
||||||
}
|
}
|
||||||
return true
|
})
|
||||||
}, openStats: { [weak self] in
|
|
||||||
if let self {
|
|
||||||
self.openStats(boosts: true, boostStatus: status)
|
|
||||||
}
|
|
||||||
}, openGift: premiumConfiguration.giveawayGiftsPurchaseAvailable ? { [weak self] in
|
|
||||||
if let self {
|
|
||||||
let controller = createGiveawayController(context: self.context, peerId: self.peerId, subject: .generic)
|
|
||||||
self.controller?.push(controller)
|
|
||||||
}
|
|
||||||
} : nil)
|
|
||||||
navigationController.pushViewController(controller)
|
navigationController.pushViewController(controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10383,7 +10353,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, case .broadcast = channel.info, channel.hasPermission(.postStories) {
|
if let data = self.data, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -541,6 +541,10 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
if self.isApplyingSettings {
|
if self.isApplyingSettings {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if resolvedState.changes.isEmpty {
|
||||||
|
self.environment?.controller()?.dismiss()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let requiredLevel = requiredBoostSubject.requiredLevel(group: self.isGroup, context: component.context, configuration: premiumConfiguration)
|
let requiredLevel = requiredBoostSubject.requiredLevel(group: self.isGroup, context: component.context, configuration: premiumConfiguration)
|
||||||
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
|
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
|
||||||
@ -938,8 +942,12 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
|
|
||||||
var requiredBoostSubjects: [BoostSubject] = [.nameColors(colors: resolvedState.nameColor)]
|
let isGroup = self.isGroup
|
||||||
|
|
||||||
|
var requiredBoostSubjects: [BoostSubject] = []
|
||||||
|
if !isGroup {
|
||||||
|
requiredBoostSubjects.append(.nameColors(colors: resolvedState.nameColor))
|
||||||
|
}
|
||||||
let replyFileId = resolvedState.replyFileId
|
let replyFileId = resolvedState.replyFileId
|
||||||
if replyFileId != nil {
|
if replyFileId != nil {
|
||||||
requiredBoostSubjects.append(.nameIcon)
|
requiredBoostSubjects.append(.nameIcon)
|
||||||
@ -1022,7 +1030,6 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isGroup = false
|
|
||||||
if case let .user(user) = peer {
|
if case let .user(user) = peer {
|
||||||
peer = .user(user
|
peer = .user(user
|
||||||
.withUpdatedNameColor(resolvedState.nameColor)
|
.withUpdatedNameColor(resolvedState.nameColor)
|
||||||
@ -1039,9 +1046,6 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
.withUpdatedBackgroundEmojiId(replyFileId)
|
.withUpdatedBackgroundEmojiId(replyFileId)
|
||||||
.withUpdatedProfileBackgroundEmojiId(backgroundFileId)
|
.withUpdatedProfileBackgroundEmojiId(backgroundFileId)
|
||||||
)
|
)
|
||||||
if case .group = channel.info {
|
|
||||||
isGroup = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let replyIconLevel = Int(BoostSubject.nameIcon.requiredLevel(group: isGroup, context: component.context, configuration: premiumConfiguration))
|
let replyIconLevel = Int(BoostSubject.nameIcon.requiredLevel(group: isGroup, context: component.context, configuration: premiumConfiguration))
|
||||||
|
|||||||
@ -1601,10 +1601,15 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let mode = self.mode, case let .peer(peer, existing) = mode {
|
if let mode = self.mode, case let .peer(peer, existing) = mode {
|
||||||
if case .channel = peer {
|
if case let .channel(channel) = peer {
|
||||||
topMessageText = presentationData.strings.WallpaperPreview_ChannelTopText
|
topMessageText = presentationData.strings.WallpaperPreview_ChannelTopText
|
||||||
bottomMessageText = ""
|
bottomMessageText = ""
|
||||||
serviceMessageText = presentationData.strings.WallpaperPreview_ChannelHeader
|
switch channel.info {
|
||||||
|
case .group:
|
||||||
|
serviceMessageText = presentationData.strings.WallpaperPreview_GroupHeader
|
||||||
|
case .broadcast:
|
||||||
|
serviceMessageText = presentationData.strings.WallpaperPreview_ChannelHeader
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
topMessageText = presentationData.strings.WallpaperPreview_ChatTopText
|
topMessageText = presentationData.strings.WallpaperPreview_ChatTopText
|
||||||
bottomMessageText = presentationData.strings.WallpaperPreview_ChatBottomText
|
bottomMessageText = presentationData.strings.WallpaperPreview_ChatBottomText
|
||||||
|
|||||||
@ -544,8 +544,12 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
case .pin:
|
case .pin:
|
||||||
if self.selectedOptions.contains(.pin) {
|
if self.selectedOptions.contains(.pin) {
|
||||||
animationName = "anim_profileadd"
|
animationName = "anim_profileadd"
|
||||||
if let peerId = self.sendAsPeerId, peerId.namespace != Namespaces.Peer.CloudUser {
|
if let sendAsPeerId = self.sendAsPeerId, sendAsPeerId.isGroupOrChannel {
|
||||||
text = presentationData.strings.Story_Privacy_TooltipStoryArchivedChannel
|
if let selectedPeer = self.effectiveStateValue?.sendAsPeers.first(where: { $0.id == sendAsPeerId }), case let .channel(channel) = selectedPeer, case .group = channel.info {
|
||||||
|
text = presentationData.strings.Story_Privacy_TooltipStoryArchivedGroup
|
||||||
|
} else {
|
||||||
|
text = presentationData.strings.Story_Privacy_TooltipStoryArchivedChannel
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Story_Privacy_TooltipStoryArchived
|
text = presentationData.strings.Story_Privacy_TooltipStoryArchived
|
||||||
}
|
}
|
||||||
@ -837,6 +841,15 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSendAsGroup = false
|
||||||
|
if let sendAsPeerId = self.sendAsPeerId, sendAsPeerId.isGroupOrChannel == true {
|
||||||
|
if let selectedPeer = stateValue.sendAsPeers.first(where: { $0.id == sendAsPeerId }) {
|
||||||
|
if case let .channel(channel) = selectedPeer, case .group = channel.info {
|
||||||
|
isSendAsGroup = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var topOffset = -self.scrollView.bounds.minY + itemLayout.topInset
|
var topOffset = -self.scrollView.bounds.minY + itemLayout.topInset
|
||||||
topOffset = max(0.0, topOffset)
|
topOffset = max(0.0, topOffset)
|
||||||
transition.setTransform(layer: self.backgroundView.layer, transform: CATransform3DMakeTranslation(0.0, topOffset + itemLayout.containerInset, 0.0))
|
transition.setTransform(layer: self.backgroundView.layer, transform: CATransform3DMakeTranslation(0.0, topOffset + itemLayout.containerInset, 0.0))
|
||||||
@ -939,10 +952,10 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
} else if section.id == 2 {
|
} else if section.id == 2 {
|
||||||
sectionTitle = environment.strings.Story_Privacy_WhoCanViewHeader
|
sectionTitle = environment.strings.Story_Privacy_WhoCanViewHeader
|
||||||
} else if section.id == 1 {
|
} else if section.id == 1 {
|
||||||
if case .members = component.stateContext.subject {
|
if case let .members(isGroup, _, _) = component.stateContext.subject {
|
||||||
sectionTitle = environment.strings.BoostGift_Subscribers_SectionTitle
|
sectionTitle = isGroup ? environment.strings.BoostGift_Members_SectionTitle : environment.strings.BoostGift_Subscribers_SectionTitle
|
||||||
} else if case .channels = component.stateContext.subject {
|
} else if case let .channels(isGroup, _, _) = component.stateContext.subject {
|
||||||
sectionTitle = environment.strings.BoostGift_Channels_SectionTitle
|
sectionTitle = isGroup ? environment.strings.BoostGift_GroupsOrChannels_SectionTitle : environment.strings.BoostGift_ChannelsOrGroups_SectionTitle
|
||||||
} else {
|
} else {
|
||||||
sectionTitle = environment.strings.Story_Privacy_ContactsHeader
|
sectionTitle = environment.strings.Story_Privacy_ContactsHeader
|
||||||
}
|
}
|
||||||
@ -1106,35 +1119,20 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
let _ = combineLatest(
|
let _ = combineLatest(
|
||||||
queue: Queue.mainQueue(),
|
queue: Queue.mainQueue(),
|
||||||
component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id)),
|
component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id)),
|
||||||
component.context.engine.peers.getChannelBoostStatus(peerId: peer.id)
|
component.context.engine.peers.getChannelBoostStatus(peerId: peer.id),
|
||||||
).start(next: { [weak self] peer, status in
|
component.context.engine.peers.getMyBoostStatus()
|
||||||
guard let self, let component = self.component, let peer, let status else {
|
).start(next: { [weak self] peer, boostStatus, myBoostStatus in
|
||||||
|
guard let self, let component = self.component, let peer, let boostStatus, let myBoostStatus else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let link = status.url
|
|
||||||
if let navigationController = self.environment?.controller()?.navigationController as? NavigationController {
|
if let navigationController = self.environment?.controller()?.navigationController as? NavigationController {
|
||||||
if let previousController = navigationController.viewControllers.last as? ShareWithPeersScreen {
|
if let previousController = navigationController.viewControllers.last as? ShareWithPeersScreen {
|
||||||
previousController.dismiss()
|
previousController.dismiss()
|
||||||
}
|
}
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
let controller = component.context.sharedContext.makePremiumBoostLevelsController(context: component.context, peerId: peer.id, boostStatus: boostStatus, myBoostStatus: myBoostStatus, forceDark: true, openStats: nil)
|
||||||
let controller = component.context.sharedContext.makePremiumLimitController(context: component.context, subject: .storiesChannelBoost(peer: peer, isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, myBoostCount: 0, canBoostAgain: false), count: Int32(status.boosts), forceDark: true, cancel: {}, action: { [weak navigationController] in
|
|
||||||
UIPasteboard.general.string = link
|
|
||||||
|
|
||||||
if let previousController = navigationController?.viewControllers.reversed().first(where: { $0 is ShareWithPeersScreen}) as? ShareWithPeersScreen {
|
|
||||||
previousController.dismiss(completion: { [weak navigationController] in
|
|
||||||
Queue.mainQueue().justDispatch {
|
|
||||||
if let controller = navigationController?.viewControllers.last as? ViewController {
|
|
||||||
controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: true, position: .top, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
navigationController.pushViewController(controller)
|
navigationController.pushViewController(controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.hapticFeedback.impact(.light)
|
self.hapticFeedback.impact(.light)
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
@ -1494,11 +1492,11 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if case .members = component.stateContext.subject, self.selectedPeers.count >= 10, index == nil {
|
if case let .members(isGroup, _, _) = component.stateContext.subject, self.selectedPeers.count >= 10, index == nil {
|
||||||
self.hapticFeedback.error()
|
self.hapticFeedback.error()
|
||||||
|
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
controller.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: environment.strings.BoostGift_Subscribers_MaximumReached("\(10)").string, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
controller.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: isGroup ? environment.strings.BoostGift_Members_MaximumReached("\(10)").string : environment.strings.BoostGift_Subscribers_MaximumReached("\(10)").string, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
togglePeer()
|
togglePeer()
|
||||||
@ -1541,7 +1539,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
var title = item.title
|
var title = item.title
|
||||||
if item.id == .pin && !hasCategories {
|
if item.id == .pin && !hasCategories {
|
||||||
title = environment.strings.Story_Privacy_KeepOnChannelPage
|
title = isSendAsGroup ? environment.strings.Story_Privacy_KeepOnGroupPage : environment.strings.Story_Privacy_KeepOnChannelPage
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = visibleItem.update(
|
let _ = visibleItem.update(
|
||||||
@ -1595,8 +1593,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
let footerValue = environment.strings.Story_Privacy_KeepOnMyPageHours(Int32(component.timeout / 3600))
|
let footerValue = environment.strings.Story_Privacy_KeepOnMyPageHours(Int32(component.timeout / 3600))
|
||||||
var footerText = environment.strings.Story_Privacy_KeepOnMyPageInfo(footerValue).string
|
var footerText = environment.strings.Story_Privacy_KeepOnMyPageInfo(footerValue).string
|
||||||
|
|
||||||
if self.sendAsPeerId?.isGroupOrChannel == true {
|
if let sendAsPeerId = self.sendAsPeerId, sendAsPeerId.isGroupOrChannel == true {
|
||||||
footerText = environment.strings.Story_Privacy_KeepOnChannelPageInfo(footerValue).string
|
footerText = isSendAsGroup ? environment.strings.Story_Privacy_KeepOnGroupPageInfo(footerValue).string : environment.strings.Story_Privacy_KeepOnChannelPageInfo(footerValue).string
|
||||||
}
|
}
|
||||||
|
|
||||||
let footerSize = sectionFooter.update(
|
let footerSize = sectionFooter.update(
|
||||||
@ -1701,7 +1699,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
if let searchStateContext = self.searchStateContext, let value = searchStateContext.stateValue {
|
if let searchStateContext = self.searchStateContext, let value = searchStateContext.stateValue {
|
||||||
if case let .contactsSearch(query, _) = searchStateContext.subject {
|
if case let .contactsSearch(query, _) = searchStateContext.subject {
|
||||||
searchQuery = query
|
searchQuery = query
|
||||||
} else if case let .members(_, query) = searchStateContext.subject {
|
} else if case let .members(_, _, query) = searchStateContext.subject {
|
||||||
searchQuery = query
|
searchQuery = query
|
||||||
} else if case let .channels(_, _, query) = searchStateContext.subject {
|
} else if case let .channels(_, _, query) = searchStateContext.subject {
|
||||||
searchQuery = query
|
searchQuery = query
|
||||||
@ -2040,10 +2038,10 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
let placeholder: String
|
let placeholder: String
|
||||||
switch component.stateContext.subject {
|
switch component.stateContext.subject {
|
||||||
case .members:
|
case let .members(isGroup, _, _):
|
||||||
placeholder = environment.strings.BoostGift_Subscribers_Search
|
placeholder = isGroup ? environment.strings.BoostGift_Members_Search : environment.strings.BoostGift_Subscribers_Search
|
||||||
case .channels:
|
case let .channels(isGroup, _, _):
|
||||||
placeholder = environment.strings.BoostGift_Channels_Search
|
placeholder = isGroup ? environment.strings.BoostGift_GroupsOrChannels_Search : environment.strings.BoostGift_ChannelsOrGroups_Search
|
||||||
case .chats:
|
case .chats:
|
||||||
placeholder = environment.strings.Story_Privacy_SearchChats
|
placeholder = environment.strings.Story_Privacy_SearchChats
|
||||||
default:
|
default:
|
||||||
@ -2090,8 +2088,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
switch component.stateContext.subject {
|
switch component.stateContext.subject {
|
||||||
case let .channels(isGroup, exclude, _):
|
case let .channels(isGroup, exclude, _):
|
||||||
searchSubject = .channels(isGroup: isGroup, exclude: exclude, searchQuery: searchQuery)
|
searchSubject = .channels(isGroup: isGroup, exclude: exclude, searchQuery: searchQuery)
|
||||||
case let .members(peerId, _):
|
case let .members(isGroup, peerId, _):
|
||||||
searchSubject = .members(peerId: peerId, searchQuery: searchQuery)
|
searchSubject = .members(isGroup: isGroup, peerId: peerId, searchQuery: searchQuery)
|
||||||
default:
|
default:
|
||||||
searchSubject = .contactsSearch(query: searchQuery, onlyContacts: onlyContacts)
|
searchSubject = .contactsSearch(query: searchQuery, onlyContacts: onlyContacts)
|
||||||
}
|
}
|
||||||
@ -2389,9 +2387,9 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
case .contactsSearch:
|
case .contactsSearch:
|
||||||
title = ""
|
title = ""
|
||||||
case .members:
|
case let .members(isGroup, _, _):
|
||||||
title = environment.strings.BoostGift_Subscribers_Title
|
title = environment.strings.BoostGift_Subscribers_Title
|
||||||
subtitle = environment.strings.BoostGift_Subscribers_Subtitle("\(10)").string
|
subtitle = isGroup ? environment.strings.BoostGift_Members_Subtitle("\(10)").string : environment.strings.BoostGift_Subscribers_Subtitle("\(10)").string
|
||||||
actionButtonTitle = environment.strings.BoostGift_Subscribers_Save
|
actionButtonTitle = environment.strings.BoostGift_Subscribers_Save
|
||||||
case let .channels(isGroup, _, _):
|
case let .channels(isGroup, _, _):
|
||||||
title = isGroup ? environment.strings.BoostGift_GroupsOrChannels_Title : environment.strings.BoostGift_ChannelsOrGroups_Title
|
title = isGroup ? environment.strings.BoostGift_GroupsOrChannels_Title : environment.strings.BoostGift_ChannelsOrGroups_Title
|
||||||
|
|||||||
@ -48,7 +48,7 @@ public extension ShareWithPeersScreen {
|
|||||||
case chats(blocked: Bool)
|
case chats(blocked: Bool)
|
||||||
case contacts(base: EngineStoryPrivacy.Base)
|
case contacts(base: EngineStoryPrivacy.Base)
|
||||||
case contactsSearch(query: String, onlyContacts: Bool)
|
case contactsSearch(query: String, onlyContacts: Bool)
|
||||||
case members(peerId: EnginePeer.Id, searchQuery: String?)
|
case members(isGroup: Bool, peerId: EnginePeer.Id, searchQuery: String?)
|
||||||
case channels(isGroup: Bool, exclude: Set<EnginePeer.Id>, searchQuery: String?)
|
case channels(isGroup: Bool, exclude: Set<EnginePeer.Id>, searchQuery: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,7 +515,7 @@ public extension ShareWithPeersScreen {
|
|||||||
|
|
||||||
self.readySubject.set(true)
|
self.readySubject.set(true)
|
||||||
})
|
})
|
||||||
case let .members(peerId, searchQuery):
|
case let .members(_, peerId, searchQuery):
|
||||||
let membersState = Promise<ChannelMemberListState>()
|
let membersState = Promise<ChannelMemberListState>()
|
||||||
let contactsState = Promise<ChannelMemberListState>()
|
let contactsState = Promise<ChannelMemberListState>()
|
||||||
|
|
||||||
|
|||||||
@ -318,7 +318,7 @@ func makeAttachmentFileControllerImpl(context: AccountContext, updatedPresentati
|
|||||||
} else {
|
} else {
|
||||||
banDescription = presentationData.strings.Conversation_DefaultRestrictedMedia
|
banDescription = presentationData.strings.Conversation_DefaultRestrictedMedia
|
||||||
}
|
}
|
||||||
emptyItem = AttachmentFileEmptyStateItem(context: context, theme: presentationData.theme, strings: presentationData.strings, content: .bannedSendMedia(banDescription))
|
emptyItem = AttachmentFileEmptyStateItem(context: context, theme: presentationData.theme, strings: presentationData.strings, content: .bannedSendMedia(text: banDescription, canBoost: false))
|
||||||
} else if let recentDocuments = recentDocuments, recentDocuments.isEmpty {
|
} else if let recentDocuments = recentDocuments, recentDocuments.isEmpty {
|
||||||
emptyItem = AttachmentFileEmptyStateItem(context: context, theme: presentationData.theme, strings: presentationData.strings, content: .intro)
|
emptyItem = AttachmentFileEmptyStateItem(context: context, theme: presentationData.theme, strings: presentationData.strings, content: .intro)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,12 @@ import PresentationDataUtils
|
|||||||
import AnimatedStickerNode
|
import AnimatedStickerNode
|
||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import SolidRoundedButtonNode
|
||||||
|
|
||||||
final class AttachmentFileEmptyStateItem: ItemListControllerEmptyStateItem {
|
final class AttachmentFileEmptyStateItem: ItemListControllerEmptyStateItem {
|
||||||
enum Content: Equatable {
|
enum Content: Equatable {
|
||||||
case intro
|
case intro
|
||||||
case bannedSendMedia(String)
|
case bannedSendMedia(text: String, canBoost: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -48,6 +49,7 @@ final class AttachmentFileEmptyStateItem: ItemListControllerEmptyStateItem {
|
|||||||
final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNode {
|
final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNode {
|
||||||
private var animationNode: AnimatedStickerNode
|
private var animationNode: AnimatedStickerNode
|
||||||
private let textNode: ASTextNode
|
private let textNode: ASTextNode
|
||||||
|
private let buttonNode: SolidRoundedButtonNode
|
||||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
|
|
||||||
var item: AttachmentFileEmptyStateItem {
|
var item: AttachmentFileEmptyStateItem {
|
||||||
@ -62,8 +64,19 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
|
|||||||
init(item: AttachmentFileEmptyStateItem) {
|
init(item: AttachmentFileEmptyStateItem) {
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|
||||||
|
let name: String
|
||||||
|
let playbackMode: AnimatedStickerPlaybackMode
|
||||||
|
switch item.content {
|
||||||
|
case .intro:
|
||||||
|
name = "Files"
|
||||||
|
playbackMode = .loop
|
||||||
|
case .bannedSendMedia:
|
||||||
|
name = "Banned"
|
||||||
|
playbackMode = .once
|
||||||
|
}
|
||||||
|
|
||||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "Files"), width: 320, height: 320, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
||||||
self.animationNode.visibility = true
|
self.animationNode.visibility = true
|
||||||
|
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ASTextNode()
|
||||||
@ -71,6 +84,8 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
|
|||||||
self.textNode.lineSpacing = 0.1
|
self.textNode.lineSpacing = 0.1
|
||||||
self.textNode.textAlignment = .center
|
self.textNode.textAlignment = .center
|
||||||
|
|
||||||
|
self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), height: 50.0, cornerRadius: 11.0, gloss: true)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.isUserInteractionEnabled = false
|
self.isUserInteractionEnabled = false
|
||||||
@ -79,6 +94,10 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
|
|||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
|
|
||||||
self.updateThemeAndStrings(theme: self.item.theme, strings: self.item.strings)
|
self.updateThemeAndStrings(theme: self.item.theme, strings: self.item.strings)
|
||||||
|
|
||||||
|
if case .bannedSendMedia(_, true) = item.content {
|
||||||
|
self.addSubnode(self.buttonNode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
private func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
||||||
@ -86,46 +105,47 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
|
|||||||
switch self.item.content {
|
switch self.item.content {
|
||||||
case .intro:
|
case .intro:
|
||||||
text = strings.Attachment_FilesIntro
|
text = strings.Attachment_FilesIntro
|
||||||
case let .bannedSendMedia(banDescription):
|
case let .bannedSendMedia(banDescription, _):
|
||||||
text = banDescription
|
text = banDescription
|
||||||
}
|
}
|
||||||
self.textNode.attributedText = NSAttributedString(string: text.replacingOccurrences(of: "\n", with: " "), font: Font.regular(15.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center)
|
self.textNode.attributedText = NSAttributedString(string: text.replacingOccurrences(of: "\n", with: " "), font: Font.regular(15.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center)
|
||||||
|
self.buttonNode.title = strings.Attachment_OpenSettings
|
||||||
|
self.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = (layout, navigationBarHeight)
|
self.validLayout = (layout, navigationBarHeight)
|
||||||
var insets = layout.insets(options: [])
|
|
||||||
insets.top += navigationBarHeight
|
|
||||||
|
|
||||||
let imageSpacing: CGFloat = 12.0
|
|
||||||
|
|
||||||
var imageSize = CGSize(width: 144.0, height: 144.0)
|
var imageSize = CGSize(width: 144.0, height: 144.0)
|
||||||
|
var insets = layout.insets(options: [])
|
||||||
if layout.size.width == 320.0 {
|
if layout.size.width == 320.0 {
|
||||||
|
insets.top += -60.0
|
||||||
imageSize = CGSize(width: 112.0, height: 112.0)
|
imageSize = CGSize(width: 112.0, height: 112.0)
|
||||||
|
} else {
|
||||||
|
insets.top += -160.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let imageSpacing: CGFloat = 12.0
|
||||||
|
let textSpacing: CGFloat = 12.0
|
||||||
|
let buttonSpacing: CGFloat = 15.0
|
||||||
|
let bottomSpacing: CGFloat = 33.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
|
||||||
if !imageHeight.isZero {
|
|
||||||
if case .intro = self.item.content {
|
|
||||||
insets.top -= 92.0
|
|
||||||
} else {
|
|
||||||
insets.top -= 160.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if layout.size.width == 320.0 {
|
let buttonWidth: CGFloat = 248.0
|
||||||
insets.top += 110.0
|
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
||||||
}
|
|
||||||
|
|
||||||
let textSize = self.textNode.measure(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 70.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
|
|
||||||
|
|
||||||
let totalHeight = imageHeight + textSize.height
|
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 40.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
|
||||||
|
|
||||||
|
let totalHeight = imageHeight + textSpacing + textSize.height + buttonSpacing + buttonHeight + bottomSpacing
|
||||||
let topOffset = insets.top + floor((layout.size.height - insets.top - insets.bottom - totalHeight) / 2.0)
|
let topOffset = insets.top + floor((layout.size.height - insets.top - insets.bottom - totalHeight) / 2.0)
|
||||||
|
|
||||||
transition.updateAlpha(node: self.animationNode, alpha: imageHeight > 0.0 ? 1.0 : 0.0)
|
transition.updateAlpha(node: self.animationNode, alpha: imageHeight > 0.0 ? 1.0 : 0.0)
|
||||||
transition.updateFrame(node: self.animationNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - imageSize.width) / 2.0), y: topOffset), size: imageSize))
|
transition.updateFrame(node: self.animationNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - imageSize.width) / 2.0), y: topOffset), size: imageSize))
|
||||||
self.animationNode.updateLayout(size: imageSize)
|
self.animationNode.updateLayout(size: imageSize)
|
||||||
|
|
||||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - textSize.width - layout.safeInsets.left - layout.safeInsets.right) / 2.0), y: topOffset + imageHeight), size: textSize))
|
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - textSize.width - layout.safeInsets.left - layout.safeInsets.right) / 2.0), y: topOffset + imageHeight + textSpacing), size: textSize))
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - buttonWidth - layout.safeInsets.left - layout.safeInsets.right) / 2.0), y: self.textNode.frame.maxY + buttonSpacing), size: CGSize(width: buttonWidth, height: buttonHeight)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -120,6 +120,7 @@ import VideoMessageCameraScreen
|
|||||||
import TopMessageReactions
|
import TopMessageReactions
|
||||||
import PeerInfoScreen
|
import PeerInfoScreen
|
||||||
import AudioWaveform
|
import AudioWaveform
|
||||||
|
import PeerNameColorScreen
|
||||||
|
|
||||||
public enum ChatControllerPeekActions {
|
public enum ChatControllerPeekActions {
|
||||||
case standard
|
case standard
|
||||||
@ -961,11 +962,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.presentThemeSelection()
|
strongSelf.presentThemeSelection()
|
||||||
return true
|
return true
|
||||||
case let .setChatWallpaper(wallpaper, _):
|
case let .setChatWallpaper(wallpaper, _):
|
||||||
guard message.effectivelyIncoming(strongSelf.context.account.peerId), let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
strongSelf.presentThemeSelection()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if peer is TelegramChannel {
|
if let peer = peer as? TelegramChannel, peer.hasPermission(.changeInfo) {
|
||||||
|
let _ = (context.engine.peers.getChannelBoostStatus(peerId: peer.id)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] boostStatus in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peer.id, boostStatus: boostStatus))
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
guard message.effectivelyIncoming(strongSelf.context.account.peerId), let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
|
strongSelf.presentThemeSelection()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
strongSelf.chatDisplayNode.dismissInput()
|
||||||
@ -1692,6 +1703,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, peer.hasBannedPermission(.banSendStickers) != nil {
|
||||||
|
if let boostsToUnrestrict = strongSelf.presentationInterfaceState.boostsToUnrestrict, boostsToUnrestrict > 0, (strongSelf.presentationInterfaceState.appliedBoosts ?? 0) < boostsToUnrestrict {
|
||||||
|
strongSelf.interfaceInteraction?.openBoostToUnrestrict()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
if let query = query {
|
if let query = query {
|
||||||
attributes.append(EmojiSearchQueryMessageAttribute(query: query))
|
attributes.append(EmojiSearchQueryMessageAttribute(query: query))
|
||||||
@ -1837,6 +1855,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, peer.hasBannedPermission(.banSendGifs) != nil {
|
||||||
|
if let boostsToUnrestrict = strongSelf.presentationInterfaceState.boostsToUnrestrict, boostsToUnrestrict > 0, (strongSelf.presentationInterfaceState.appliedBoosts ?? 0) < boostsToUnrestrict {
|
||||||
|
strongSelf.interfaceInteraction?.openBoostToUnrestrict()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.chatDisplayNode.collapseInput()
|
strongSelf.chatDisplayNode.collapseInput()
|
||||||
@ -1881,6 +1906,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, peer.hasBannedPermission(.banSendGifs) != nil {
|
||||||
|
if let boostsToUnrestrict = strongSelf.presentationInterfaceState.boostsToUnrestrict, boostsToUnrestrict > 0, (strongSelf.presentationInterfaceState.appliedBoosts ?? 0) < boostsToUnrestrict {
|
||||||
|
strongSelf.interfaceInteraction?.openBoostToUnrestrict()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.enqueueChatContextResult(collection, result, hideVia: true, closeMediaInput: true, silentPosting: silentPosting, resetTextInputState: resetTextInputState)
|
strongSelf.enqueueChatContextResult(collection, result, hideVia: true, closeMediaInput: true, silentPosting: silentPosting, resetTextInputState: resetTextInputState)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -9538,10 +9570,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let boostsToUnrestrict = (strongSelf.peerView?.cachedData as? CachedChannelData)?.boostsToUnrestrict, boostsToUnrestrict > 0 {
|
|
||||||
strongSelf.interfaceInteraction?.openBoostToUnrestrict()
|
let canBypassRestrictions = canBypassRestrictions(chatPresentationInterfaceState: strongSelf.presentationInterfaceState)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let subjectFlags: [TelegramChatBannedRightsFlags]
|
let subjectFlags: [TelegramChatBannedRightsFlags]
|
||||||
switch subject {
|
switch subject {
|
||||||
@ -9554,7 +9584,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var bannedPermission: (Int32, Bool)? = nil
|
var bannedPermission: (Int32, Bool)? = nil
|
||||||
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||||
for subjectFlag in subjectFlags {
|
for subjectFlag in subjectFlags {
|
||||||
if let value = channel.hasBannedPermission(subjectFlag) {
|
if let value = channel.hasBannedPermission(subjectFlag, ignoreDefault: canBypassRestrictions) {
|
||||||
bannedPermission = value
|
bannedPermission = value
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -9568,6 +9598,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let boostsToUnrestrict = (strongSelf.peerView?.cachedData as? CachedChannelData)?.boostsToUnrestrict, boostsToUnrestrict > 0, let bannedPermission, !bannedPermission.1 {
|
||||||
|
strongSelf.interfaceInteraction?.openBoostToUnrestrict()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var displayToast = false
|
var displayToast = false
|
||||||
|
|
||||||
if let (untilDate, personal) = bannedPermission {
|
if let (untilDate, personal) = bannedPermission {
|
||||||
@ -10765,8 +10800,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
items.append(.custom(ChatSendAsPeerTitleContextItem(text: strongSelf.presentationInterfaceState.strings.Conversation_SendMesageAs.uppercased()), false))
|
items.append(.custom(ChatSendAsPeerTitleContextItem(text: strongSelf.presentationInterfaceState.strings.Conversation_SendMesageAs.uppercased()), false))
|
||||||
items.append(.custom(ChatSendAsPeerListContextItem(context: strongSelf.context, chatPeerId: peerId, peers: peers, selectedPeerId: myPeerId, isPremium: isPremium, presentToast: { [weak self] peer in
|
items.append(.custom(ChatSendAsPeerListContextItem(context: strongSelf.context, chatPeerId: peerId, peers: peers, selectedPeerId: myPeerId, isPremium: isPremium, presentToast: { [weak self] peer in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let hapticFeedback = HapticFeedback()
|
HapticFeedback().impact()
|
||||||
hapticFeedback.impact()
|
|
||||||
|
|
||||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action, duration: 3), elevatedLayout: false, action: { [weak self] action in
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action, duration: 3), elevatedLayout: false, action: { [weak self] action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -11040,6 +11074,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let self, let peerId = self.chatLocation.peerId, let cachedData = self.peerView?.cachedData as? CachedChannelData, let boostToUnrestrict = cachedData.boostsToUnrestrict else {
|
guard let self, let peerId = self.chatLocation.peerId, let cachedData = self.peerView?.cachedData as? CachedChannelData, let boostToUnrestrict = cachedData.boostsToUnrestrict else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HapticFeedback().impact()
|
||||||
|
|
||||||
let _ = combineLatest(queue: Queue.mainQueue(),
|
let _ = combineLatest(queue: Queue.mainQueue(),
|
||||||
context.engine.peers.getChannelBoostStatus(peerId: peerId),
|
context.engine.peers.getChannelBoostStatus(peerId: peerId),
|
||||||
context.engine.peers.getMyBoostStatus()
|
context.engine.peers.getMyBoostStatus()
|
||||||
|
|||||||
@ -44,11 +44,12 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
|
|
||||||
let inputIsActive = self.presentationInterfaceState.inputMode == .text
|
let inputIsActive = self.presentationInterfaceState.inputMode == .text
|
||||||
|
|
||||||
self.chatDisplayNode.dismissInput()
|
self.chatDisplayNode.dismissInput()
|
||||||
|
|
||||||
|
let canByPassRestrictions = canBypassRestrictions(chatPresentationInterfaceState: self.presentationInterfaceState)
|
||||||
|
|
||||||
var banSendText: (Int32, Bool)?
|
var banSendText: (Int32, Bool)?
|
||||||
var bannedSendPhotos: (Int32, Bool)?
|
var bannedSendPhotos: (Int32, Bool)?
|
||||||
var bannedSendVideos: (Int32, Bool)?
|
var bannedSendVideos: (Int32, Bool)?
|
||||||
@ -60,19 +61,19 @@ extension ChatControllerImpl {
|
|||||||
} else if peer is TelegramSecretChat {
|
} else if peer is TelegramSecretChat {
|
||||||
canSendPolls = false
|
canSendPolls = false
|
||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
if let value = channel.hasBannedPermission(.banSendPhotos) {
|
if let value = channel.hasBannedPermission(.banSendPhotos, ignoreDefault: canByPassRestrictions) {
|
||||||
bannedSendPhotos = value
|
bannedSendPhotos = value
|
||||||
}
|
}
|
||||||
if let value = channel.hasBannedPermission(.banSendVideos) {
|
if let value = channel.hasBannedPermission(.banSendVideos, ignoreDefault: canByPassRestrictions) {
|
||||||
bannedSendVideos = value
|
bannedSendVideos = value
|
||||||
}
|
}
|
||||||
if let value = channel.hasBannedPermission(.banSendFiles) {
|
if let value = channel.hasBannedPermission(.banSendFiles, ignoreDefault: canByPassRestrictions) {
|
||||||
bannedSendFiles = value
|
bannedSendFiles = value
|
||||||
}
|
}
|
||||||
if let value = channel.hasBannedPermission(.banSendText) {
|
if let value = channel.hasBannedPermission(.banSendText, ignoreDefault: canByPassRestrictions) {
|
||||||
banSendText = value
|
banSendText = value
|
||||||
}
|
}
|
||||||
if channel.hasBannedPermission(.banSendPolls) != nil {
|
if channel.hasBannedPermission(.banSendPolls, ignoreDefault: canByPassRestrictions) != nil {
|
||||||
canSendPolls = false
|
canSendPolls = false
|
||||||
}
|
}
|
||||||
} else if let group = peer as? TelegramGroup {
|
} else if let group = peer as? TelegramGroup {
|
||||||
|
|||||||
@ -1472,6 +1472,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||||||
let deviceContactsNumbers = self.context.sharedContext.deviceContactPhoneNumbers.get()
|
let deviceContactsNumbers = self.context.sharedContext.deviceContactPhoneNumbers.get()
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
let messageViewQueue = Queue.mainQueue()
|
let messageViewQueue = Queue.mainQueue()
|
||||||
let historyViewTransitionDisposable = combineLatest(queue: messageViewQueue,
|
let historyViewTransitionDisposable = combineLatest(queue: messageViewQueue,
|
||||||
historyViewUpdate,
|
historyViewUpdate,
|
||||||
@ -1676,22 +1678,28 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||||||
reverseGroups = reverseGroupsValue
|
reverseGroups = reverseGroupsValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var isCopyProtectionEnabled: Bool = data.initialData?.peer?.isCopyProtectionEnabled ?? false
|
|
||||||
for entry in view.additionalData {
|
|
||||||
if case let .peer(_, maybePeer) = entry, let peer = maybePeer {
|
|
||||||
isCopyProtectionEnabled = peer.isCopyProtectionEnabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isPremium = false
|
var isPremium = false
|
||||||
if case let .user(user) = accountPeer, user.isPremium {
|
if case let .user(user) = accountPeer, user.isPremium {
|
||||||
isPremium = true
|
isPremium = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var audioTranscriptionProvidedByBoost = false
|
||||||
|
var isCopyProtectionEnabled: Bool = data.initialData?.peer?.isCopyProtectionEnabled ?? false
|
||||||
|
for entry in view.additionalData {
|
||||||
|
if case let .peer(_, maybePeer) = entry, let peer = maybePeer {
|
||||||
|
isCopyProtectionEnabled = peer.isCopyProtectionEnabled
|
||||||
|
if let channel = peer as? TelegramChannel, let boostLevel = channel.approximateBoostLevel {
|
||||||
|
if boostLevel >= premiumConfiguration.minGroupAudioTranscriptionLevel {
|
||||||
|
audioTranscriptionProvidedByBoost = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let alwaysDisplayTranscribeButton = ChatMessageItemAssociatedData.DisplayTranscribeButton(
|
let alwaysDisplayTranscribeButton = ChatMessageItemAssociatedData.DisplayTranscribeButton(
|
||||||
canBeDisplayed: suggestAudioTranscription.0 < 2,
|
canBeDisplayed: suggestAudioTranscription.0 < 2,
|
||||||
displayForNotConsumed: suggestAudioTranscription.1,
|
displayForNotConsumed: suggestAudioTranscription.1,
|
||||||
providedByGroupBoost: false
|
providedByGroupBoost: audioTranscriptionProvidedByBoost
|
||||||
)
|
)
|
||||||
|
|
||||||
var translateToLanguage: String?
|
var translateToLanguage: String?
|
||||||
|
|||||||
@ -201,8 +201,12 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
|||||||
if isTextEmpty, case .broadcast = peer.info, canSendMessagesToPeer(peer) {
|
if isTextEmpty, case .broadcast = peer.info, canSendMessagesToPeer(peer) {
|
||||||
accessoryItems.append(.silentPost(chatPresentationInterfaceState.interfaceState.silentPosting))
|
accessoryItems.append(.silentPost(chatPresentationInterfaceState.interfaceState.silentPosting))
|
||||||
}
|
}
|
||||||
if peer.hasBannedPermission(.banSendStickers) != nil {
|
if let boostsToUnrestrict = chatPresentationInterfaceState.boostsToUnrestrict, boostsToUnrestrict > 0 {
|
||||||
stickersEnabled = false
|
|
||||||
|
} else {
|
||||||
|
if peer.hasBannedPermission(.banSendStickers) != nil {
|
||||||
|
stickersEnabled = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
|
} else if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
|
||||||
if peer.hasBannedPermission(.banSendStickers) {
|
if peer.hasBannedPermission(.banSendStickers) {
|
||||||
|
|||||||
@ -4703,6 +4703,7 @@ private func generateClearImage(color: UIColor) -> UIImage? {
|
|||||||
|
|
||||||
|
|
||||||
private final class BoostSlowModeButton: HighlightTrackingButtonNode {
|
private final class BoostSlowModeButton: HighlightTrackingButtonNode {
|
||||||
|
let containerNode: ASDisplayNode
|
||||||
let backgroundNode: ASImageNode
|
let backgroundNode: ASImageNode
|
||||||
let textNode: ImmediateAnimatedCountLabelNode
|
let textNode: ImmediateAnimatedCountLabelNode
|
||||||
let iconNode: ASImageNode
|
let iconNode: ASImageNode
|
||||||
@ -4712,6 +4713,8 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
|
|||||||
var requestUpdate: () -> Void = {}
|
var requestUpdate: () -> Void = {}
|
||||||
|
|
||||||
override init(pointerStyle: PointerStyle? = nil) {
|
override init(pointerStyle: PointerStyle? = nil) {
|
||||||
|
self.containerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.backgroundNode = ASImageNode()
|
self.backgroundNode = ASImageNode()
|
||||||
self.backgroundNode.displaysAsynchronously = false
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
self.backgroundNode.clipsToBounds = true
|
self.backgroundNode.clipsToBounds = true
|
||||||
@ -4727,9 +4730,20 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
|
|||||||
|
|
||||||
super.init(pointerStyle: pointerStyle)
|
super.init(pointerStyle: pointerStyle)
|
||||||
|
|
||||||
self.addSubnode(self.backgroundNode)
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(self.iconNode)
|
self.containerNode.addSubnode(self.backgroundNode)
|
||||||
self.addSubnode(self.textNode)
|
self.containerNode.addSubnode(self.iconNode)
|
||||||
|
self.containerNode.addSubnode(self.textNode)
|
||||||
|
|
||||||
|
self.highligthedChanged = { [weak self] highlighted in
|
||||||
|
if let self {
|
||||||
|
if highlighted {
|
||||||
|
self.containerNode.layer.animateScale(from: 1.0, to: 0.75, duration: 0.4, removeOnCompletion: false)
|
||||||
|
} else if let presentationLayer = self.containerNode.layer.presentation() {
|
||||||
|
self.containerNode.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(size: CGSize, interfaceState: ChatPresentationInterfaceState) -> CGSize {
|
func update(size: CGSize, interfaceState: ChatPresentationInterfaceState) -> CGSize {
|
||||||
@ -4743,13 +4757,17 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
|
|||||||
case .pendingMessages:
|
case .pendingMessages:
|
||||||
relativeTimestamp = CGFloat(slowmodeState.timeout)
|
relativeTimestamp = CGFloat(slowmodeState.timeout)
|
||||||
}
|
}
|
||||||
text = stringForDuration(Int32(relativeTimestamp))
|
|
||||||
|
|
||||||
self.updateTimer?.invalidate()
|
self.updateTimer?.invalidate()
|
||||||
self.updateTimer = SwiftSignalKit.Timer(timeout: 1.0 / 60.0, repeat: false, completion: { [weak self] in
|
|
||||||
self?.requestUpdate()
|
if relativeTimestamp >= 0.0 {
|
||||||
}, queue: .mainQueue())
|
text = stringForDuration(Int32(relativeTimestamp))
|
||||||
self.updateTimer?.start()
|
|
||||||
|
self.updateTimer = SwiftSignalKit.Timer(timeout: 1.0 / 60.0, repeat: false, completion: { [weak self] in
|
||||||
|
self?.requestUpdate()
|
||||||
|
}, queue: .mainQueue())
|
||||||
|
self.updateTimer?.start()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.updateTimer?.invalidate()
|
self.updateTimer?.invalidate()
|
||||||
self.updateTimer = nil
|
self.updateTimer = nil
|
||||||
@ -4774,6 +4792,8 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
|
|||||||
let textSize = self.textNode.updateLayout(size: CGSize(width: 200.0, height: 100.0), animated: true)
|
let textSize = self.textNode.updateLayout(size: CGSize(width: 200.0, height: 100.0), animated: true)
|
||||||
let totalSize = CGSize(width: textSize.width > 0.0 ? textSize.width + 38.0 : 33.0, height: 33.0)
|
let totalSize = CGSize(width: textSize.width > 0.0 ? textSize.width + 38.0 : 33.0, height: 33.0)
|
||||||
|
|
||||||
|
self.containerNode.bounds = CGRect(origin: .zero, size: totalSize)
|
||||||
|
self.containerNode.position = CGPoint(x: totalSize.width / 2.0, y: totalSize.height / 2.0)
|
||||||
self.backgroundNode.frame = CGRect(origin: .zero, size: totalSize)
|
self.backgroundNode.frame = CGRect(origin: .zero, size: totalSize)
|
||||||
self.backgroundNode.cornerRadius = totalSize.height / 2.0
|
self.backgroundNode.cornerRadius = totalSize.height / 2.0
|
||||||
self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: floorToScreenPixels((totalSize.height - textSize.height) / 2.0)), size: textSize)
|
self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: floorToScreenPixels((totalSize.height - textSize.height) / 2.0)), size: textSize)
|
||||||
|
|||||||
@ -866,18 +866,6 @@ func openResolvedUrlImpl(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .boost(peerId, status, myBoostStatus):
|
case let .boost(peerId, status, myBoostStatus):
|
||||||
guard let status else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var isCurrent = false
|
|
||||||
if case let .chat(chatPeerId, _, _) = urlContext, chatPeerId == peerId {
|
|
||||||
isCurrent = true
|
|
||||||
}
|
|
||||||
var forceDark = false
|
|
||||||
if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance {
|
|
||||||
forceDark = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var dismissedImpl: (() -> Void)?
|
var dismissedImpl: (() -> Void)?
|
||||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||||
let updateExternalController = storyProgressPauseContext.update
|
let updateExternalController = storyProgressPauseContext.update
|
||||||
@ -886,24 +874,45 @@ func openResolvedUrlImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = PremiumBoostLevelsScreen(
|
if let peerId, let status {
|
||||||
context: context,
|
var isCurrent = false
|
||||||
peerId: peerId,
|
if case let .chat(chatPeerId, _, _) = urlContext, chatPeerId == peerId {
|
||||||
mode: .user(mode: isCurrent ? .current : .external),
|
isCurrent = true
|
||||||
status: status,
|
}
|
||||||
myBoostStatus: myBoostStatus,
|
var forceDark = false
|
||||||
openPeer: { peer in
|
if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance {
|
||||||
openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
forceDark = true
|
||||||
},
|
}
|
||||||
forceDark: forceDark
|
let controller = PremiumBoostLevelsScreen(
|
||||||
)
|
context: context,
|
||||||
controller.disposed = {
|
peerId: peerId,
|
||||||
dismissedImpl?()
|
mode: .user(mode: isCurrent ? .current : .external),
|
||||||
}
|
status: status,
|
||||||
navigationController?.pushViewController(controller)
|
myBoostStatus: myBoostStatus,
|
||||||
|
openPeer: { peer in
|
||||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||||
storyProgressPauseContext.update(controller)
|
},
|
||||||
|
forceDark: forceDark
|
||||||
|
)
|
||||||
|
controller.disposed = {
|
||||||
|
dismissedImpl?()
|
||||||
|
}
|
||||||
|
navigationController?.pushViewController(controller)
|
||||||
|
|
||||||
|
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||||
|
storyProgressPauseContext.update(controller)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let controller = textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Chat_ErrorCantBoost, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||||
|
present(controller, nil)
|
||||||
|
|
||||||
|
controller.dismissed = { _ in
|
||||||
|
dismissedImpl?()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||||
|
storyProgressPauseContext.update(controller)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case let .premiumGiftCode(slug):
|
case let .premiumGiftCode(slug):
|
||||||
var forceDark = false
|
var forceDark = false
|
||||||
|
|||||||
@ -2213,6 +2213,44 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makePremiumBoostLevelsController(context: AccountContext, peerId: EnginePeer.Id, boostStatus: ChannelBoostStatus, myBoostStatus: MyBoostStatus, forceDark: Bool, openStats: (() -> Void)?) -> ViewController {
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
|
var pushImpl: ((ViewController) -> Void)?
|
||||||
|
var dismissImpl: (() -> Void)?
|
||||||
|
let controller = PremiumBoostLevelsScreen(
|
||||||
|
context: context,
|
||||||
|
peerId: peerId,
|
||||||
|
mode: .owner(subject: .stories),
|
||||||
|
status: boostStatus,
|
||||||
|
myBoostStatus: myBoostStatus,
|
||||||
|
openStats: openStats,
|
||||||
|
openGift: premiumConfiguration.giveawayGiftsPurchaseAvailable ? {
|
||||||
|
var updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||||
|
if forceDark {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
|
updatedPresentationData = (presentationData, .single(presentationData))
|
||||||
|
}
|
||||||
|
let controller = createGiveawayController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, subject: .generic)
|
||||||
|
pushImpl?(controller)
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.4) {
|
||||||
|
dismissImpl?()
|
||||||
|
}
|
||||||
|
} : nil,
|
||||||
|
forceDark: forceDark
|
||||||
|
)
|
||||||
|
pushImpl = { [weak controller] c in
|
||||||
|
controller?.push(c)
|
||||||
|
}
|
||||||
|
dismissImpl = { [weak controller] in
|
||||||
|
if let controller, let navigationController = controller.navigationController as? NavigationController {
|
||||||
|
navigationController.setViewControllers(navigationController.viewControllers.filter { !($0 is PremiumBoostLevelsScreen) }, animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController {
|
public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController {
|
||||||
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -787,7 +787,11 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
|||||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .single(.result(.peer(nil, .info(nil))))
|
if case .boost = parameter {
|
||||||
|
return .single(.result(.boost(peerId: nil, status: nil, myBoostStatus: nil)))
|
||||||
|
} else {
|
||||||
|
return .single(.result(.peer(nil, .info(nil))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .peerId(peerId):
|
case let .peerId(peerId):
|
||||||
|
|||||||
@ -86,5 +86,9 @@ public func parseUrl(url: String, wasConcealed: Bool) -> (string: String, concea
|
|||||||
concealed = false
|
concealed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if url.hasPrefix("tg://premium_multigift") || url.hasPrefix("tg://premium_offer") {
|
||||||
|
concealed = false
|
||||||
|
}
|
||||||
|
|
||||||
return (rawDisplayUrl, concealed)
|
return (rawDisplayUrl, concealed)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"app": "10.7.1",
|
"app": "10.7.2",
|
||||||
"bazel": "7.0.2",
|
"bazel": "7.0.2",
|
||||||
"xcode": "15.2",
|
"xcode": "15.2",
|
||||||
"macos": "13.0"
|
"macos": "13.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user