This commit is contained in:
Ali 2023-07-10 22:38:18 +04:00
parent 855dc9c9c0
commit d6e97be56b
19 changed files with 258 additions and 92 deletions

View File

@ -6911,7 +6911,7 @@ Sorry for the inconvenience.";
"SponsoredMessageMenu.Info" = "What are sponsored\nmessages?";
"SponsoredMessageInfoScreen.Title" = "What are sponsored messages?";
"SponsoredMessageInfoScreen.Text" = "Unlike other apps, Telegram never uses your private data to target ads. You are seeing this message only because someone chose this public one-to many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message.\n\nUnline other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can't spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible adverticers at:\n[url]\nAds should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together.";
"SponsoredMessageInfoScreen.MarkdownText" = "Unlike other apps, Telegram never uses your private data to target ads. [Learn more in the Privacy Policy](https://telegram.org/privacy#5-6-no-ads-based-on-user-data)\nYou are seeing this message only because someone chose this public one-to many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message.\n\nUnline other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can't spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible adverticers at:\n[url]\nAds should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together.";
"SponsoredMessageInfo.Action" = "Learn More";
"SponsoredMessageInfo.Url" = "https://telegram.org/ads";

View File

@ -17,6 +17,7 @@ swift_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/AccountContext:AccountContext",
"//submodules/Markdown",
],
visibility = [
"//visibility:public",

View File

@ -7,6 +7,7 @@ import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import Markdown
public final class AdInfoScreen: ViewController {
private final class Node: ViewControllerTracingNode {
@ -84,9 +85,16 @@ public final class AdInfoScreen: ViewController {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
}
var openUrl: (() -> Void)?
var openUrl: ((String) -> Void)?
#if DEBUG && false
let rawText = "First Line\n**Bold Text** [Description](http://google.com) text\n[url]\nabcdee"
#else
let rawText = self.presentationData.strings.SponsoredMessageInfoScreen_MarkdownText
#endif
let defaultUrl = self.presentationData.strings.SponsoredMessageInfo_Url
let rawText = self.presentationData.strings.SponsoredMessageInfoScreen_Text
var items: [Item] = []
var didAddUrl = false
for component in rawText.components(separatedBy: "[url]") {
@ -100,20 +108,40 @@ public final class AdInfoScreen: ViewController {
let textNode = ImmediateTextNode()
textNode.maximumNumberOfLines = 0
textNode.attributedText = NSAttributedString(string: itemText, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
textNode.attributedText = parseMarkdownIntoAttributedString(itemText, attributes: MarkdownAttributes(
body: MarkdownAttributeSet(font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor),
bold: MarkdownAttributeSet(font: Font.semibold(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor),
link: MarkdownAttributeSet(font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemAccentColor),
linkAttribute: { url in
return ("URL", url)
}
))
items.append(.text(textNode))
textNode.highlightAttributeAction = { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
return NSAttributedString.Key(rawValue: "URL")
} else {
return nil
}
}
textNode.tapAttributeAction = { attributes, _ in
if let value = attributes[NSAttributedString.Key(rawValue: "URL")] as? String {
openUrl?(value)
}
}
textNode.linkHighlightColor = self.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.5)
if !didAddUrl {
didAddUrl = true
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_Url, color: self.presentationData.theme.list.itemAccentColor, action: {
openUrl?()
openUrl?(defaultUrl)
})))
}
}
if !didAddUrl {
didAddUrl = true
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_Url, color: self.presentationData.theme.list.itemAccentColor, action: {
openUrl?()
openUrl?(defaultUrl)
})))
}
self.items = items
@ -133,11 +161,11 @@ public final class AdInfoScreen: ViewController {
}
}
openUrl = { [weak self] in
openUrl = { [weak self] url in
guard let strongSelf = self else {
return
}
strongSelf.context.sharedContext.applicationBindings.openUrl(strongSelf.presentationData.strings.SponsoredMessageInfo_Url)
strongSelf.context.sharedContext.applicationBindings.openUrl(url)
}
}

View File

@ -186,7 +186,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.contentOffset = offset
self.contentOffsetChanged(offset: offset)
if self.contactListNode.listNode.isTracking {
/*if self.contactListNode.listNode.isTracking {
if case let .known(value) = offset {
if !self.storiesUnlocked {
if value < -40.0 {
@ -220,7 +220,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
default:
break
}
}
}*/
}
self.contactListNode.contentScrollingEnded = { [weak self] listView in
@ -280,32 +280,8 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
}
private func contentScrollingEnded(listView: ListView) -> Bool {
if "".isEmpty {
return false
}
/*if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
if let clippedScrollOffset = navigationBarComponentView.clippedScrollOffset {
if navigationBarComponentView.effectiveStoriesInsetHeight > 0.0 {
if clippedScrollOffset > 0.0 && clippedScrollOffset < navigationBarComponentView.effectiveStoriesInsetHeight {
if clippedScrollOffset < navigationBarComponentView.effectiveStoriesInsetHeight * 0.5 {
let _ = listView.scrollToOffsetFromTop(0.0, animated: true)
} else {
let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight, animated: true)
}
return true
} else {
let searchScrollOffset = clippedScrollOffset - navigationBarComponentView.effectiveStoriesInsetHeight
if searchScrollOffset > 0.0 && searchScrollOffset < ChatListNavigationBar.searchScrollHeight {
if searchScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 {
let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight, animated: true)
} else {
let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight + ChatListNavigationBar.searchScrollHeight, animated: true)
}
return true
}
}
} else {
if clippedScrollOffset > 0.0 && clippedScrollOffset < ChatListNavigationBar.searchScrollHeight {
if clippedScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 {
let _ = listView.scrollToOffsetFromTop(0.0, animated: true)
@ -316,7 +292,6 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
}
}
}
}*/
return false
}

View File

@ -1235,7 +1235,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset, topItemFound {
if !self.stackFromBottom {
completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset - effectiveInsets.bottom - effectiveInsets.top)
if !keepMinimalScrollHeightWithTopInset.isZero {
completeHeight = max(completeHeight, self.visibleSize.height + effectiveInsets.top + effectiveInsets.bottom)
}
//completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset - effectiveInsets.bottom - effectiveInsets.top)
bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight)
} else {
effectiveInsets.top = max(effectiveInsets.top, self.visibleSize.height - completeHeight)
@ -1647,7 +1650,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset {
if !self.stackFromBottom {
completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset)
if !keepMinimalScrollHeightWithTopInset.isZero {
completeHeight = max(completeHeight, self.visibleSize.height + effectiveInsets.top + effectiveInsets.bottom)
}
//completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset)
bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight)
}
}

View File

@ -580,7 +580,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
public let topShadowNode: ASImageNode
public let bottomShadowNode: ASImageNode
public var storyParams: (peer: EnginePeer, items: [EngineStoryItem], count: Int, hasUnseen: Bool)?
public var storyParams: (peer: EnginePeer, items: [EngineStoryItem], count: Int, hasUnseen: Bool, hasUnseenPrivate: Bool)?
private var expandedStorySetIndicator: ComponentView<Empty>?
public var expandedStorySetIndicatorTransitionView: (UIView, CGRect)? {
if let setView = self.expandedStorySetIndicator?.view as? StorySetIndicatorComponent.View {
@ -1268,6 +1268,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
peer: storyParams.peer,
items: storyParams.items,
hasUnseen: storyParams.hasUnseen,
hasUnseenPrivate: storyParams.hasUnseenPrivate,
totalCount: storyParams.count,
theme: defaultDarkPresentationTheme,
action: { [weak self] in

View File

@ -1074,6 +1074,10 @@ public final class SparseItemGrid: ASDisplayNode {
}
for id in removeIds {
if let item = self.visibleItems.removeValue(forKey: id) {
if let blurLayer = item.blurLayer {
item.blurLayer = nil
blurLayer.removeFromSuperlayer()
}
if let layer = item.layer {
items.itemBinding.unbindLayer(layer: layer)
layer.removeFromSuperlayer()

View File

@ -607,7 +607,11 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput
if let firstFrameFile = firstFrameFile {
account.postbox.mediaBox.storeCachedResourceRepresentation(resource.id.stringRepresentation, representationId: "first-frame", keepDuration: .general, tempFile: firstFrameFile)
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
if let data = try? Data(contentsOf: URL(fileURLWithPath: firstFrameFile.path), options: .mappedIfSafe) {
let localResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: nil, isSecretRelated: false)
account.postbox.mediaBox.storeResourceData(localResource.id, data: data)
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: dimensions, resource: localResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
}
}
let fileMedia = TelegramMediaFile(

View File

@ -544,9 +544,9 @@ public final class PeerStoryListContext {
self.requestDisposable = (self.account.postbox.transaction { transaction -> Api.InputUser? in
return transaction.getPeer(peerId).flatMap(apiInputUser)
}
|> mapToSignal { inputUser -> Signal<([EngineStoryItem], Int, PeerReference?), NoError> in
|> mapToSignal { inputUser -> Signal<([EngineStoryItem], Int, PeerReference?, Bool), NoError> in
guard let inputUser = inputUser else {
return .single(([], 0, nil))
return .single(([], 0, nil, false))
}
let signal: Signal<Api.stories.Stories, MTRpcError>
@ -562,18 +562,20 @@ public final class PeerStoryListContext {
|> `catch` { _ -> Signal<Api.stories.Stories?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<([EngineStoryItem], Int, PeerReference?), NoError> in
|> mapToSignal { result -> Signal<([EngineStoryItem], Int, PeerReference?, Bool), NoError> in
guard let result = result else {
return .single(([], 0, nil))
return .single(([], 0, nil, false))
}
return account.postbox.transaction { transaction -> ([EngineStoryItem], Int, PeerReference?) in
return account.postbox.transaction { transaction -> ([EngineStoryItem], Int, PeerReference?, Bool) in
var storyItems: [EngineStoryItem] = []
var totalCount: Int = 0
var hasMore: Bool = false
switch result {
case let .stories(count, stories, users):
totalCount = Int(count)
hasMore = stories.count >= 100
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users))
@ -619,11 +621,11 @@ public final class PeerStoryListContext {
}
}
return (storyItems, totalCount, transaction.getPeer(peerId).flatMap(PeerReference.init))
return (storyItems, totalCount, transaction.getPeer(peerId).flatMap(PeerReference.init), hasMore)
}
}
}
|> deliverOn(self.queue)).start(next: { [weak self] storyItems, totalCount, peerReference in
|> deliverOn(self.queue)).start(next: { [weak self] storyItems, totalCount, peerReference, hasMore in
guard let `self` = self else {
return
}
@ -650,7 +652,11 @@ public final class PeerStoryListContext {
updatedState.peerReference = peerReference
}
if hasMore {
updatedState.loadMoreToken = (storyItems.last?.id).flatMap(Int.init)
} else {
updatedState.loadMoreToken = nil
}
if updatedState.loadMoreToken != nil {
updatedState.totalCount = max(totalCount, updatedState.items.count)
} else {

View File

@ -26,6 +26,8 @@ swift_library(
"//submodules/UndoUI",
"//submodules/TelegramUI/Components/BottomButtonPanelComponent",
"//submodules/TelegramUI/Components/MoreHeaderButton",
"//submodules/TelegramUI/Components/MediaEditorScreen",
"//submodules/SaveToCameraRoll",
],
visibility = [
"//visibility:public",

View File

@ -14,6 +14,8 @@ import ChatTitleView
import BottomButtonPanelComponent
import UndoUI
import MoreHeaderButton
import MediaEditorScreen
import SaveToCameraRoll
final class PeerInfoStoryGridScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -93,11 +95,11 @@ final class PeerInfoStoryGridScreenComponent: Component {
}, action: { [weak self] _, a in
a(.default)
guard let self, let component = self.component else {
guard let self else {
return
}
let _ = component
self.saveSelected()
})))
items.append(.action(ContextMenuActionItem(text: strings.Common_Delete, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
@ -279,6 +281,72 @@ final class PeerInfoStoryGridScreenComponent: Component {
controller.presentInGlobalOverlay(contextController)
}
private func saveSelected() {
guard let component = self.component else {
return
}
let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in
guard let self, let component = self.component, let peer else {
return
}
guard let peerReference = PeerReference(peer._asPeer()) else {
return
}
guard let paneNode = self.paneNode, !paneNode.selectedIds.isEmpty else {
return
}
var signals: [Signal<Float, NoError>] = []
let sortedItems = paneNode.selectedItems.sorted(by: { lhs, rhs in return lhs.key < rhs.key })
if sortedItems.isEmpty {
return
}
//TODO:localize
let saveScreen = SaveProgressScreen(context: component.context, content: .progress("Saving", 0.0))
self.environment?.controller()?.present(saveScreen, in: .current)
let valueNorm: Float = 1.0 / Float(sortedItems.count)
var progressStart: Float = 0.0
for (_, item) in sortedItems {
let itemOffset = progressStart
progressStart += valueNorm
signals.append(saveToCameraRoll(context: component.context, postbox: component.context.account.postbox, userLocation: .other, mediaReference: .story(peer: peerReference, id: item.id, media: item.media._asMedia()))
|> map { progress -> Float in
return itemOffset + progress * valueNorm
})
}
var allSignal: Signal<Float, NoError> = .single(0.0)
for signal in signals {
allSignal = allSignal |> then(signal)
}
let disposable = (allSignal
|> deliverOnMainQueue).start(next: { [weak saveScreen] progress in
guard let saveScreen else {
return
}
saveScreen.content = .progress("Saving", progress)
}, completed: { [weak saveScreen] in
guard let saveScreen else {
return
}
saveScreen.content = .completion("Saved")
Queue.mainQueue().after(3.0, { [weak saveScreen] in
saveScreen?.dismiss()
})
})
saveScreen.cancelled = {
disposable.dispose()
}
})
}
func update(component: PeerInfoStoryGridScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
self.component = component
self.state = state

View File

@ -1487,29 +1487,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
public func loadHole(anchor: SparseItemGrid.HoleAnchor, at location: SparseItemGrid.HoleLocation) -> Signal<Never, NoError> {
//TODO:load more
/*guard let anchor = anchor as? VisualMediaHoleAnchor else {
return .never()
}
let mappedDirection: SparseMessageList.LoadHoleDirection
switch location {
case .around:
mappedDirection = .around
case .toLower:
mappedDirection = .later
case .toUpper:
mappedDirection = .earlier
}
let listSource = self.listSource
return Signal { subscriber in
listSource.loadHole(anchor: anchor.messageId, direction: mappedDirection, completion: {
subscriber.putCompletion()
})
return Signal { _ in
listSource.loadMore()
return EmptyDisposable
}*/
return .never()
}
|> runOn(.mainQueue())
}
public func updateContentType(contentType: ContentType) {
@ -1575,7 +1559,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
let timezoneOffset = Int32(TimeZone.current.secondsFromGMT())
var mappedItems: [SparseItemGrid.Item] = []
let mappedHoles: [SparseItemGrid.HoleAnchor] = []
var mappedHoles: [SparseItemGrid.HoleAnchor] = []
var totalCount: Int = 0
if let peerReference = state.peerReference {
for item in state.items {
@ -1586,6 +1570,9 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
localMonthTimestamp: Month(localTimestamp: item.timestamp + timezoneOffset).packedValue
))
}
if mappedItems.count < state.totalCount, let lastItem = state.items.last {
mappedHoles.append(VisualMediaHoleAnchor(index: mappedItems.count, storyId: 1, localMonthTimestamp: Month(localTimestamp: lastItem.timestamp + timezoneOffset).packedValue))
}
}
totalCount = state.totalCount
totalCount = max(mappedItems.count, totalCount)

View File

@ -1247,6 +1247,10 @@ private final class StoryContainerScreenComponent: Component {
} else if slice.previousItemId != nil {
component.content.navigate(navigation: .item(.previous))
} else if let environment = self.environment {
if let sourceIsAvatar = component.transitionIn?.sourceIsAvatar, sourceIsAvatar {
} else {
self.dismissWithoutTransitionOut = true
}
environment.controller()?.dismiss()
}

View File

@ -1750,7 +1750,7 @@ public final class StoryItemSetContainerComponent: Component {
self.sendMessageContext.videoRecorderValue?.dismissVideo()
self.sendMessageContext.discardMediaRecordingPreview(view: self)
},
attachmentAction: { [weak self] in
attachmentAction: component.slice.peer.isService ? nil : { [weak self] in
guard let self else {
return
}
@ -3635,7 +3635,7 @@ public final class StoryItemSetContainerComponent: Component {
self.requestSave()
})))
if component.slice.item.storyItem.isPublic && (component.slice.peer.addressName != nil || !component.slice.peer._asPeer().usernames.isEmpty) {
if component.slice.item.storyItem.isPublic && (component.slice.peer.addressName != nil || !component.slice.peer._asPeer().usernames.isEmpty) && component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) {
items.append(.action(ContextMenuActionItem(text: "Copy Link", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
@ -3773,6 +3773,46 @@ public final class StoryItemSetContainerComponent: Component {
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
var items: [ContextMenuItem] = []
if component.slice.item.storyItem.isPublic && (component.slice.peer.addressName != nil || !component.slice.peer._asPeer().usernames.isEmpty) {
items.append(.action(ContextMenuActionItem(text: "Copy Link", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
a(.default)
guard let self, let component = self.component else {
return
}
let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id)
|> deliverOnMainQueue).start(next: { [weak self] link in
guard let self, let component = self.component else {
return
}
if let link {
UIPasteboard.general.string = link
component.presentController(UndoOverlayController(
presentationData: presentationData,
content: .linkCopied(text: "Link copied."),
elevatedLayout: false,
animateInAsReplacement: false,
action: { _ in return false }
), nil)
}
})
})))
items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
a(.default)
guard let self else {
return
}
self.sendMessageContext.performShareAction(view: self)
})))
}
let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: component.slice.peer._asPeer(), peerSettings: settings._asNotificationSettings())
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in
@ -3824,7 +3864,7 @@ public final class StoryItemSetContainerComponent: Component {
isHidden = storiesHidden
}
items.append(.action(ContextMenuActionItem(text: isHidden ? "Unhide \(component.slice.peer.compactDisplayTitle)" : "Hide \(component.slice.peer.compactDisplayTitle)", icon: { theme in
items.append(.action(ContextMenuActionItem(text: isHidden ? "Unarchive" : "Archive", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: isHidden ? "Chat/Context Menu/MoveToChats" : "Chat/Context Menu/MoveToContacts"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
a(.default)
@ -3864,6 +3904,25 @@ public final class StoryItemSetContainerComponent: Component {
component.controller()?.present(tooltipScreen, in: .current)
})))
#if DEBUG
let saveText: String
if case .file = component.slice.item.storyItem.media {
saveText = "Save Video"
} else {
saveText = "Save Image"
}
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
a(.default)
guard let self else {
return
}
self.requestSave()
})))
#endif
items.append(.action(ContextMenuActionItem(text: "Report", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, a in

View File

@ -2353,7 +2353,7 @@ final class StoryItemSetContainerSendMessage {
})
}
func openPeerMention(view: StoryItemSetContainerComponent.View, name: String, navigation: ChatControllerInteractionNavigateToPeer = .default, sourceMessageId: MessageId? = nil) {
func openPeerMention(view: StoryItemSetContainerComponent.View, name: String, navigation: ChatControllerInteractionNavigateToPeer = .info, sourceMessageId: MessageId? = nil) {
guard let component = view.component, let parentController = component.controller() else {
return
}

View File

@ -92,6 +92,7 @@ public final class StorySetIndicatorComponent: Component {
public let peer: EnginePeer
public let items: [EngineStoryItem]
public let hasUnseen: Bool
public let hasUnseenPrivate: Bool
public let totalCount: Int
public let theme: PresentationTheme
public let action: () -> Void
@ -101,6 +102,7 @@ public final class StorySetIndicatorComponent: Component {
peer: EnginePeer,
items: [EngineStoryItem],
hasUnseen: Bool,
hasUnseenPrivate: Bool,
totalCount: Int,
theme: PresentationTheme,
action: @escaping () -> Void
@ -109,6 +111,7 @@ public final class StorySetIndicatorComponent: Component {
self.peer = peer
self.items = items
self.hasUnseen = hasUnseen
self.hasUnseenPrivate = hasUnseenPrivate
self.totalCount = totalCount
self.theme = theme
self.action = action
@ -121,6 +124,9 @@ public final class StorySetIndicatorComponent: Component {
if lhs.hasUnseen != rhs.hasUnseen {
return false
}
if lhs.hasUnseenPrivate != rhs.hasUnseenPrivate {
return false
}
if lhs.totalCount != rhs.totalCount {
return false
}
@ -349,7 +355,9 @@ public final class StorySetIndicatorComponent: Component {
let borderColors: [UInt32]
if component.hasUnseen {
if component.hasUnseenPrivate {
borderColors = [component.theme.chatList.storyUnseenPrivateColors.topColor.argb, component.theme.chatList.storyUnseenPrivateColors.bottomColor.argb]
} else if component.hasUnseen {
borderColors = [component.theme.chatList.storyUnseenColors.topColor.argb, component.theme.chatList.storyUnseenColors.bottomColor.argb]
} else {
borderColors = [UIColor(white: 1.0, alpha: 0.3).argb, UIColor(white: 1.0, alpha: 0.3).argb]

View File

@ -457,9 +457,6 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor)
}, iconSource: nil, action: { _, f in
/*c.dismiss(completion: {
controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context))
})*/
f(.dismissWithoutContent)
controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context))
})))
@ -625,6 +622,14 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}
}
for media in messages[0].media {
if let story = media as? TelegramMediaStory {
if let story = message.associatedStories[story.storyId], story.data.isEmpty {
canPin = false
}
}
}
var loadStickerSaveStatusSignal: Signal<Bool?, NoError> = .single(nil)
if let loadStickerSaveStatus = loadStickerSaveStatus {
loadStickerSaveStatusSignal = context.engine.stickers.isStickerSaved(id: loadStickerSaveStatus)
@ -1943,6 +1948,10 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
}
} else if let action = media as? TelegramMediaAction, case .phoneCall = action.action {
optionsMap[id]!.insert(.rateCall)
} else if let story = media as? TelegramMediaStory {
if let story = message.associatedStories[story.storyId], story.data.isEmpty {
isShareProtected = true
}
}
}
if id.namespace == Namespaces.Message.ScheduledCloud {
@ -1962,7 +1971,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
optionsMap[id]!.insert(.deleteLocally)
}
} else if id.peerId == accountPeerId {
if !(message.flags.isSending || message.flags.contains(.Failed)) {
if !(message.flags.isSending || message.flags.contains(.Failed)) && !isShareProtected {
optionsMap[id]!.insert(.forward)
}
optionsMap[id]!.insert(.deleteLocally)
@ -2006,7 +2015,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
banPeer = nil
}
}
if !message.containsSecretMedia && !isAction {
if !message.containsSecretMedia && !isAction && !isShareProtected {
if message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.isCopyProtected() {
if !(message.flags.isSending || message.flags.contains(.Failed)) {
optionsMap[id]!.insert(.forward)
@ -2023,7 +2032,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
}
} else if let group = peer as? TelegramGroup {
if message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.containsSecretMedia {
if !isAction && !message.isCopyProtected() {
if !isAction && !message.isCopyProtected() && !isShareProtected {
if !(message.flags.isSending || message.flags.contains(.Failed)) {
optionsMap[id]!.insert(.forward)
}
@ -2057,7 +2066,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
}
}
} else if let user = peer as? TelegramUser {
if !isScheduled && message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.containsSecretMedia && !isAction && !message.id.peerId.isReplies && !message.isCopyProtected() {
if !isScheduled && message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.containsSecretMedia && !isAction && !message.id.peerId.isReplies && !message.isCopyProtected() && !isShareProtected {
if !(message.flags.isSending || message.flags.contains(.Failed)) {
optionsMap[id]!.insert(.forward)
}

View File

@ -536,7 +536,11 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
}
if let backgroundNode = self.backgroundNode, backgroundNode.frame.contains(point) {
if let item = self.item, item.message.media.contains(where: { $0 is TelegramMediaStory }) {
return .none
} else {
return .openMessage
}
} else {
return .none
}

View File

@ -3903,7 +3903,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
case .placeholder:
return nil
}
}, state.items.count, state.hasUnseen)
}, state.items.count, state.hasUnseen, state.hasUnseenCloseFriends)
}
self.requestLayout(animated: false)