mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit 'eb5f6e8bc03b545d4be5de02912ecbf28c5d6107'
This commit is contained in:
commit
88ecedba25
@ -3365,7 +3365,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
timeout: privacy.timeout,
|
timeout: privacy.timeout,
|
||||||
mentions: mentions,
|
mentions: mentions,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] privacy, allowScreenshots, pin in
|
completion: { [weak self] privacy, allowScreenshots, pin, _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -3408,12 +3408,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
allowScreenshots: !isForwardingDisabled,
|
allowScreenshots: !isForwardingDisabled,
|
||||||
pin: pin,
|
pin: pin,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] result, isForwardingDisabled, pin in
|
completion: { [weak self] result, isForwardingDisabled, pin, peers in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if case .closeFriends = privacy.base {
|
if case .closeFriends = privacy.base {
|
||||||
let _ = self.context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
let _ = self.context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
||||||
|
self.closeFriends.set(.single(peers))
|
||||||
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
||||||
} else {
|
} else {
|
||||||
completion(result)
|
completion(result)
|
||||||
|
@ -35,7 +35,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
let mentions: [String]
|
let mentions: [String]
|
||||||
let categoryItems: [CategoryItem]
|
let categoryItems: [CategoryItem]
|
||||||
let optionItems: [OptionItem]
|
let optionItems: [OptionItem]
|
||||||
let completion: (EngineStoryPrivacy, Bool, Bool) -> Void
|
let completion: (EngineStoryPrivacy, Bool, Bool, [EnginePeer]) -> Void
|
||||||
let editCategory: (EngineStoryPrivacy, Bool, Bool) -> Void
|
let editCategory: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@ -48,7 +48,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
mentions: [String],
|
mentions: [String],
|
||||||
categoryItems: [CategoryItem],
|
categoryItems: [CategoryItem],
|
||||||
optionItems: [OptionItem],
|
optionItems: [OptionItem],
|
||||||
completion: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
completion: @escaping (EngineStoryPrivacy, Bool, Bool, [EnginePeer]) -> Void,
|
||||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void
|
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -1552,7 +1552,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
additionallyIncludePeers: self.selectedPeers
|
additionallyIncludePeers: self.selectedPeers
|
||||||
),
|
),
|
||||||
self.selectedOptions.contains(.screenshot),
|
self.selectedOptions.contains(.screenshot),
|
||||||
self.selectedOptions.contains(.pin)
|
self.selectedOptions.contains(.pin),
|
||||||
|
self.component?.stateContext.stateValue?.peers.filter { self.selectedPeers.contains($0.id) } ?? []
|
||||||
)
|
)
|
||||||
|
|
||||||
controller.dismissAllTooltips()
|
controller.dismissAllTooltips()
|
||||||
@ -1918,9 +1919,13 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
case let .search(query, onlyContacts):
|
case let .search(query, onlyContacts):
|
||||||
let signal: Signal<[EngineRenderedPeer], NoError>
|
let signal: Signal<[EngineRenderedPeer], NoError>
|
||||||
if onlyContacts {
|
if onlyContacts {
|
||||||
signal = context.engine.contacts.searchContacts(query: query)
|
signal = combineLatest(
|
||||||
|> map { result in
|
context.engine.contacts.searchLocalPeers(query: query),
|
||||||
return result.0.map { EngineRenderedPeer(peer: $0) }
|
context.engine.contacts.searchContacts(query: query)
|
||||||
|
)
|
||||||
|
|> map { peers, contacts in
|
||||||
|
let contactIds = Set(contacts.0.map { $0.id })
|
||||||
|
return peers.filter { contactIds.contains($0.peerId) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
signal = context.engine.contacts.searchLocalPeers(query: query)
|
signal = context.engine.contacts.searchLocalPeers(query: query)
|
||||||
@ -1975,7 +1980,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
timeout: Int = 0,
|
timeout: Int = 0,
|
||||||
mentions: [String] = [],
|
mentions: [String] = [],
|
||||||
stateContext: StateContext,
|
stateContext: StateContext,
|
||||||
completion: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
completion: @escaping (EngineStoryPrivacy, Bool, Bool, [EnginePeer]) -> Void,
|
||||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void
|
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
@ -1425,7 +1425,7 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
},
|
},
|
||||||
keyboardInputData: self.inputMediaNodeDataPromise.get(),
|
keyboardInputData: self.inputMediaNodeDataPromise.get(),
|
||||||
closeFriends: self.closeFriendsPromise.get(),
|
closeFriends: self.closeFriendsPromise,
|
||||||
sharedViewListsContext: self.sharedViewListsContext
|
sharedViewListsContext: self.sharedViewListsContext
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
|
@ -111,7 +111,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
public let controller: () -> ViewController?
|
public let controller: () -> ViewController?
|
||||||
public let toggleAmbientMode: () -> Void
|
public let toggleAmbientMode: () -> Void
|
||||||
public let keyboardInputData: Signal<ChatEntityKeyboardInputNode.InputData, NoError>
|
public let keyboardInputData: Signal<ChatEntityKeyboardInputNode.InputData, NoError>
|
||||||
public let closeFriends: Signal<[EnginePeer], NoError>
|
public let closeFriends: Promise<[EnginePeer]>
|
||||||
let sharedViewListsContext: StoryItemSetViewListComponent.SharedListsContext
|
let sharedViewListsContext: StoryItemSetViewListComponent.SharedListsContext
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@ -144,7 +144,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
controller: @escaping () -> ViewController?,
|
controller: @escaping () -> ViewController?,
|
||||||
toggleAmbientMode: @escaping () -> Void,
|
toggleAmbientMode: @escaping () -> Void,
|
||||||
keyboardInputData: Signal<ChatEntityKeyboardInputNode.InputData, NoError>,
|
keyboardInputData: Signal<ChatEntityKeyboardInputNode.InputData, NoError>,
|
||||||
closeFriends: Signal<[EnginePeer], NoError>,
|
closeFriends: Promise<[EnginePeer]>,
|
||||||
sharedViewListsContext: StoryItemSetViewListComponent.SharedListsContext
|
sharedViewListsContext: StoryItemSetViewListComponent.SharedListsContext
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -3202,7 +3202,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
context: context,
|
context: context,
|
||||||
subject: .stories(editing: true),
|
subject: .stories(editing: true),
|
||||||
initialPeerIds: Set(privacy.additionallyIncludePeers),
|
initialPeerIds: Set(privacy.additionallyIncludePeers),
|
||||||
closeFriends: component.closeFriends
|
closeFriends: component.closeFriends.get()
|
||||||
)
|
)
|
||||||
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -3212,7 +3212,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
context: context,
|
context: context,
|
||||||
initialPrivacy: privacy,
|
initialPrivacy: privacy,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] privacy, _, _ in
|
completion: { [weak self] privacy, _, _, _ in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -3262,9 +3262,12 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
context: context,
|
context: context,
|
||||||
initialPrivacy: privacy,
|
initialPrivacy: privacy,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { result, _, _ in
|
completion: { [weak self] result, _, _, peers in
|
||||||
if case .closeFriends = privacy.base {
|
if case .closeFriends = privacy.base {
|
||||||
let _ = context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
let _ = context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
||||||
|
if let component = self?.component {
|
||||||
|
component.closeFriends.set(.single(peers))
|
||||||
|
}
|
||||||
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
||||||
} else {
|
} else {
|
||||||
completion(result)
|
completion(result)
|
||||||
@ -3632,6 +3635,132 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func getLinkedStickerPacks() -> (ContextController.Tip?, Signal<ContextController.Tip?, NoError>?) {
|
||||||
|
guard let component = self.component else {
|
||||||
|
return (nil, nil)
|
||||||
|
}
|
||||||
|
var tip: ContextController.Tip?
|
||||||
|
var tipSignal: Signal<ContextController.Tip?, NoError>?
|
||||||
|
|
||||||
|
var hasLinkedStickers = false
|
||||||
|
let media = component.slice.item.storyItem.media._asMedia()
|
||||||
|
if let image = media as? TelegramMediaImage {
|
||||||
|
hasLinkedStickers = image.flags.contains(.hasStickers)
|
||||||
|
} else if let file = media as? TelegramMediaFile {
|
||||||
|
hasLinkedStickers = file.hasLinkedStickers
|
||||||
|
}
|
||||||
|
|
||||||
|
var emojiFileIds: [Int64] = []
|
||||||
|
for entity in component.slice.item.storyItem.entities {
|
||||||
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||||
|
emojiFileIds.append(fileId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !emojiFileIds.isEmpty || hasLinkedStickers, let peerReference = PeerReference(component.slice.peer._asPeer()) {
|
||||||
|
let context = component.context
|
||||||
|
|
||||||
|
tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil)
|
||||||
|
|
||||||
|
let packsPromise = Promise<[StickerPackReference]>()
|
||||||
|
if hasLinkedStickers {
|
||||||
|
packsPromise.set(context.engine.stickers.stickerPacksAttachedToMedia(media: .story(peer: peerReference, id: component.slice.item.storyItem.id, media: media)))
|
||||||
|
} else {
|
||||||
|
packsPromise.set(.single([]))
|
||||||
|
}
|
||||||
|
|
||||||
|
let customEmojiPacksPromise = Promise<[StickerPackReference]>()
|
||||||
|
if !emojiFileIds.isEmpty {
|
||||||
|
customEmojiPacksPromise.set( context.engine.stickers.resolveInlineStickers(fileIds: emojiFileIds)
|
||||||
|
|> map { files -> [StickerPackReference] in
|
||||||
|
var packReferences: [StickerPackReference] = []
|
||||||
|
var existingIds = Set<Int64>()
|
||||||
|
for (_, file) in files {
|
||||||
|
loop: for attribute in file.attributes {
|
||||||
|
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
|
||||||
|
if case let .id(id, _) = packReference, !existingIds.contains(id) {
|
||||||
|
packReferences.append(packReference)
|
||||||
|
existingIds.insert(id)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packReferences
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
customEmojiPacksPromise.set(.single([]))
|
||||||
|
}
|
||||||
|
|
||||||
|
let action: () -> Void = { [weak self] in
|
||||||
|
if let self {
|
||||||
|
let combinedPacks = combineLatest(packsPromise.get(), customEmojiPacksPromise.get())
|
||||||
|
|> map { embeddedPackReferences, customEmojiPackReferences in
|
||||||
|
var packReferences: [StickerPackReference] = []
|
||||||
|
for packReference in embeddedPackReferences {
|
||||||
|
packReferences.append(packReference)
|
||||||
|
}
|
||||||
|
for packReference in customEmojiPackReferences {
|
||||||
|
if !packReferences.contains(packReference) {
|
||||||
|
packReferences.append(packReference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packReferences
|
||||||
|
}
|
||||||
|
self.sendMessageContext.openAttachedStickers(view: self, packs: combinedPacks |> take(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tipSignal = combineLatest(packsPromise.get(), customEmojiPacksPromise.get())
|
||||||
|
|> mapToSignal { embeddedPackReferences, customEmojiPackReferences -> Signal<ContextController.Tip?, NoError> in
|
||||||
|
var packReferences: [StickerPackReference] = []
|
||||||
|
for packReference in embeddedPackReferences {
|
||||||
|
packReferences.append(packReference)
|
||||||
|
}
|
||||||
|
for packReference in customEmojiPackReferences {
|
||||||
|
if !packReferences.contains(packReference) {
|
||||||
|
packReferences.append(packReference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if packReferences.count > 1 {
|
||||||
|
let valueText = component.strings.Story_Context_EmbeddedStickersValue(Int32(packReferences.count))
|
||||||
|
return .single(.animatedEmoji(text: component.strings.Story_Context_EmbeddedStickers(valueText).string, arguments: nil, file: nil, action: action))
|
||||||
|
} else if let reference = packReferences.first {
|
||||||
|
return context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false)
|
||||||
|
|> filter { result in
|
||||||
|
if case .result = result {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<ContextController.Tip?, NoError> in
|
||||||
|
if case let .result(info, items, _) = result {
|
||||||
|
let isEmoji = info.flags.contains(.isEmoji)
|
||||||
|
let tip: ContextController.Tip = .animatedEmoji(
|
||||||
|
text: isEmoji ? component.strings.Story_Context_EmbeddedEmojiPack(info.title).string : component.strings.Story_Context_EmbeddedStickerPack(info.title).string,
|
||||||
|
arguments: TextNodeWithEntities.Arguments(
|
||||||
|
context: context,
|
||||||
|
cache: context.animationCache,
|
||||||
|
renderer: context.animationRenderer,
|
||||||
|
placeholderColor: .clear,
|
||||||
|
attemptSynchronous: true
|
||||||
|
),
|
||||||
|
file: items.first?.file,
|
||||||
|
action: action)
|
||||||
|
return .single(tip)
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tip, tipSignal)
|
||||||
|
}
|
||||||
|
|
||||||
private func performMyMoreAction(sourceView: UIView, gesture: ContextGesture?) {
|
private func performMyMoreAction(sourceView: UIView, gesture: ContextGesture?) {
|
||||||
guard let component = self.component, let controller = component.controller() else {
|
guard let component = self.component, let controller = component.controller() else {
|
||||||
return
|
return
|
||||||
@ -3770,66 +3899,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasLinkedStickers = false
|
let (tip, tipSignal) = self.getLinkedStickerPacks()
|
||||||
let media = component.slice.item.storyItem.media._asMedia()
|
|
||||||
if let image = media as? TelegramMediaImage {
|
|
||||||
hasLinkedStickers = image.flags.contains(.hasStickers)
|
|
||||||
} else if let file = media as? TelegramMediaFile {
|
|
||||||
hasLinkedStickers = file.hasLinkedStickers
|
|
||||||
}
|
|
||||||
|
|
||||||
var tip: ContextController.Tip?
|
|
||||||
var tipSignal: Signal<ContextController.Tip?, NoError>?
|
|
||||||
if hasLinkedStickers, let peerReference = PeerReference(component.slice.peer._asPeer()) {
|
|
||||||
let context = component.context
|
|
||||||
tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil)
|
|
||||||
|
|
||||||
let packsPromise = Promise<[StickerPackReference]>()
|
|
||||||
packsPromise.set(context.engine.stickers.stickerPacksAttachedToMedia(media: .story(peer: peerReference, id: component.slice.item.storyItem.id, media: media)))
|
|
||||||
|
|
||||||
let action: () -> Void = { [weak self] in
|
|
||||||
if let self {
|
|
||||||
self.sendMessageContext.openAttachedStickers(view: self, packs: packsPromise.get() |> take(1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tipSignal = packsPromise.get()
|
|
||||||
|> mapToSignal { packReferences -> Signal<ContextController.Tip?, NoError> in
|
|
||||||
if packReferences.count > 1 {
|
|
||||||
let valueText = component.strings.Story_Context_EmbeddedStickersValue(Int32(packReferences.count))
|
|
||||||
return .single(.animatedEmoji(text: component.strings.Story_Context_EmbeddedStickers(valueText).string, arguments: nil, file: nil, action: action))
|
|
||||||
} else if let reference = packReferences.first {
|
|
||||||
return context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false)
|
|
||||||
|> filter { result in
|
|
||||||
if case .result = result {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> mapToSignal { result -> Signal<ContextController.Tip?, NoError> in
|
|
||||||
if case let .result(info, items, _) = result {
|
|
||||||
let isEmoji = info.flags.contains(.isEmoji)
|
|
||||||
let tip: ContextController.Tip = .animatedEmoji(
|
|
||||||
text: isEmoji ? component.strings.Story_Context_EmbeddedEmojiPack(info.title).string : component.strings.Story_Context_EmbeddedStickerPack(info.title).string,
|
|
||||||
arguments: TextNodeWithEntities.Arguments(
|
|
||||||
context: context,
|
|
||||||
cache: context.animationCache,
|
|
||||||
renderer: context.animationRenderer,
|
|
||||||
placeholderColor: .clear,
|
|
||||||
attemptSynchronous: true
|
|
||||||
),
|
|
||||||
file: items.first?.file,
|
|
||||||
action: action)
|
|
||||||
return .single(tip)
|
|
||||||
} else {
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal)
|
let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal)
|
||||||
|
|
||||||
@ -4039,66 +4109,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasLinkedStickers = false
|
let (tip, tipSignal) = self.getLinkedStickerPacks()
|
||||||
let media = component.slice.item.storyItem.media._asMedia()
|
|
||||||
if let image = media as? TelegramMediaImage {
|
|
||||||
hasLinkedStickers = image.flags.contains(.hasStickers)
|
|
||||||
} else if let file = media as? TelegramMediaFile {
|
|
||||||
hasLinkedStickers = file.hasLinkedStickers
|
|
||||||
}
|
|
||||||
|
|
||||||
var tip: ContextController.Tip?
|
|
||||||
var tipSignal: Signal<ContextController.Tip?, NoError>?
|
|
||||||
if hasLinkedStickers {
|
|
||||||
let context = component.context
|
|
||||||
tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil)
|
|
||||||
|
|
||||||
let packsPromise = Promise<[StickerPackReference]>()
|
|
||||||
packsPromise.set(context.engine.stickers.stickerPacksAttachedToMedia(media: .standalone(media: media)))
|
|
||||||
|
|
||||||
let action: () -> Void = { [weak self] in
|
|
||||||
if let self {
|
|
||||||
self.sendMessageContext.openAttachedStickers(view: self, packs: packsPromise.get() |> take(1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tipSignal = packsPromise.get()
|
|
||||||
|> mapToSignal { packReferences -> Signal<ContextController.Tip?, NoError> in
|
|
||||||
if packReferences.count > 1 {
|
|
||||||
let valueText = component.strings.Story_Context_EmbeddedStickersValue(Int32(packReferences.count))
|
|
||||||
return .single(.animatedEmoji(text: component.strings.Story_Context_EmbeddedStickers(valueText).string, arguments: nil, file: nil, action: action))
|
|
||||||
} else if let reference = packReferences.first {
|
|
||||||
return context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false)
|
|
||||||
|> filter { result in
|
|
||||||
if case .result = result {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> mapToSignal { result -> Signal<ContextController.Tip?, NoError> in
|
|
||||||
if case let .result(info, items, _) = result {
|
|
||||||
let isEmoji = info.flags.contains(.isEmoji)
|
|
||||||
let tip: ContextController.Tip = .animatedEmoji(
|
|
||||||
text: isEmoji ? component.strings.Story_Context_EmbeddedEmojiPack(info.title).string : component.strings.Story_Context_EmbeddedStickerPack(info.title).string,
|
|
||||||
arguments: TextNodeWithEntities.Arguments(
|
|
||||||
context: context,
|
|
||||||
cache: context.animationCache,
|
|
||||||
renderer: context.animationRenderer,
|
|
||||||
placeholderColor: .clear,
|
|
||||||
attemptSynchronous: true
|
|
||||||
),
|
|
||||||
file: items.first?.file,
|
|
||||||
action: action)
|
|
||||||
return .single(tip)
|
|
||||||
} else {
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal)
|
let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal)
|
||||||
|
|
||||||
|
@ -2466,7 +2466,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func openPeerMention(view: StoryItemSetContainerComponent.View, name: String, navigation: ChatControllerInteractionNavigateToPeer = .info, sourceMessageId: MessageId? = nil) {
|
func openPeerMention(view: StoryItemSetContainerComponent.View, name: String, navigation: ChatControllerInteractionNavigateToPeer = .default, sourceMessageId: MessageId? = nil) {
|
||||||
guard let component = view.component, let parentController = component.controller() else {
|
guard let component = view.component, let parentController = component.controller() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import ComponentFlow
|
||||||
|
import AccountContext
|
||||||
|
import TelegramCore
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import AvatarNode
|
||||||
|
|
||||||
|
final class StoryPrivacyIconComponent: Component {
|
||||||
|
enum Privacy {
|
||||||
|
case everyone
|
||||||
|
case closeFriends
|
||||||
|
case contacts
|
||||||
|
case selectedContacts
|
||||||
|
}
|
||||||
|
let privacy: Privacy
|
||||||
|
let isEditable: Bool
|
||||||
|
|
||||||
|
init(privacy: Privacy, isEditable: Bool) {
|
||||||
|
self.privacy = privacy
|
||||||
|
self.isEditable = isEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: StoryPrivacyIconComponent, rhs: StoryPrivacyIconComponent) -> Bool {
|
||||||
|
if lhs.privacy != rhs.privacy {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isEditable != rhs.isEditable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
final class View: UIImageView {
|
||||||
|
private var component: StoryPrivacyIconComponent?
|
||||||
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
|
func update(component: StoryPrivacyIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
self.component = component
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
let size = CGSize(width: component.isEditable ? 40.0 : 24.0, height: 24.0)
|
||||||
|
self.image = generateImage(size, rotatedContext: { size, context in
|
||||||
|
let path: CGPath
|
||||||
|
if size.width == size.height {
|
||||||
|
path = CGPath(ellipseIn: CGRect(origin: .zero, size: size), transform: nil)
|
||||||
|
} else {
|
||||||
|
path = CGPath(roundedRect: CGRect(origin: .zero, size: size), cornerWidth: size.height / 2.0, cornerHeight: size.height / 2.0, transform: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.addPath(path)
|
||||||
|
context.clip()
|
||||||
|
|
||||||
|
var locations: [CGFloat] = [0.0, 1.0]
|
||||||
|
let colors: [CGColor]
|
||||||
|
let icon: UIImage
|
||||||
|
|
||||||
|
switch component.privacy {
|
||||||
|
case .everyone:
|
||||||
|
colors = [UIColor(rgb: 0x4faaff).cgColor, UIColor(rgb: 0x017aff).cgColor]
|
||||||
|
icon = UIImage()
|
||||||
|
case .closeFriends:
|
||||||
|
colors = [UIColor(rgb: 0x87d93a).cgColor, UIColor(rgb: 0x31b73b).cgColor]
|
||||||
|
icon = UIImage()
|
||||||
|
case .contacts:
|
||||||
|
colors = [UIColor(rgb: 0xc36eff).cgColor, UIColor(rgb: 0x8c61fa).cgColor]
|
||||||
|
icon = UIImage()
|
||||||
|
case .selectedContacts:
|
||||||
|
colors = [UIColor(rgb: 0xffb643).cgColor, UIColor(rgb: 0xf69a36).cgColor]
|
||||||
|
icon = UIImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
|
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||||
|
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||||
|
|
||||||
|
if let cgImage = icon.cgImage {
|
||||||
|
context.draw(cgImage, in: CGRect(origin: .zero, size: icon.size))
|
||||||
|
}
|
||||||
|
|
||||||
|
if component.isEditable {
|
||||||
|
let arrowIcon = UIImage()
|
||||||
|
if let cgImage = arrowIcon.cgImage {
|
||||||
|
context.draw(cgImage, in: CGRect(origin: .zero, size: icon.size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeView() -> View {
|
||||||
|
return View(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user