mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Stories improvements
This commit is contained in:
parent
0c091f6c1b
commit
9368d93053
@ -899,7 +899,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
||||||
|
|
||||||
func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode) -> ViewController
|
func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController
|
||||||
|
|
||||||
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
||||||
|
|
||||||
|
|||||||
@ -2566,10 +2566,42 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = self.context.engine.peers.togglePeerStoriesMuted(peerId: peer.id).start()
|
let _ = self.context.engine.peers.togglePeerStoriesMuted(peerId: peer.id).start()
|
||||||
|
|
||||||
|
let iconColor = UIColor.white
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
if isMuted {
|
||||||
|
self.present(UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [
|
||||||
|
"Middle.Group 1.Fill 1": iconColor,
|
||||||
|
"Top.Group 1.Fill 1": iconColor,
|
||||||
|
"Bottom.Group 1.Fill 1": iconColor,
|
||||||
|
"EXAMPLE.Group 1.Fill 1": iconColor,
|
||||||
|
"Line.Group 1.Stroke 1": iconColor
|
||||||
|
], title: nil, text: "You will now get a notification whenever **\(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))** posts a story.", customUndoText: nil, timeout: nil),
|
||||||
|
elevatedLayout: false,
|
||||||
|
animateInAsReplacement: false,
|
||||||
|
action: { _ in return false }
|
||||||
|
), in: .current)
|
||||||
|
} else {
|
||||||
|
self.present(UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [
|
||||||
|
"Middle.Group 1.Fill 1": iconColor,
|
||||||
|
"Top.Group 1.Fill 1": iconColor,
|
||||||
|
"Bottom.Group 1.Fill 1": iconColor,
|
||||||
|
"EXAMPLE.Group 1.Fill 1": iconColor,
|
||||||
|
"Line.Group 1.Stroke 1": iconColor
|
||||||
|
], title: nil, text: "You will no longer receive a notification when **\(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))** posts a story.", customUndoText: nil, timeout: nil),
|
||||||
|
elevatedLayout: false,
|
||||||
|
animateInAsReplacement: false,
|
||||||
|
action: { _ in return false }
|
||||||
|
), in: .current)
|
||||||
|
}
|
||||||
})))
|
})))
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: "Archive", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: "Hide", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MoveToContacts"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
|
|
||||||
@ -2577,6 +2609,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true)
|
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true)
|
||||||
|
|
||||||
|
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -370,6 +370,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
let highlightedPackId = controller.highlightedPackId
|
let highlightedPackId = controller.highlightedPackId
|
||||||
|
|
||||||
|
let forceTheme = controller.forceTheme
|
||||||
self.disposable = (combineLatest(queue: .mainQueue(),
|
self.disposable = (combineLatest(queue: .mainQueue(),
|
||||||
mappedFeatured,
|
mappedFeatured,
|
||||||
self.additionalPacks.get(),
|
self.additionalPacks.get(),
|
||||||
@ -377,6 +378,11 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
|||||||
context.sharedContext.presentationData
|
context.sharedContext.presentationData
|
||||||
)
|
)
|
||||||
|> map { featuredEntries, additionalPacks, view, presentationData -> FeaturedTransition in
|
|> map { featuredEntries, additionalPacks, view, presentationData -> FeaturedTransition in
|
||||||
|
var presentationData = presentationData
|
||||||
|
if let forceTheme {
|
||||||
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
|
}
|
||||||
|
|
||||||
var installedPacks = Set<ItemCollectionId>()
|
var installedPacks = Set<ItemCollectionId>()
|
||||||
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])] as? ItemCollectionInfosView {
|
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])] as? ItemCollectionInfosView {
|
||||||
if let packsEntries = stickerPacksView.entriesByNamespace[Namespaces.ItemCollection.CloudStickerPacks] {
|
if let packsEntries = stickerPacksView.entriesByNamespace[Namespaces.ItemCollection.CloudStickerPacks] {
|
||||||
@ -801,6 +807,7 @@ public final class FeaturedStickersScreen: ViewController {
|
|||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
fileprivate let forceTheme: PresentationTheme?
|
||||||
|
|
||||||
private let _ready = Promise<Bool>()
|
private let _ready = Promise<Bool>()
|
||||||
override public var ready: Promise<Bool> {
|
override public var ready: Promise<Bool> {
|
||||||
@ -809,12 +816,18 @@ public final class FeaturedStickersScreen: ViewController {
|
|||||||
|
|
||||||
fileprivate var searchNavigationNode: SearchNavigationContentNode?
|
fileprivate var searchNavigationNode: SearchNavigationContentNode?
|
||||||
|
|
||||||
public init(context: AccountContext, highlightedPackId: ItemCollectionId?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil) {
|
public init(context: AccountContext, highlightedPackId: ItemCollectionId?, forceTheme: PresentationTheme? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.highlightedPackId = highlightedPackId
|
self.highlightedPackId = highlightedPackId
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
|
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
var presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
if let forceTheme {
|
||||||
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.presentationData = presentationData
|
||||||
|
self.forceTheme = forceTheme
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||||
|
|
||||||
@ -833,6 +846,11 @@ public final class FeaturedStickersScreen: ViewController {
|
|||||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let previous = strongSelf.presentationData
|
let previous = strongSelf.presentationData
|
||||||
|
|
||||||
|
var presentationData = presentationData
|
||||||
|
if let forceTheme {
|
||||||
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
|
}
|
||||||
strongSelf.presentationData = presentationData
|
strongSelf.presentationData = presentationData
|
||||||
|
|
||||||
if previous.theme !== presentationData.theme || previous.strings !== presentationData.strings {
|
if previous.theme !== presentationData.theme || previous.strings !== presentationData.strings {
|
||||||
|
|||||||
@ -242,7 +242,7 @@ private func archivedStickerPacksControllerEntries(context: AccountContext, pres
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
public func archivedStickerPacksController(context: AccountContext, mode: ArchivedStickerPacksControllerMode, archived: [ArchivedStickerPackItem]?, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void) -> ViewController {
|
public func archivedStickerPacksController(context: AccountContext, mode: ArchivedStickerPacksControllerMode, archived: [ArchivedStickerPackItem]?, forceTheme: PresentationTheme? = nil, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void) -> ViewController {
|
||||||
let statePromise = ValuePromise(ArchivedStickerPacksControllerState(), ignoreRepeated: true)
|
let statePromise = ValuePromise(ArchivedStickerPacksControllerState(), ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: ArchivedStickerPacksControllerState())
|
let stateValue = Atomic(value: ArchivedStickerPacksControllerState())
|
||||||
let updateState: ((ArchivedStickerPacksControllerState) -> ArchivedStickerPacksControllerState) -> Void = { f in
|
let updateState: ((ArchivedStickerPacksControllerState) -> ArchivedStickerPacksControllerState) -> Void = { f in
|
||||||
@ -279,6 +279,11 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
|
|||||||
let installedStickerPacks = Promise<CombinedView>()
|
let installedStickerPacks = Promise<CombinedView>()
|
||||||
installedStickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionIds(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])]))
|
installedStickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionIds(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])]))
|
||||||
|
|
||||||
|
var presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
if let forceTheme {
|
||||||
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
|
}
|
||||||
|
|
||||||
var presentStickerPackController: ((StickerPackCollectionInfo) -> Void)?
|
var presentStickerPackController: ((StickerPackCollectionInfo) -> Void)?
|
||||||
|
|
||||||
let arguments = ArchivedStickerPacksControllerArguments(context: context, openStickerPack: { info in
|
let arguments = ArchivedStickerPacksControllerArguments(context: context, openStickerPack: { info in
|
||||||
@ -325,8 +330,6 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { info, items in
|
|> deliverOnMainQueue).start(next: { info, items in
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
|
|
||||||
var animateInAsReplacement = false
|
var animateInAsReplacement = false
|
||||||
if let navigationController = navigationControllerImpl?() {
|
if let navigationController = navigationControllerImpl?() {
|
||||||
for controller in navigationController.overlayControllers {
|
for controller in navigationController.overlayControllers {
|
||||||
@ -419,6 +422,11 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
|
|||||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, installedStickerPacks.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue)
|
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, installedStickerPacks.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { presentationData, state, packs, installedView, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, packs, installedView, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
var presentationData = presentationData
|
||||||
|
if let forceTheme {
|
||||||
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
|
}
|
||||||
|
|
||||||
var stickerSettings = StickerSettings.defaultSettings
|
var stickerSettings = StickerSettings.defaultSettings
|
||||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
|
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
|
||||||
stickerSettings = value
|
stickerSettings = value
|
||||||
|
|||||||
@ -616,7 +616,7 @@ private func installedStickerPacksControllerEntries(context: AccountContext, pre
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
public func installedStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, archivedPacks: [ArchivedStickerPackItem]? = nil, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void = { _ in }, focusOnItemTag: InstalledStickerPacksEntryTag? = nil) -> ViewController {
|
public func installedStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, archivedPacks: [ArchivedStickerPackItem]? = nil, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void = { _ in }, focusOnItemTag: InstalledStickerPacksEntryTag? = nil, forceTheme: PresentationTheme? = nil) -> ViewController {
|
||||||
let initialState = InstalledStickerPacksControllerState().withUpdatedEditing(mode == .modal).withUpdatedSelectedPackIds(mode == .modal ? Set() : nil)
|
let initialState = InstalledStickerPacksControllerState().withUpdatedEditing(mode == .modal).withUpdatedSelectedPackIds(mode == .modal ? Set() : nil)
|
||||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: initialState)
|
let stateValue = Atomic(value: initialState)
|
||||||
@ -624,6 +624,11 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
statePromise.set(stateValue.modify { f($0) })
|
statePromise.set(stateValue.modify { f($0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
if let forceTheme {
|
||||||
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
|
}
|
||||||
|
|
||||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||||
var pushControllerImpl: ((ViewController) -> Void)?
|
var pushControllerImpl: ((ViewController) -> Void)?
|
||||||
var navigateToChatControllerImpl: ((PeerId) -> Void)?
|
var navigateToChatControllerImpl: ((PeerId) -> Void)?
|
||||||
@ -650,7 +655,6 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, removePack: { archivedItem in
|
}, removePack: { archivedItem in
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let controller = ActionSheetController(presentationData: presentationData)
|
let controller = ActionSheetController(presentationData: presentationData)
|
||||||
let dismissAction: () -> Void = { [weak controller] in
|
let dismissAction: () -> Void = { [weak controller] in
|
||||||
controller?.dismissAnimated()
|
controller?.dismissAnimated()
|
||||||
@ -734,15 +738,15 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, openMasks: {
|
}, openMasks: {
|
||||||
pushControllerImpl?(installedStickerPacksController(context: context, mode: .masks, archivedPacks: archivedPacks, updatedPacks: { _ in}))
|
pushControllerImpl?(installedStickerPacksController(context: context, mode: .masks, archivedPacks: archivedPacks, updatedPacks: { _ in }, forceTheme: forceTheme))
|
||||||
}, openEmoji: {
|
}, openEmoji: {
|
||||||
pushControllerImpl?(installedStickerPacksController(context: context, mode: .emoji, archivedPacks: archivedPacks, updatedPacks: { _ in}))
|
pushControllerImpl?(installedStickerPacksController(context: context, mode: .emoji, archivedPacks: archivedPacks, updatedPacks: { _ in }, forceTheme: forceTheme))
|
||||||
}, openQuickReaction: {
|
}, openQuickReaction: {
|
||||||
pushControllerImpl?(quickReactionSetupController(
|
pushControllerImpl?(quickReactionSetupController(
|
||||||
context: context
|
context: context
|
||||||
))
|
))
|
||||||
}, openFeatured: {
|
}, openFeatured: {
|
||||||
pushControllerImpl?(FeaturedStickersScreen(context: context, highlightedPackId: nil))
|
pushControllerImpl?(FeaturedStickersScreen(context: context, highlightedPackId: nil, forceTheme: forceTheme))
|
||||||
}, openArchived: { archived in
|
}, openArchived: { archived in
|
||||||
let archivedMode: ArchivedStickerPacksControllerMode
|
let archivedMode: ArchivedStickerPacksControllerMode
|
||||||
switch mode {
|
switch mode {
|
||||||
@ -753,12 +757,11 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
default:
|
default:
|
||||||
archivedMode = .stickers
|
archivedMode = .stickers
|
||||||
}
|
}
|
||||||
pushControllerImpl?(archivedStickerPacksController(context: context, mode: archivedMode, archived: archived, updatedPacks: { packs in
|
pushControllerImpl?(archivedStickerPacksController(context: context, mode: archivedMode, archived: archived, forceTheme: forceTheme, updatedPacks: { packs in
|
||||||
archivedPromise.set(.single(packs))
|
archivedPromise.set(.single(packs))
|
||||||
updatedPacks(packs)
|
updatedPacks(packs)
|
||||||
}))
|
}))
|
||||||
}, openSuggestOptions: {
|
}, openSuggestOptions: {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let controller = ActionSheetController(presentationData: presentationData)
|
let controller = ActionSheetController(presentationData: presentationData)
|
||||||
let dismissAction: () -> Void = { [weak controller] in
|
let dismissAction: () -> Void = { [weak controller] in
|
||||||
controller?.dismissAnimated()
|
controller?.dismissAnimated()
|
||||||
@ -889,6 +892,11 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
)
|
)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { presentationData, state, view, temporaryPackOrder, featuredAndArchived, sharedData, quickReaction, availableReactions, emojiCount -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, view, temporaryPackOrder, featuredAndArchived, sharedData, quickReaction, availableReactions, emojiCount -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
var presentationData = presentationData
|
||||||
|
if let forceTheme {
|
||||||
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
|
}
|
||||||
|
|
||||||
var stickerSettings = StickerSettings.defaultSettings
|
var stickerSettings = StickerSettings.defaultSettings
|
||||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
|
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
|
||||||
stickerSettings = value
|
stickerSettings = value
|
||||||
|
|||||||
@ -609,7 +609,11 @@ private func apiInputPrivacyRules(privacy: EngineStoryPrivacy, transaction: Tran
|
|||||||
case .closeFriends:
|
case .closeFriends:
|
||||||
privacyRules = [.inputPrivacyValueAllowCloseFriends]
|
privacyRules = [.inputPrivacyValueAllowCloseFriends]
|
||||||
case .nobody:
|
case .nobody:
|
||||||
privacyRules = [.inputPrivacyValueDisallowAll]
|
if privacy.additionallyIncludePeers.isEmpty {
|
||||||
|
privacyRules = [.inputPrivacyValueDisallowAll]
|
||||||
|
} else {
|
||||||
|
privacyRules = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var privacyUsers: [Api.InputUser] = []
|
var privacyUsers: [Api.InputUser] = []
|
||||||
var privacyChats: [Int64] = []
|
var privacyChats: [Int64] = []
|
||||||
@ -833,7 +837,7 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: Int32, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy?) -> Signal<StoryUploadResult, NoError> {
|
func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: Int32, text: String?, entities: [MessageTextEntity]?, privacy: EngineStoryPrivacy?) -> Signal<StoryUploadResult, NoError> {
|
||||||
let contentSignal: Signal<PendingMessageUploadedContentResult?, NoError>
|
let contentSignal: Signal<PendingMessageUploadedContentResult?, NoError>
|
||||||
let originalMedia: Media?
|
let originalMedia: Media?
|
||||||
if let media = media {
|
if let media = media {
|
||||||
@ -865,13 +869,11 @@ func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: In
|
|||||||
if let _ = inputMedia {
|
if let _ = inputMedia {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
}
|
}
|
||||||
if !text.isEmpty {
|
if let text = text {
|
||||||
flags |= 1 << 1
|
flags |= 1 << 1
|
||||||
apiCaption = text
|
apiCaption = text
|
||||||
|
|
||||||
if !entities.isEmpty {
|
if let entities = entities {
|
||||||
flags |= 1 << 1
|
|
||||||
|
|
||||||
var associatedPeers: [PeerId: Peer] = [:]
|
var associatedPeers: [PeerId: Peer] = [:]
|
||||||
for entity in entities {
|
for entity in entities {
|
||||||
for entityPeerId in entity.associatedPeerIds {
|
for entityPeerId in entity.associatedPeerIds {
|
||||||
@ -1184,6 +1186,10 @@ extension Stories.StoredItem {
|
|||||||
for id in users {
|
for id in users {
|
||||||
additionalPeerIds.append(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)))
|
additionalPeerIds.append(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)))
|
||||||
}
|
}
|
||||||
|
case let .privacyValueDisallowUsers(users):
|
||||||
|
for id in users {
|
||||||
|
additionalPeerIds.append(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)))
|
||||||
|
}
|
||||||
case let .privacyValueAllowChatParticipants(chats):
|
case let .privacyValueAllowChatParticipants(chats):
|
||||||
for id in chats {
|
for id in chats {
|
||||||
if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudGroup, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
|
if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudGroup, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
|
||||||
|
|||||||
@ -939,7 +939,7 @@ public extension TelegramEngine {
|
|||||||
_internal_cancelStoryUpload(account: self.account, stableId: stableId)
|
_internal_cancelStoryUpload(account: self.account, stableId: stableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func editStory(media: EngineStoryInputMedia?, id: Int32, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy?) -> Signal<StoryUploadResult, NoError> {
|
public func editStory(media: EngineStoryInputMedia?, id: Int32, text: String?, entities: [MessageTextEntity]?, privacy: EngineStoryPrivacy?) -> Signal<StoryUploadResult, NoError> {
|
||||||
return _internal_editStory(account: self.account, media: media, id: id, text: text, entities: entities, privacy: privacy)
|
return _internal_editStory(account: self.account, media: media, id: id, text: text, entities: entities, privacy: privacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -88,6 +88,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
||||||
let getNavigationController: () -> NavigationController?
|
let getNavigationController: () -> NavigationController?
|
||||||
let requestLayout: (ContainedViewLayoutTransition) -> Void
|
let requestLayout: (ContainedViewLayoutTransition) -> Void
|
||||||
|
public var forceTheme: PresentationTheme?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool,
|
sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool,
|
||||||
@ -1333,6 +1334,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
interaction.getNavigationController()?.pushViewController(FeaturedStickersScreen(
|
interaction.getNavigationController()?.pushViewController(FeaturedStickersScreen(
|
||||||
context: context,
|
context: context,
|
||||||
highlightedPackId: featuredStickerPack.info.id,
|
highlightedPackId: featuredStickerPack.info.id,
|
||||||
|
forceTheme: interaction.forceTheme,
|
||||||
sendSticker: { [weak interaction] fileReference, sourceNode, sourceRect in
|
sendSticker: { [weak interaction] fileReference, sourceNode, sourceRect in
|
||||||
guard let interaction else {
|
guard let interaction else {
|
||||||
return false
|
return false
|
||||||
@ -1369,7 +1371,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
guard let interaction else {
|
guard let interaction else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = context.sharedContext.makeInstalledStickerPacksController(context: context, mode: .modal)
|
let controller = context.sharedContext.makeInstalledStickerPacksController(context: context, mode: .modal, forceTheme: interaction.forceTheme)
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
interaction.getNavigationController()?.pushViewController(controller)
|
interaction.getNavigationController()?.pushViewController(controller)
|
||||||
},
|
},
|
||||||
@ -1381,6 +1383,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
interaction.getNavigationController()?.pushViewController(FeaturedStickersScreen(
|
interaction.getNavigationController()?.pushViewController(FeaturedStickersScreen(
|
||||||
context: context,
|
context: context,
|
||||||
highlightedPackId: nil,
|
highlightedPackId: nil,
|
||||||
|
forceTheme: interaction.forceTheme,
|
||||||
sendSticker: { [weak interaction] fileReference, sourceNode, sourceRect in
|
sendSticker: { [weak interaction] fileReference, sourceNode, sourceRect in
|
||||||
guard let interaction else {
|
guard let interaction else {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -417,22 +417,22 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let view = self.saveButton.view {
|
if let view = self.saveButton.view {
|
||||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
view.layer.animateAlpha(from: 0.0, to: view.alpha, duration: 0.2)
|
||||||
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let view = self.muteButton.view {
|
if let view = self.muteButton.view {
|
||||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
view.layer.animateAlpha(from: 0.0, to: view.alpha, duration: 0.2)
|
||||||
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let view = self.settingsButton.view {
|
if let view = self.settingsButton.view {
|
||||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
view.layer.animateAlpha(from: 0.0, to: view.alpha, duration: 0.2)
|
||||||
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let view = self.privacyButton.view {
|
if let view = self.privacyButton.view {
|
||||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
view.layer.animateAlpha(from: 0.0, to: view.alpha, duration: 0.2)
|
||||||
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,6 +603,12 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
|
||||||
|
if self.component == nil {
|
||||||
|
if let controller = environment.controller() as? MediaEditorScreen {
|
||||||
|
self.inputPanelExternalState.initialText = controller.initialCaption
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
@ -1051,7 +1057,8 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
transition.setAlpha(view: inputPanelView, alpha: isEditingTextEntity || component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
|
transition.setAlpha(view: inputPanelView, alpha: isEditingTextEntity || component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
let privacyText: String
|
let additionalPeersCount = component.privacy.privacy.additionallyIncludePeers.count
|
||||||
|
var privacyText: String
|
||||||
switch component.privacy.privacy.base {
|
switch component.privacy.privacy.base {
|
||||||
case .everyone:
|
case .everyone:
|
||||||
privacyText = "Everyone"
|
privacyText = "Everyone"
|
||||||
@ -1059,8 +1066,16 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
privacyText = "Close Friends"
|
privacyText = "Close Friends"
|
||||||
case .contacts:
|
case .contacts:
|
||||||
privacyText = "Contacts"
|
privacyText = "Contacts"
|
||||||
|
if additionalPeersCount > 0 {
|
||||||
|
privacyText += " (-\(additionalPeersCount))"
|
||||||
|
}
|
||||||
case .nobody:
|
case .nobody:
|
||||||
privacyText = "Selected Contacts"
|
privacyText = "Selected Contacts"
|
||||||
|
if additionalPeersCount > 0 {
|
||||||
|
privacyText += " (\(additionalPeersCount))"
|
||||||
|
} else {
|
||||||
|
privacyText = "Only You"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayTopButtons = !(self.inputPanelExternalState.isEditing || isEditingTextEntity || component.isDisplayingTool)
|
let displayTopButtons = !(self.inputPanelExternalState.isEditing || isEditingTextEntity || component.isDisplayingTool)
|
||||||
@ -1549,6 +1564,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
private var isDismissed = false
|
private var isDismissed = false
|
||||||
private var isDismissBySwipeSuppressed = false
|
private var isDismissBySwipeSuppressed = false
|
||||||
|
|
||||||
|
fileprivate var hasAnyChanges = false
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
@ -1698,7 +1715,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let isSavingAvailable: Bool
|
let isSavingAvailable: Bool
|
||||||
switch subject {
|
switch subject {
|
||||||
case .image, .video:
|
case .image, .video:
|
||||||
isSavingAvailable = true
|
isSavingAvailable = !controller.isEditingStory
|
||||||
default:
|
default:
|
||||||
isSavingAvailable = false
|
isSavingAvailable = false
|
||||||
}
|
}
|
||||||
@ -1776,6 +1793,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
if !isSavingAvailable && controller.previousSavedValues == nil {
|
if !isSavingAvailable && controller.previousSavedValues == nil {
|
||||||
controller.previousSavedValues = values
|
controller.previousSavedValues = values
|
||||||
} else {
|
} else {
|
||||||
|
self.hasAnyChanges = true
|
||||||
|
|
||||||
controller.isSavingAvailable = true
|
controller.isSavingAvailable = true
|
||||||
controller.requestLayout(transition: .animated(duration: 0.25, curve: .easeInOut))
|
controller.requestLayout(transition: .animated(duration: 0.25, curve: .easeInOut))
|
||||||
}
|
}
|
||||||
@ -2258,11 +2277,17 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4, removeOnCompletion: false)
|
if controller.isEditingStory {
|
||||||
self.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4, removeOnCompletion: false, completion: { _ in
|
||||||
self.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: self.bounds.height), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { _ in
|
completion()
|
||||||
completion()
|
})
|
||||||
})
|
} else {
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4, removeOnCompletion: false)
|
||||||
|
self.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
|
self.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: self.bounds.height), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2566,6 +2591,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let stickerEntity = DrawingStickerEntity(content: .file(file))
|
let stickerEntity = DrawingStickerEntity(content: .file(file))
|
||||||
self.interaction?.insertEntity(stickerEntity)
|
self.interaction?.insertEntity(stickerEntity)
|
||||||
|
|
||||||
|
self.hasAnyChanges = true
|
||||||
self.controller?.isSavingAvailable = true
|
self.controller?.isSavingAvailable = true
|
||||||
self.controller?.requestLayout(transition: .immediate)
|
self.controller?.requestLayout(transition: .immediate)
|
||||||
}
|
}
|
||||||
@ -2585,6 +2611,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let textEntity = DrawingTextEntity(text: NSAttributedString(), style: .regular, animation: .none, font: .sanFrancisco, alignment: .center, fontSize: 1.0, color: DrawingColor(color: .white))
|
let textEntity = DrawingTextEntity(text: NSAttributedString(), style: .regular, animation: .none, font: .sanFrancisco, alignment: .center, fontSize: 1.0, color: DrawingColor(color: .white))
|
||||||
self.interaction?.insertEntity(textEntity)
|
self.interaction?.insertEntity(textEntity)
|
||||||
|
|
||||||
|
self.hasAnyChanges = true
|
||||||
self.controller?.isSavingAvailable = true
|
self.controller?.isSavingAvailable = true
|
||||||
self.controller?.requestLayout(transition: .immediate)
|
self.controller?.requestLayout(transition: .immediate)
|
||||||
return
|
return
|
||||||
@ -2817,17 +2844,22 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
case videoFile(path: String)
|
case videoFile(path: String)
|
||||||
case asset(localIdentifier: String)
|
case asset(localIdentifier: String)
|
||||||
}
|
}
|
||||||
case image(image: UIImage, dimensions: PixelDimensions, caption: NSAttributedString?)
|
case image(image: UIImage, dimensions: PixelDimensions)
|
||||||
case video(video: VideoResult, coverImage: UIImage?, values: MediaEditorValues, duration: Double, dimensions: PixelDimensions, caption: NSAttributedString?)
|
case video(video: VideoResult, coverImage: UIImage?, values: MediaEditorValues, duration: Double, dimensions: PixelDimensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate let context: AccountContext
|
fileprivate let context: AccountContext
|
||||||
fileprivate let subject: Signal<Subject?, NoError>
|
fileprivate let subject: Signal<Subject?, NoError>
|
||||||
|
fileprivate let isEditingStory: Bool
|
||||||
|
|
||||||
|
fileprivate let initialCaption: NSAttributedString?
|
||||||
|
fileprivate let initialPrivacy: EngineStoryPrivacy?
|
||||||
|
|
||||||
fileprivate let transitionIn: TransitionIn?
|
fileprivate let transitionIn: TransitionIn?
|
||||||
fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut?
|
fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut?
|
||||||
|
|
||||||
public var cancelled: (Bool) -> Void = { _ in }
|
public var cancelled: (Bool) -> Void = { _ in }
|
||||||
public var completion: (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _ in }
|
public var completion: (Int64, MediaEditorScreen.Result?, NSAttributedString, MediaEditorResultPrivacy , @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _, _ in }
|
||||||
public var dismissed: () -> Void = { }
|
public var dismissed: () -> Void = { }
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
@ -2835,12 +2867,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
subject: Signal<Subject?, NoError>,
|
subject: Signal<Subject?, NoError>,
|
||||||
|
isEditing: Bool,
|
||||||
|
initialCaption: NSAttributedString? = nil,
|
||||||
|
initialPrivacy: EngineStoryPrivacy? = nil,
|
||||||
transitionIn: TransitionIn?,
|
transitionIn: TransitionIn?,
|
||||||
transitionOut: @escaping (Bool, Bool?) -> TransitionOut?,
|
transitionOut: @escaping (Bool, Bool?) -> TransitionOut?,
|
||||||
completion: @escaping (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void
|
completion: @escaping (Int64, MediaEditorScreen.Result?, NSAttributedString, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
|
self.isEditingStory = isEditing
|
||||||
|
self.initialCaption = initialCaption
|
||||||
|
self.initialPrivacy = initialPrivacy
|
||||||
self.transitionIn = transitionIn
|
self.transitionIn = transitionIn
|
||||||
self.transitionOut = transitionOut
|
self.transitionOut = transitionOut
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
@ -2851,6 +2889,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
|
|
||||||
|
if let initialPrivacy {
|
||||||
|
self.state.privacy = MediaEditorResultPrivacy(privacy: initialPrivacy, timeout: 86400, archive: false)
|
||||||
|
}
|
||||||
|
|
||||||
self.automaticallyControlPresentationContextLayout = false
|
self.automaticallyControlPresentationContextLayout = false
|
||||||
|
|
||||||
self.navigationPresentation = .flatModal
|
self.navigationPresentation = .flatModal
|
||||||
@ -2894,6 +2936,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
ShareWithPeersScreen(
|
ShareWithPeersScreen(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
initialPrivacy: initialPrivacy,
|
initialPrivacy: initialPrivacy,
|
||||||
|
timeout: timeout,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] privacy in
|
completion: { [weak self] privacy in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -2928,6 +2971,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
ShareWithPeersScreen(
|
ShareWithPeersScreen(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
initialPrivacy: privacy,
|
initialPrivacy: privacy,
|
||||||
|
timeout: 0,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] result in
|
completion: { [weak self] result in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -2935,8 +2979,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
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()
|
||||||
|
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
||||||
|
} else {
|
||||||
|
completion(result)
|
||||||
}
|
}
|
||||||
completion(result)
|
|
||||||
},
|
},
|
||||||
editCategory: { _ in }
|
editCategory: { _ in }
|
||||||
)
|
)
|
||||||
@ -3051,6 +3097,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isEligibleForDraft() -> Bool {
|
func isEligibleForDraft() -> Bool {
|
||||||
|
if self.isEditingStory {
|
||||||
|
return false
|
||||||
|
}
|
||||||
guard let mediaEditor = self.node.mediaEditor else {
|
guard let mediaEditor = self.node.mediaEditor else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -3235,13 +3284,26 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
||||||
|
|
||||||
let caption = self.getCaption()
|
let caption = self.getCaption()
|
||||||
|
|
||||||
let randomId: Int64
|
let randomId: Int64
|
||||||
if case let .draft(_, id) = subject, let id {
|
if case let .draft(_, id) = subject, let id {
|
||||||
randomId = id
|
randomId = id
|
||||||
} else {
|
} else {
|
||||||
randomId = Int64.random(in: .min ... .max)
|
randomId = Int64.random(in: .min ... .max)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.isEditingStory && !self.node.hasAnyChanges {
|
||||||
|
self.completion(randomId, nil, caption, self.state.privacy, { [weak self] finished in
|
||||||
|
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
|
self?.dismiss()
|
||||||
|
Queue.mainQueue().justDispatch {
|
||||||
|
finished()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if mediaEditor.resultIsVideo {
|
if mediaEditor.resultIsVideo {
|
||||||
var firstFrame: Signal<UIImage?, NoError>
|
var firstFrame: Signal<UIImage?, NoError>
|
||||||
let firstFrameTime = CMTime(seconds: mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, preferredTimescale: CMTimeScale(60))
|
let firstFrameTime = CMTime(seconds: mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, preferredTimescale: CMTimeScale(60))
|
||||||
@ -3357,7 +3419,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
if let self {
|
if let self {
|
||||||
makeEditorImageComposition(account: self.context.account, inputImage: image ?? UIImage(), dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in
|
makeEditorImageComposition(account: self.context.account, inputImage: image ?? UIImage(), dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in
|
||||||
if let self {
|
if let self {
|
||||||
self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions, caption: caption), self.state.privacy, { [weak self] finished in
|
self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), caption, self.state.privacy, { [weak self] finished in
|
||||||
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
@ -3379,7 +3441,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in
|
makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in
|
||||||
if let self, let resultImage {
|
if let self, let resultImage {
|
||||||
self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size), caption: caption), self.state.privacy, { [weak self] finished in
|
self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), caption, self.state.privacy, { [weak self] finished in
|
||||||
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
|
|||||||
@ -268,7 +268,7 @@ private final class MediaToolsScreenComponent: Component {
|
|||||||
view.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: delay, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
view.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: delay, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: delay)
|
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: delay)
|
||||||
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: delay)
|
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: delay)
|
||||||
delay += 0.05
|
delay += 0.03
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -116,6 +116,7 @@ final class VideoScrubberComponent: Component {
|
|||||||
private let leftHandleView = HandleView()
|
private let leftHandleView = HandleView()
|
||||||
private let rightHandleView = HandleView()
|
private let rightHandleView = HandleView()
|
||||||
private let borderView = UIImageView()
|
private let borderView = UIImageView()
|
||||||
|
private let zoneView = HandleView()
|
||||||
private let cursorView = HandleView()
|
private let cursorView = HandleView()
|
||||||
|
|
||||||
private let transparentFramesContainer = UIView()
|
private let transparentFramesContainer = UIView()
|
||||||
@ -202,11 +203,13 @@ final class VideoScrubberComponent: Component {
|
|||||||
|
|
||||||
self.addSubview(self.transparentFramesContainer)
|
self.addSubview(self.transparentFramesContainer)
|
||||||
self.addSubview(self.opaqueFramesContainer)
|
self.addSubview(self.opaqueFramesContainer)
|
||||||
|
self.addSubview(self.zoneView)
|
||||||
self.addSubview(self.leftHandleView)
|
self.addSubview(self.leftHandleView)
|
||||||
self.addSubview(self.rightHandleView)
|
self.addSubview(self.rightHandleView)
|
||||||
self.addSubview(self.borderView)
|
self.addSubview(self.borderView)
|
||||||
self.addSubview(self.cursorView)
|
self.addSubview(self.cursorView)
|
||||||
|
|
||||||
|
self.zoneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleZoneHandlePan(_:))))
|
||||||
self.leftHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleLeftHandlePan(_:))))
|
self.leftHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleLeftHandlePan(_:))))
|
||||||
self.rightHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleRightHandlePan(_:))))
|
self.rightHandleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleRightHandlePan(_:))))
|
||||||
self.cursorView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePositionHandlePan(_:))))
|
self.cursorView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePositionHandlePan(_:))))
|
||||||
@ -225,6 +228,42 @@ final class VideoScrubberComponent: Component {
|
|||||||
self.displayLink?.invalidate()
|
self.displayLink?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func handleZoneHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
|
||||||
|
guard let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let translation = gestureRecognizer.translation(in: self)
|
||||||
|
|
||||||
|
let start = handleWidth / 2.0
|
||||||
|
let end = self.frame.width - handleWidth / 2.0
|
||||||
|
let length = end - start
|
||||||
|
|
||||||
|
let delta = translation.x / length
|
||||||
|
|
||||||
|
let duration = component.endPosition - component.startPosition
|
||||||
|
let startValue = max(0.0, min(component.duration - duration, component.startPosition + delta))
|
||||||
|
let endValue = startValue + duration
|
||||||
|
|
||||||
|
var transition: Transition = .immediate
|
||||||
|
switch gestureRecognizer.state {
|
||||||
|
case .began, .changed:
|
||||||
|
self.isPanningTrimHandle = true
|
||||||
|
component.trimUpdated(startValue, endValue, false, false)
|
||||||
|
if case .began = gestureRecognizer.state {
|
||||||
|
transition = .easeInOut(duration: 0.25)
|
||||||
|
}
|
||||||
|
case .ended, .cancelled:
|
||||||
|
self.isPanningTrimHandle = false
|
||||||
|
component.trimUpdated(startValue, endValue, false, true)
|
||||||
|
transition = .easeInOut(duration: 0.25)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
gestureRecognizer.setTranslation(.zero, in: self)
|
||||||
|
self.state?.updated(transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func handleLeftHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
|
@objc private func handleLeftHandlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
|
||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
@ -413,6 +452,9 @@ final class VideoScrubberComponent: Component {
|
|||||||
|
|
||||||
let rightHandleFrame = CGRect(origin: CGPoint(x: max(leftHandleFrame.maxX, rightHandlePosition - handleWidth / 2.0), y: 0.0), size: CGSize(width: handleWidth, height: scrubberSize.height))
|
let rightHandleFrame = CGRect(origin: CGPoint(x: max(leftHandleFrame.maxX, rightHandlePosition - handleWidth / 2.0), y: 0.0), size: CGSize(width: handleWidth, height: scrubberSize.height))
|
||||||
transition.setFrame(view: self.rightHandleView, frame: rightHandleFrame)
|
transition.setFrame(view: self.rightHandleView, frame: rightHandleFrame)
|
||||||
|
|
||||||
|
let zoneFrame = CGRect(x: leftHandleFrame.maxX, y: 0.0, width: rightHandleFrame.minX - leftHandleFrame.maxX, height: scrubberSize.height)
|
||||||
|
transition.setFrame(view: self.zoneView, frame: zoneFrame)
|
||||||
|
|
||||||
if self.isPanningPositionHandle || !component.isPlaying {
|
if self.isPanningPositionHandle || !component.isPlaying {
|
||||||
self.positionAnimation = nil
|
self.positionAnimation = nil
|
||||||
|
|||||||
@ -118,7 +118,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, inp
|
|||||||
let peers: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = context.engine.contacts.searchLocalPeers(query: normalizedQuery)
|
let peers: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = context.engine.contacts.searchLocalPeers(query: normalizedQuery)
|
||||||
|> map { peersAndPresences -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
|> map { peersAndPresences -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||||
let peers = peersAndPresences.filter { peer in
|
let peers = peersAndPresences.filter { peer in
|
||||||
if let peer = peer.peer, case .user = peer {
|
if let peer = peer.peer, case .user = peer, peer.addressName != nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -30,6 +30,8 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
public fileprivate(set) var hasText: Bool = false
|
public fileprivate(set) var hasText: Bool = false
|
||||||
public fileprivate(set) var isKeyboardHidden: Bool = false
|
public fileprivate(set) var isKeyboardHidden: Bool = false
|
||||||
|
|
||||||
|
public var initialText: NSAttributedString?
|
||||||
|
|
||||||
public fileprivate(set) var insertText: (NSAttributedString) -> Void = { _ in }
|
public fileprivate(set) var insertText: (NSAttributedString) -> Void = { _ in }
|
||||||
public fileprivate(set) var deleteBackward: () -> Void = { }
|
public fileprivate(set) var deleteBackward: () -> Void = { }
|
||||||
|
|
||||||
@ -366,6 +368,11 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
|
|
||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
if let initialText = component.externalState.initialText {
|
||||||
|
component.externalState.initialText = nil
|
||||||
|
self.textFieldExternalState.initialText = initialText
|
||||||
|
}
|
||||||
|
|
||||||
let hasMediaRecording = component.audioRecorder != nil || component.videoRecordingStatus != nil
|
let hasMediaRecording = component.audioRecorder != nil || component.videoRecordingStatus != nil
|
||||||
let hasMediaEditing = component.recordedAudioPreview != nil
|
let hasMediaEditing = component.recordedAudioPreview != nil
|
||||||
|
|||||||
@ -25,6 +25,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let stateContext: ShareWithPeersScreen.StateContext
|
let stateContext: ShareWithPeersScreen.StateContext
|
||||||
let initialPrivacy: EngineStoryPrivacy
|
let initialPrivacy: EngineStoryPrivacy
|
||||||
|
let timeout: Int
|
||||||
let categoryItems: [CategoryItem]
|
let categoryItems: [CategoryItem]
|
||||||
let completion: (EngineStoryPrivacy) -> Void
|
let completion: (EngineStoryPrivacy) -> Void
|
||||||
let editCategory: (EngineStoryPrivacy) -> Void
|
let editCategory: (EngineStoryPrivacy) -> Void
|
||||||
@ -34,6 +35,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
stateContext: ShareWithPeersScreen.StateContext,
|
stateContext: ShareWithPeersScreen.StateContext,
|
||||||
initialPrivacy: EngineStoryPrivacy,
|
initialPrivacy: EngineStoryPrivacy,
|
||||||
|
timeout: Int,
|
||||||
categoryItems: [CategoryItem],
|
categoryItems: [CategoryItem],
|
||||||
completion: @escaping (EngineStoryPrivacy) -> Void,
|
completion: @escaping (EngineStoryPrivacy) -> Void,
|
||||||
editCategory: @escaping (EngineStoryPrivacy) -> Void,
|
editCategory: @escaping (EngineStoryPrivacy) -> Void,
|
||||||
@ -42,6 +44,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.stateContext = stateContext
|
self.stateContext = stateContext
|
||||||
self.initialPrivacy = initialPrivacy
|
self.initialPrivacy = initialPrivacy
|
||||||
|
self.timeout = timeout
|
||||||
self.categoryItems = categoryItems
|
self.categoryItems = categoryItems
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
self.editCategory = editCategory
|
self.editCategory = editCategory
|
||||||
@ -58,6 +61,9 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
if lhs.initialPrivacy != rhs.initialPrivacy {
|
if lhs.initialPrivacy != rhs.initialPrivacy {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.timeout != rhs.timeout {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.categoryItems != rhs.categoryItems {
|
if lhs.categoryItems != rhs.categoryItems {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -237,6 +243,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
private var selectedPeers: [EnginePeer.Id] = []
|
private var selectedPeers: [EnginePeer.Id] = []
|
||||||
private var selectedCategories = Set<CategoryId>()
|
private var selectedCategories = Set<CategoryId>()
|
||||||
|
private var selectedPeersByCategory: [CategoryId: [EnginePeer.Id]] = [:]
|
||||||
|
|
||||||
private var component: ShareWithPeersScreenComponent?
|
private var component: ShareWithPeersScreenComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
@ -447,7 +454,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
let sectionTitle: String
|
let sectionTitle: String
|
||||||
if section.id == 0 {
|
if section.id == 0 {
|
||||||
sectionTitle = "WHO CAN VIEW FOR 24 HOURS"
|
let hours = component.timeout / 3600
|
||||||
|
sectionTitle = "WHO CAN VIEW FOR \(hours) HOURS"
|
||||||
} else {
|
} else {
|
||||||
if case .chats = component.stateContext.subject {
|
if case .chats = component.stateContext.subject {
|
||||||
sectionTitle = "CHATS"
|
sectionTitle = "CHATS"
|
||||||
@ -522,8 +530,14 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
if self.selectedCategories.contains(categoryId) {
|
if self.selectedCategories.contains(categoryId) {
|
||||||
} else {
|
} else {
|
||||||
self.selectedPeers = []
|
self.selectedPeers = []
|
||||||
|
|
||||||
self.selectedCategories.removeAll()
|
self.selectedCategories.removeAll()
|
||||||
self.selectedCategories.insert(categoryId)
|
self.selectedCategories.insert(categoryId)
|
||||||
|
|
||||||
|
if self.selectedPeers.isEmpty && categoryId == .selectedContacts {
|
||||||
|
component.editCategory(EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: []))
|
||||||
|
controller.dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.35, curve: .spring)))
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.35, curve: .spring)))
|
||||||
},
|
},
|
||||||
@ -1233,7 +1247,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
self.initialPeerIds = Set(selectedPeers.map { $0.id })
|
self.initialPeerIds = Set(selectedPeers.map { $0.id })
|
||||||
} else {
|
} else {
|
||||||
for peer in contactList.peers {
|
for peer in contactList.peers {
|
||||||
if case let .user(user) = peer, initialPeerIds.contains(user.id) {
|
if case let .user(user) = peer, initialPeerIds.contains(user.id), !user.isDeleted {
|
||||||
selectedPeers.append(peer)
|
selectedPeers.append(peer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1249,7 +1263,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
})
|
})
|
||||||
|
|
||||||
var peers: [EnginePeer] = []
|
var peers: [EnginePeer] = []
|
||||||
peers = contactList.peers.filter { !self.initialPeerIds.contains($0.id) && $0.id != context.account.peerId }.sorted(by: { lhs, rhs in
|
peers = contactList.peers.filter { !self.initialPeerIds.contains($0.id) && $0.id != context.account.peerId && !$0.isDeleted }.sorted(by: { lhs, rhs in
|
||||||
let result = lhs.indexName.isLessThan(other: rhs.indexName, ordering: .firstLast)
|
let result = lhs.indexName.isLessThan(other: rhs.indexName, ordering: .firstLast)
|
||||||
if result == .orderedSame {
|
if result == .orderedSame {
|
||||||
return lhs.id < rhs.id
|
return lhs.id < rhs.id
|
||||||
@ -1297,7 +1311,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
private var isDismissed: Bool = false
|
private var isDismissed: Bool = false
|
||||||
|
|
||||||
public init(context: AccountContext, initialPrivacy: EngineStoryPrivacy, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void, editCategory: @escaping (EngineStoryPrivacy) -> Void, secondaryAction: @escaping () -> Void = {}) {
|
public init(context: AccountContext, initialPrivacy: EngineStoryPrivacy, timeout: Int, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void, editCategory: @escaping (EngineStoryPrivacy) -> Void, secondaryAction: @escaping () -> Void = {}) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = []
|
var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = []
|
||||||
@ -1328,7 +1342,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
title: "Selected Contacts",
|
title: "Selected Contacts",
|
||||||
icon: "Chat List/Filters/Group",
|
icon: "Chat List/Filters/Group",
|
||||||
iconColor: .violet,
|
iconColor: .violet,
|
||||||
actionTitle: "edit list"
|
actionTitle: "choose"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1336,6 +1350,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
context: context,
|
context: context,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
initialPrivacy: initialPrivacy,
|
initialPrivacy: initialPrivacy,
|
||||||
|
timeout: timeout,
|
||||||
categoryItems: categoryItems,
|
categoryItems: categoryItems,
|
||||||
completion: completion,
|
completion: completion,
|
||||||
editCategory: editCategory,
|
editCategory: editCategory,
|
||||||
|
|||||||
@ -65,6 +65,8 @@ swift_library(
|
|||||||
"//submodules/OverlayStatusController",
|
"//submodules/OverlayStatusController",
|
||||||
"//submodules/Utils/VolumeButtons",
|
"//submodules/Utils/VolumeButtons",
|
||||||
"//submodules/TelegramUI/Components/PeerReportScreen",
|
"//submodules/TelegramUI/Components/PeerReportScreen",
|
||||||
|
"//submodules/LocalMediaResources",
|
||||||
|
"//submodules/SaveToCameraRoll",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -25,6 +25,9 @@ import PresentationDataUtils
|
|||||||
import PeerReportScreen
|
import PeerReportScreen
|
||||||
import ChatEntityKeyboardInputNode
|
import ChatEntityKeyboardInputNode
|
||||||
import TextFieldComponent
|
import TextFieldComponent
|
||||||
|
import TextFormat
|
||||||
|
import LocalMediaResources
|
||||||
|
import SaveToCameraRoll
|
||||||
|
|
||||||
public final class StoryItemSetContainerComponent: Component {
|
public final class StoryItemSetContainerComponent: Component {
|
||||||
public final class ExternalState {
|
public final class ExternalState {
|
||||||
@ -578,7 +581,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if let navigationController = component.controller()?.navigationController as? NavigationController {
|
if let navigationController = component.controller()?.navigationController as? NavigationController {
|
||||||
if !(navigationController.topViewController is StoryContainerScreen) {
|
let topViewController = navigationController.topViewController
|
||||||
|
if !(topViewController is StoryContainerScreen) && !(topViewController is MediaEditorScreen) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1222,7 +1226,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
items.append(.action(ContextMenuActionItem(text: component.slice.additionalPeerData.isMuted ? "Notify" : "Not Notify", icon: { theme in
|
|
||||||
|
let isMuted = component.slice.additionalPeerData.isMuted
|
||||||
|
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Not Notify", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: component.slice.additionalPeerData.isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: component.slice.additionalPeerData.isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, a in
|
}, action: { [weak self] _, a in
|
||||||
a(.default)
|
a(.default)
|
||||||
@ -1232,6 +1238,38 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = component.context.engine.peers.togglePeerStoriesMuted(peerId: component.slice.peer.id).start()
|
let _ = component.context.engine.peers.togglePeerStoriesMuted(peerId: component.slice.peer.id).start()
|
||||||
|
|
||||||
|
let iconColor = UIColor.white
|
||||||
|
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||||
|
if isMuted {
|
||||||
|
self.component?.presentController(UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [
|
||||||
|
"Middle.Group 1.Fill 1": iconColor,
|
||||||
|
"Top.Group 1.Fill 1": iconColor,
|
||||||
|
"Bottom.Group 1.Fill 1": iconColor,
|
||||||
|
"EXAMPLE.Group 1.Fill 1": iconColor,
|
||||||
|
"Line.Group 1.Stroke 1": iconColor
|
||||||
|
], title: nil, text: "You will now get a notification whenever **\(component.slice.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))** posts a story.", customUndoText: nil, timeout: nil),
|
||||||
|
elevatedLayout: false,
|
||||||
|
animateInAsReplacement: false,
|
||||||
|
action: { _ in return false }
|
||||||
|
), nil)
|
||||||
|
} else {
|
||||||
|
self.component?.presentController(UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [
|
||||||
|
"Middle.Group 1.Fill 1": iconColor,
|
||||||
|
"Top.Group 1.Fill 1": iconColor,
|
||||||
|
"Bottom.Group 1.Fill 1": iconColor,
|
||||||
|
"EXAMPLE.Group 1.Fill 1": iconColor,
|
||||||
|
"Line.Group 1.Stroke 1": iconColor
|
||||||
|
], title: nil, text: "You will no longer receive a notification when **\(component.slice.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))** posts a story.", customUndoText: nil, timeout: nil),
|
||||||
|
elevatedLayout: false,
|
||||||
|
animateInAsReplacement: false,
|
||||||
|
action: { _ in return false }
|
||||||
|
), nil)
|
||||||
|
}
|
||||||
})))
|
})))
|
||||||
|
|
||||||
var isHidden = false
|
var isHidden = false
|
||||||
@ -1240,7 +1278,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 ? "Unhide \(component.slice.peer.compactDisplayTitle)" : "Hide \(component.slice.peer.compactDisplayTitle)", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: component.slice.item.storyItem.isPinned ? "Chat/Context Menu/Archive" : "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: isHidden ? "Chat/Context Menu/MoveToChats" : "Chat/Context Menu/MoveToContacts"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, a in
|
}, action: { [weak self] _, a in
|
||||||
a(.default)
|
a(.default)
|
||||||
|
|
||||||
@ -1359,7 +1397,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
self.sendMessageContext.updateInputMediaNode(inputPanel: self.inputPanel, availableSize: availableSize, bottomInset: component.safeInsets.bottom, inputHeight: component.inputHeight, effectiveInputHeight: inputHeight, metrics: component.metrics, deviceMetrics: component.deviceMetrics, transition: transition)
|
self.sendMessageContext.updateInputMediaNode(inputPanel: self.inputPanel, availableSize: availableSize, bottomInset: component.safeInsets.bottom, inputHeight: component.inputHeight, effectiveInputHeight: inputHeight, metrics: component.metrics, deviceMetrics: component.deviceMetrics, transition: transition)
|
||||||
|
|
||||||
let bottomContentInsetWithoutInput = bottomContentInset
|
//let bottomContentInsetWithoutInput = bottomContentInset
|
||||||
var viewListInset: CGFloat = 0.0
|
var viewListInset: CGFloat = 0.0
|
||||||
|
|
||||||
var inputPanelBottomInset: CGFloat
|
var inputPanelBottomInset: CGFloat
|
||||||
@ -1505,14 +1543,10 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let privacyText: String
|
let privacyText: String
|
||||||
switch component.slice.item.storyItem.privacy?.base {
|
switch component.slice.item.storyItem.privacy?.base {
|
||||||
case .closeFriends:
|
case .closeFriends:
|
||||||
if additionalCount != 0 {
|
privacyText = "Close Friends"
|
||||||
privacyText = "Close Friends (+\(additionalCount)"
|
|
||||||
} else {
|
|
||||||
privacyText = "Close Friends"
|
|
||||||
}
|
|
||||||
case .contacts:
|
case .contacts:
|
||||||
if additionalCount != 0 {
|
if additionalCount != 0 {
|
||||||
privacyText = "Contacts (+\(additionalCount)"
|
privacyText = "Contacts (-\(additionalCount))"
|
||||||
} else {
|
} else {
|
||||||
privacyText = "Contacts"
|
privacyText = "Contacts"
|
||||||
}
|
}
|
||||||
@ -1565,8 +1599,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
let _ = component.context.engine.messages.updateStoriesArePinned(ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).start()
|
let _ = component.context.engine.messages.updateStoriesArePinned(ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).start()
|
||||||
|
|
||||||
|
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||||
if component.slice.item.storyItem.isPinned {
|
if component.slice.item.storyItem.isPinned {
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
|
||||||
self.component?.presentController(UndoOverlayController(
|
self.component?.presentController(UndoOverlayController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
content: .info(title: nil, text: "Story removed from your profile", timeout: nil),
|
content: .info(title: nil, text: "Story removed from your profile", timeout: nil),
|
||||||
@ -1575,7 +1609,6 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
} else {
|
} else {
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
|
||||||
self.component?.presentController(UndoOverlayController(
|
self.component?.presentController(UndoOverlayController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
content: .info(title: "Story saved to your profile", text: "Saved stories can be viewed by others on your profile until you remove them.", timeout: nil),
|
content: .info(title: "Story saved to your profile", text: "Saved stories can be viewed by others on your profile until you remove them.", timeout: nil),
|
||||||
@ -1833,7 +1866,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
transition.setFrame(layer: self.topContentGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: gradientHeight)))
|
transition.setFrame(layer: self.topContentGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: gradientHeight)))
|
||||||
transition.setAlpha(layer: self.topContentGradientLayer, alpha: (component.hideUI || self.displayViewList) ? 0.0 : 1.0)
|
transition.setAlpha(layer: self.topContentGradientLayer, alpha: (component.hideUI || self.displayViewList) ? 0.0 : 1.0)
|
||||||
|
|
||||||
let itemLayout = ItemLayout(size: CGSize(width: contentFrame.width, height: availableSize.height - component.containerInsets.top - 44.0 - bottomContentInsetWithoutInput))
|
let itemSize = CGSize(width: contentFrame.width, height: floorToScreenPixels(contentFrame.width * 1.77778))
|
||||||
|
let itemLayout = ItemLayout(size: itemSize) //ItemLayout(size: CGSize(width: contentFrame.width, height: availableSize.height - component.containerInsets.top - 44.0 - bottomContentInsetWithoutInput))
|
||||||
self.itemLayout = itemLayout
|
self.itemLayout = itemLayout
|
||||||
|
|
||||||
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
||||||
@ -2236,12 +2270,17 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
return contentSize
|
return contentSize
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openItemPrivacySettings() {
|
private func openItemPrivacySettings(initialPrivacy: EngineStoryPrivacy? = nil) {
|
||||||
guard let context = self.component?.context, let privacy = self.component?.slice.item.storyItem.privacy else {
|
guard let context = self.component?.context else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let stateContext = ShareWithPeersScreen.StateContext(context: context, subject: .stories)
|
let privacy = initialPrivacy ?? self.component?.slice.item.storyItem.privacy
|
||||||
|
guard let privacy else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let stateContext = ShareWithPeersScreen.StateContext(context: context, subject: .stories, initialPeerIds: Set(privacy.additionallyIncludePeers))
|
||||||
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 {
|
||||||
return
|
return
|
||||||
@ -2249,6 +2288,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let controller = ShareWithPeersScreen(
|
let controller = ShareWithPeersScreen(
|
||||||
context: context,
|
context: context,
|
||||||
initialPrivacy: privacy,
|
initialPrivacy: privacy,
|
||||||
|
timeout: 86400,
|
||||||
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 {
|
||||||
@ -2259,10 +2299,15 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
self.updateIsProgressPaused()
|
self.updateIsProgressPaused()
|
||||||
},
|
},
|
||||||
editCategory: { [weak self] privacy in
|
editCategory: { [weak self] privacy in
|
||||||
guard let self, let component = self.component else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = component.context.engine.messages.editStoryPrivacy(id: component.slice.item.storyItem.id, privacy: privacy).start()
|
self.openItemPrivacyCategory(privacy: privacy, completion: { [weak self] privacy in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.openItemPrivacySettings(initialPrivacy: privacy)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.component?.controller()?.push(controller)
|
self.component?.controller()?.push(controller)
|
||||||
@ -2272,6 +2317,38 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func openItemPrivacyCategory(privacy: EngineStoryPrivacy, completion: @escaping (EngineStoryPrivacy) -> Void) {
|
||||||
|
guard let context = self.component?.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let stateContext = ShareWithPeersScreen.StateContext(context: context, subject: .contacts(privacy.base), initialPeerIds: Set(privacy.additionallyIncludePeers))
|
||||||
|
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let controller = ShareWithPeersScreen(
|
||||||
|
context: context,
|
||||||
|
initialPrivacy: privacy,
|
||||||
|
timeout: 86400,
|
||||||
|
stateContext: stateContext,
|
||||||
|
completion: { result in
|
||||||
|
if case .closeFriends = privacy.base {
|
||||||
|
let _ = context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
||||||
|
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
||||||
|
} else {
|
||||||
|
completion(result)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editCategory: { _ in }
|
||||||
|
)
|
||||||
|
self.component?.controller()?.push(controller)
|
||||||
|
|
||||||
|
self.privacyController = controller
|
||||||
|
self.updateIsProgressPaused()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private func navigateToPeer(peer: EnginePeer, messageId: EngineMessage.Id? = nil) {
|
private func navigateToPeer(peer: EnginePeer, messageId: EngineMessage.Id? = nil) {
|
||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
@ -2300,48 +2377,227 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func openStoryEditing() {
|
private func openStoryEditing() {
|
||||||
guard let context = self.component?.context, let id = self.component?.slice.item.storyItem.id else {
|
guard let context = self.component?.context, let item = self.component?.slice.item.storyItem else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (getStorySource(engine: context.engine, id: Int64(id))
|
let id = item.id
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] source in
|
|
||||||
guard let self else {
|
self.isEditingStory = true
|
||||||
return
|
self.updateIsProgressPaused()
|
||||||
|
|
||||||
|
let subject: Signal<MediaEditorScreen.Subject?, NoError>
|
||||||
|
// if let source {
|
||||||
|
// subject = .single(.draft(source, Int64(id)))
|
||||||
|
// } else {
|
||||||
|
let media = item.media._asMedia()
|
||||||
|
subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .standalone(media: media))
|
||||||
|
|> mapToSignal { (value, isImage) -> Signal<MediaEditorScreen.Subject?, NoError> in
|
||||||
|
guard case let .data(data) = value, data.complete else {
|
||||||
|
return .complete()
|
||||||
}
|
}
|
||||||
self.isEditingStory = true
|
if let image = UIImage(contentsOfFile: data.path) {
|
||||||
self.updateIsProgressPaused()
|
return .single(nil)
|
||||||
|
|> then(
|
||||||
if let source {
|
.single(.image(image, PixelDimensions(image.size), nil, .bottomRight))
|
||||||
var updateProgressImpl: ((Float) -> Void)?
|
|> delay(0.2, queue: Queue.mainQueue())
|
||||||
let controller = MediaEditorScreen(
|
)
|
||||||
context: context,
|
} else {
|
||||||
subject: .single(.draft(source, Int64(id))),
|
return .single(.video(data.path, nil, nil, nil, PixelDimensions(width: 720, height: 1280), .bottomRight))
|
||||||
transitionIn: nil,
|
}
|
||||||
transitionOut: { _, _ in return nil },
|
}
|
||||||
completion: { [weak self] _, mediaResult, privacy, commit in
|
|
||||||
switch mediaResult {
|
var updateProgressImpl: ((Float) -> Void)?
|
||||||
case let .image(image, dimensions, caption):
|
let controller = MediaEditorScreen(
|
||||||
if let imageData = compressImageToJPEG(image, quality: 0.6) {
|
context: context,
|
||||||
let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData), id: id, text: caption?.string ?? "", entities: [], privacy: privacy.privacy)
|
subject: subject,
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
isEditing: true,
|
||||||
switch result {
|
initialCaption: chatInputStateStringWithAppliedEntities(item.text, entities: item.entities),
|
||||||
case let .progress(progress):
|
initialPrivacy: item.privacy,
|
||||||
updateProgressImpl?(progress)
|
transitionIn: nil,
|
||||||
case .completed:
|
transitionOut: { _, _ in return nil },
|
||||||
Queue.mainQueue().after(0.1) {
|
completion: { [weak self] _, mediaResult, caption, privacy, commit in
|
||||||
if let self {
|
guard let self else {
|
||||||
self.isEditingStory = false
|
return
|
||||||
self.rewindCurrentItem()
|
}
|
||||||
self.updateIsProgressPaused()
|
let entities = generateChatInputTextEntities(caption)
|
||||||
}
|
var updatedText: String?
|
||||||
commit({})
|
var updatedEntities: [MessageTextEntity]?
|
||||||
}
|
var updatedPrivacy: EngineStoryPrivacy?
|
||||||
}
|
if caption.string != item.text || entities != item.entities {
|
||||||
})
|
updatedText = caption.string
|
||||||
|
updatedEntities = entities
|
||||||
|
}
|
||||||
|
if privacy.privacy != item.privacy {
|
||||||
|
updatedPrivacy = privacy.privacy
|
||||||
|
}
|
||||||
|
|
||||||
|
if let mediaResult {
|
||||||
|
switch mediaResult {
|
||||||
|
case let .image(image, dimensions):
|
||||||
|
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
||||||
|
let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch result {
|
||||||
|
case let .progress(progress):
|
||||||
|
updateProgressImpl?(progress)
|
||||||
|
case .completed:
|
||||||
|
Queue.mainQueue().after(0.1) {
|
||||||
|
self.isEditingStory = false
|
||||||
|
self.rewindCurrentItem()
|
||||||
|
self.updateIsProgressPaused()
|
||||||
|
|
||||||
|
commit({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case let .video(content, firstFrameImage, values, duration, dimensions):
|
||||||
|
if let valuesData = try? JSONEncoder().encode(values) {
|
||||||
|
let data = MemoryBuffer(data: valuesData)
|
||||||
|
let digest = MemoryBuffer(data: data.md5Digest())
|
||||||
|
let adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true)
|
||||||
|
|
||||||
|
let resource: TelegramMediaResource
|
||||||
|
switch content {
|
||||||
|
case let .imageFile(path):
|
||||||
|
resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments)
|
||||||
|
case let .videoFile(path):
|
||||||
|
resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments)
|
||||||
|
case let .asset(localIdentifier):
|
||||||
|
resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments))
|
||||||
|
}
|
||||||
|
|
||||||
|
let firstFrameImageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) }
|
||||||
|
let _ = (context.engine.messages.editStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: firstFrameImageData), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch result {
|
||||||
|
case let .progress(progress):
|
||||||
|
updateProgressImpl?(progress)
|
||||||
|
case .completed:
|
||||||
|
Queue.mainQueue().after(0.1) {
|
||||||
|
self.isEditingStory = false
|
||||||
|
self.rewindCurrentItem()
|
||||||
|
self.updateIsProgressPaused()
|
||||||
|
|
||||||
|
commit({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if updatedText != nil || updatedPrivacy != nil {
|
||||||
|
let _ = (context.engine.messages.editStory(media: nil, id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
switch result {
|
||||||
|
case .completed:
|
||||||
|
Queue.mainQueue().after(0.1) {
|
||||||
|
if let self {
|
||||||
|
self.isEditingStory = false
|
||||||
|
self.rewindCurrentItem()
|
||||||
|
self.updateIsProgressPaused()
|
||||||
|
}
|
||||||
|
commit({})
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
// case let .video(content, _, values, duration, dimensions, caption):
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
self.isEditingStory = false
|
||||||
|
self.rewindCurrentItem()
|
||||||
|
self.updateIsProgressPaused()
|
||||||
|
|
||||||
|
commit({})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
controller.dismissed = { [weak self] in
|
||||||
|
self?.isEditingStory = false
|
||||||
|
self?.updateIsProgressPaused()
|
||||||
|
}
|
||||||
|
self.component?.controller()?.push(controller)
|
||||||
|
updateProgressImpl = { [weak controller] progress in
|
||||||
|
controller?.updateEditProgress(progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let _ = (getStorySource(engine: context.engine, id: Int64(id))
|
||||||
|
// |> deliverOnMainQueue).start(next: { [weak self] source in
|
||||||
|
// guard let self else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// self.isEditingStory = true
|
||||||
|
// self.updateIsProgressPaused()
|
||||||
|
//
|
||||||
|
// let subject: Signal<MediaEditorScreen.Subject?, NoError>
|
||||||
|
// if let source {
|
||||||
|
// subject = .single(.draft(source, Int64(id)))
|
||||||
|
// } else {
|
||||||
|
// let media = item.media._asMedia()
|
||||||
|
// subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .standalone(media: media))
|
||||||
|
// |> mapToSignal { (value, isImage) -> Signal<MediaEditorScreen.Subject?, NoError> in
|
||||||
|
// guard case let .data(data) = value, data.complete else {
|
||||||
|
// return .complete()
|
||||||
|
// }
|
||||||
|
// if let image = UIImage(contentsOfFile: data.path) {
|
||||||
|
// return .single(.image(image, PixelDimensions(image.size), nil, .bottomRight))
|
||||||
|
// } else {
|
||||||
|
// return .single(.video(data.path, nil, nil, nil, PixelDimensions(width: 720, height: 1280), .bottomRight))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var updateProgressImpl: ((Float) -> Void)?
|
||||||
|
// let controller = MediaEditorScreen(
|
||||||
|
// context: context,
|
||||||
|
// subject: subject,
|
||||||
|
// isEditing: true,
|
||||||
|
// transitionIn: nil,
|
||||||
|
// transitionOut: { _, _ in return nil },
|
||||||
|
// completion: { [weak self] randomId, mediaResult, caption, privacy, commit in
|
||||||
|
// let entities = generateChatInputTextEntities(caption)
|
||||||
|
// var updatedText: String?
|
||||||
|
// var updatedEntities: [MessageTextEntity]?
|
||||||
|
// var updatedPrivacy: EngineStoryPrivacy?
|
||||||
|
// if caption.string != item.text || entities != item.entities {
|
||||||
|
// updatedText = caption.string
|
||||||
|
// updatedEntities = entities
|
||||||
|
// }
|
||||||
|
// if privacy.privacy != item.privacy {
|
||||||
|
// updatedPrivacy = privacy.privacy
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if let mediaResult {
|
||||||
|
// switch mediaResult {
|
||||||
|
// case let .image(image, dimensions, caption):
|
||||||
|
// if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
||||||
|
// let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy)
|
||||||
|
// |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
// switch result {
|
||||||
|
// case let .progress(progress):
|
||||||
|
// updateProgressImpl?(progress)
|
||||||
|
// case .completed:
|
||||||
|
// Queue.mainQueue().after(0.1) {
|
||||||
|
// if let self {
|
||||||
|
// self.isEditingStory = false
|
||||||
|
// self.rewindCurrentItem()
|
||||||
|
// self.updateIsProgressPaused()
|
||||||
|
// }
|
||||||
|
// commit({})
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// case let .video(content, firstFrameImage, values, duration, dimensions, caption):
|
||||||
// let adjustments: VideoMediaResourceAdjustments
|
// let adjustments: VideoMediaResourceAdjustments
|
||||||
// if let valuesData = try? JSONEncoder().encode(values) {
|
// if let valuesData = try? JSONEncoder().encode(values) {
|
||||||
// let data = MemoryBuffer(data: valuesData)
|
// let data = MemoryBuffer(data: valuesData)
|
||||||
@ -2357,41 +2613,62 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
// case let .asset(localIdentifier):
|
// case let .asset(localIdentifier):
|
||||||
// resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments))
|
// resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments))
|
||||||
// }
|
// }
|
||||||
// if case let .story(storyPrivacy, period, pin) = privacy {
|
// let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) }
|
||||||
// let _ = (context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy, period: period, randomId: randomId)
|
//
|
||||||
// |> deliverOnMainQueue).start(next: { [weak chatListController] result in
|
// let _ = (context.engine.messages.editStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy)
|
||||||
// if let chatListController {
|
// |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
// switch result {
|
// switch result {
|
||||||
// case let .progress(progress):
|
// case let .progress(progress):
|
||||||
// let _ = progress
|
// updateProgressImpl?(progress)
|
||||||
// break
|
// case .completed:
|
||||||
// case .completed:
|
// Queue.mainQueue().after(0.1) {
|
||||||
// Queue.mainQueue().after(0.1) {
|
// if let self {
|
||||||
// commit()
|
// self.isEditingStory = false
|
||||||
// }
|
// self.rewindCurrentItem()
|
||||||
|
// self.updateIsProgressPaused()
|
||||||
// }
|
// }
|
||||||
|
// commit({})
|
||||||
// }
|
// }
|
||||||
// })
|
|
||||||
// Queue.mainQueue().justDispatch {
|
|
||||||
// commit({ [weak chatListController] in
|
|
||||||
// chatListController?.animateStoryUploadRipple()
|
|
||||||
// })
|
|
||||||
// }
|
// }
|
||||||
// }
|
// })
|
||||||
// }
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// } else if updatedText != nil || updatedPrivacy != nil {
|
||||||
)
|
// let _ = (context.engine.messages.editStory(media: nil, id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy)
|
||||||
controller.dismissed = { [weak self] in
|
// |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
self?.isEditingStory = false
|
// switch result {
|
||||||
self?.updateIsProgressPaused()
|
// case .completed:
|
||||||
}
|
// Queue.mainQueue().after(0.1) {
|
||||||
self.component?.controller()?.push(controller)
|
// if let self {
|
||||||
updateProgressImpl = { [weak controller] progress in
|
// self.isEditingStory = false
|
||||||
controller?.updateEditProgress(progress)
|
// self.rewindCurrentItem()
|
||||||
}
|
// self.updateIsProgressPaused()
|
||||||
}
|
// }
|
||||||
})
|
// commit({})
|
||||||
|
// }
|
||||||
|
// default:
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// if let self {
|
||||||
|
// self.isEditingStory = false
|
||||||
|
// self.rewindCurrentItem()
|
||||||
|
// self.updateIsProgressPaused()
|
||||||
|
// }
|
||||||
|
// commit({})
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// controller.dismissed = { [weak self] in
|
||||||
|
// self?.isEditingStory = false
|
||||||
|
// self?.updateIsProgressPaused()
|
||||||
|
// }
|
||||||
|
// self.component?.controller()?.push(controller)
|
||||||
|
// updateProgressImpl = { [weak controller] progress in
|
||||||
|
// controller?.updateEditProgress(progress)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -169,6 +169,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
self.inputMediaInteraction?.forceTheme = defaultDarkColorPresentationTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleInputMode() {
|
func toggleInputMode() {
|
||||||
|
|||||||
@ -603,8 +603,7 @@ final class StoryItemSetViewListComponent: Component {
|
|||||||
moreAction: { [weak self] sourceView, gesture in
|
moreAction: { [weak self] sourceView, gesture in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
component.moreAction(sourceView, gesture)
|
component.moreAction(sourceView, gesture)
|
||||||
}
|
}
|
||||||
)),
|
)),
|
||||||
|
|||||||
@ -20,6 +20,7 @@ public final class TextFieldComponent: Component {
|
|||||||
public final class ExternalState {
|
public final class ExternalState {
|
||||||
public fileprivate(set) var isEditing: Bool = false
|
public fileprivate(set) var isEditing: Bool = false
|
||||||
public fileprivate(set) var hasText: Bool = false
|
public fileprivate(set) var hasText: Bool = false
|
||||||
|
public var initialText: NSAttributedString?
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
@ -92,6 +93,17 @@ public final class TextFieldComponent: Component {
|
|||||||
public struct InputState {
|
public struct InputState {
|
||||||
public var inputText: NSAttributedString
|
public var inputText: NSAttributedString
|
||||||
public var selectionRange: Range<Int>
|
public var selectionRange: Range<Int>
|
||||||
|
|
||||||
|
public init(inputText: NSAttributedString, selectionRange: Range<Int>) {
|
||||||
|
self.inputText = inputText
|
||||||
|
self.selectionRange = selectionRange
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(inputText: NSAttributedString) {
|
||||||
|
self.inputText = inputText
|
||||||
|
let length = inputText.length
|
||||||
|
self.selectionRange = length ..< length
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class View: UIView, UITextViewDelegate, UIScrollViewDelegate {
|
public final class View: UIView, UITextViewDelegate, UIScrollViewDelegate {
|
||||||
@ -104,8 +116,6 @@ public final class TextFieldComponent: Component {
|
|||||||
private var customEmojiContainerView: CustomEmojiContainerView?
|
private var customEmojiContainerView: CustomEmojiContainerView?
|
||||||
private var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
private var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
||||||
|
|
||||||
//private var inputState = InputState(inputText: NSAttributedString(), selectionRange: 0 ..< 0)
|
|
||||||
|
|
||||||
private var inputState: InputState {
|
private var inputState: InputState {
|
||||||
let selectionRange: Range<Int> = self.textView.selectedRange.location ..< (self.textView.selectedRange.location + self.textView.selectedRange.length)
|
let selectionRange: Range<Int> = self.textView.selectedRange.location ..< (self.textView.selectedRange.location + self.textView.selectedRange.length)
|
||||||
return InputState(inputText: stateAttributedStringForText(self.textView.attributedText ?? NSAttributedString()), selectionRange: selectionRange)
|
return InputState(inputText: stateAttributedStringForText(self.textView.attributedText ?? NSAttributedString()), selectionRange: selectionRange)
|
||||||
@ -445,6 +455,13 @@ public final class TextFieldComponent: Component {
|
|||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
if let initialText = component.externalState.initialText {
|
||||||
|
component.externalState.initialText = nil
|
||||||
|
self.updateInputState { _ in
|
||||||
|
return TextFieldComponent.InputState(inputText: initialText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.emojiViewProvider == nil {
|
if self.emojiViewProvider == nil {
|
||||||
self.emojiViewProvider = { [weak self] emoji in
|
self.emojiViewProvider = { [weak self] emoji in
|
||||||
guard let component = self?.component else {
|
guard let component = self?.component else {
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/MoveToChats.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/MoveToChats.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "movetochats_24.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
191
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/MoveToChats.imageset/movetochats_24.pdf
vendored
Normal file
191
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/MoveToChats.imageset/movetochats_24.pdf
vendored
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.335022 2.295349 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
6.142120 15.779264 m
|
||||||
|
7.480694 17.157986 9.452373 18.039629 11.664999 18.039629 c
|
||||||
|
15.777071 18.039629 19.000000 15.023025 19.000000 11.431902 c
|
||||||
|
19.000000 7.840779 15.777071 4.824175 11.664999 4.824175 c
|
||||||
|
11.037781 4.824175 10.429922 4.895034 9.850378 5.027961 c
|
||||||
|
9.469110 5.115411 9.178260 4.904372 9.078142 4.829156 c
|
||||||
|
8.976803 4.753022 8.861000 4.645836 8.759948 4.552301 c
|
||||||
|
8.759891 4.552247 l
|
||||||
|
8.759882 4.552238 l
|
||||||
|
8.699350 4.496364 l
|
||||||
|
8.446663 4.264504 8.099366 3.961192 7.552770 3.654654 c
|
||||||
|
7.352402 3.542285 7.115053 3.463325 6.872348 3.411900 c
|
||||||
|
6.929290 3.488448 6.986062 3.569141 7.041155 3.653596 c
|
||||||
|
7.389184 4.187105 7.489088 4.765461 7.466393 5.236623 c
|
||||||
|
7.448723 5.603467 7.137013 5.886529 6.770169 5.868859 c
|
||||||
|
6.403325 5.851189 6.120263 5.539478 6.137933 5.172635 c
|
||||||
|
6.149623 4.929938 6.096569 4.639868 5.927217 4.380261 c
|
||||||
|
5.748320 4.106021 5.559394 3.900901 5.381646 3.707916 c
|
||||||
|
5.381536 3.707796 l
|
||||||
|
5.337605 3.660100 5.294358 3.613144 5.252113 3.566068 c
|
||||||
|
5.201833 3.510037 5.145585 3.445241 5.095825 3.378344 c
|
||||||
|
5.050557 3.317486 4.981599 3.217117 4.936990 3.090534 c
|
||||||
|
4.889143 2.954765 4.849132 2.725258 4.970338 2.484573 c
|
||||||
|
5.083405 2.260048 5.273345 2.155308 5.385351 2.108326 c
|
||||||
|
5.486843 2.065754 5.591929 2.046288 5.659274 2.035854 c
|
||||||
|
5.739243 2.023464 5.827882 2.014999 5.920462 2.010006 c
|
||||||
|
6.106138 1.999989 6.331503 2.002670 6.575747 2.024937 c
|
||||||
|
7.054850 2.068615 7.665498 2.193001 8.203329 2.494621 c
|
||||||
|
8.876777 2.872299 9.310046 3.251671 9.598545 3.516390 c
|
||||||
|
9.688824 3.599475 l
|
||||||
|
9.724767 3.632614 9.754330 3.659869 9.779459 3.682659 c
|
||||||
|
10.387117 3.559157 11.018166 3.494175 11.664999 3.494175 c
|
||||||
|
16.389484 3.494175 20.329998 6.989792 20.329998 11.431902 c
|
||||||
|
20.329998 15.874012 16.389484 19.369629 11.664999 19.369629 c
|
||||||
|
9.098855 19.369629 6.781081 18.346710 5.187877 16.705719 c
|
||||||
|
4.932044 16.442213 4.938264 16.021204 5.201771 15.765370 c
|
||||||
|
5.465278 15.509537 5.886287 15.515757 6.142120 15.779264 c
|
||||||
|
h
|
||||||
|
5.843703 3.353676 m
|
||||||
|
5.847516 3.352829 l
|
||||||
|
5.844881 3.353352 5.843657 3.353655 5.843703 3.353676 c
|
||||||
|
h
|
||||||
|
6.159736 2.580116 m
|
||||||
|
6.157165 2.576271 l
|
||||||
|
6.157100 2.576252 6.157888 2.577515 6.159736 2.580116 c
|
||||||
|
h
|
||||||
|
9.892341 3.775630 m
|
||||||
|
9.895166 3.777505 9.896662 3.778707 9.896752 3.778999 c
|
||||||
|
9.896927 3.779562 9.891918 3.776763 9.881212 3.768926 c
|
||||||
|
9.886010 3.771608 9.889739 3.773905 9.892341 3.775630 c
|
||||||
|
h
|
||||||
|
20.929787 12.381819 m
|
||||||
|
20.751503 12.702913 20.867275 13.107740 21.188370 13.286022 c
|
||||||
|
21.509464 13.464306 21.914291 13.348534 22.092573 13.027439 c
|
||||||
|
22.691111 11.949450 23.030014 10.727039 23.030014 9.431902 c
|
||||||
|
23.030014 6.938105 21.869383 4.889858 19.940552 3.448150 c
|
||||||
|
19.940319 3.447509 l
|
||||||
|
19.919962 3.390541 19.898252 3.296368 19.892189 3.174892 c
|
||||||
|
19.880058 2.931812 19.932896 2.640709 20.102795 2.380260 c
|
||||||
|
20.281693 2.106022 20.470617 1.900900 20.648365 1.707916 c
|
||||||
|
20.692333 1.660179 20.735619 1.613182 20.777899 1.566067 c
|
||||||
|
20.828178 1.510036 20.884426 1.445240 20.934187 1.378344 c
|
||||||
|
20.979456 1.317486 21.048412 1.217117 21.093021 1.090534 c
|
||||||
|
21.140869 0.954765 21.180880 0.725258 21.059673 0.484573 c
|
||||||
|
20.946608 0.260048 20.756666 0.155308 20.644661 0.108326 c
|
||||||
|
20.543169 0.065756 20.438084 0.046288 20.370739 0.035854 c
|
||||||
|
20.290770 0.023464 20.202129 0.014999 20.109550 0.010004 c
|
||||||
|
19.923874 -0.000013 19.698509 0.002670 19.454264 0.024937 c
|
||||||
|
18.975163 0.068615 18.364513 0.193001 17.826683 0.494623 c
|
||||||
|
17.153234 0.872299 16.719965 1.251671 16.431467 1.516390 c
|
||||||
|
16.341227 1.599442 l
|
||||||
|
16.340408 1.600197 l
|
||||||
|
16.339453 1.601078 l
|
||||||
|
16.304291 1.633493 16.275278 1.660238 16.250553 1.682659 c
|
||||||
|
15.642897 1.559156 15.011847 1.494175 14.365013 1.494175 c
|
||||||
|
13.738460 1.494175 13.126695 1.555145 12.536637 1.671227 c
|
||||||
|
12.176275 1.742119 11.941614 2.091721 12.012507 2.452084 c
|
||||||
|
12.083401 2.812447 12.433002 3.047108 12.793365 2.976213 c
|
||||||
|
13.299057 2.876730 13.824845 2.824175 14.365013 2.824175 c
|
||||||
|
14.992231 2.824175 15.600090 2.895035 16.179634 3.027962 c
|
||||||
|
16.560902 3.115410 16.851753 2.904373 16.951870 2.829155 c
|
||||||
|
17.053213 2.753019 17.169022 2.645828 17.270073 2.552288 c
|
||||||
|
17.270130 2.552237 l
|
||||||
|
17.330662 2.496365 l
|
||||||
|
17.583349 2.264503 17.930645 1.961193 18.477242 1.654654 c
|
||||||
|
18.677610 1.542286 18.914959 1.463326 19.157663 1.411900 c
|
||||||
|
19.100721 1.488449 19.043949 1.569141 18.988857 1.653595 c
|
||||||
|
18.639727 2.188793 18.540283 2.769123 18.563843 3.241184 c
|
||||||
|
18.575624 3.477282 18.618753 3.701606 18.687880 3.895054 c
|
||||||
|
18.746796 4.059927 18.860445 4.305161 19.081284 4.466843 c
|
||||||
|
20.750088 5.688613 21.700012 7.375656 21.700012 9.431902 c
|
||||||
|
21.700012 10.491108 21.423624 11.492397 20.929787 12.381819 c
|
||||||
|
h
|
||||||
|
19.953547 3.480234 m
|
||||||
|
19.956055 3.485239 l
|
||||||
|
19.957342 3.487652 19.958071 3.488707 19.958145 3.488573 c
|
||||||
|
19.958214 3.488447 19.957697 3.487259 19.956514 3.485152 c
|
||||||
|
19.953547 3.480234 l
|
||||||
|
h
|
||||||
|
16.133259 1.778999 m
|
||||||
|
16.133430 1.778439 16.138786 1.774525 16.148804 1.768925 c
|
||||||
|
16.138100 1.776760 16.133089 1.779560 16.133259 1.778999 c
|
||||||
|
h
|
||||||
|
20.186310 1.353676 m
|
||||||
|
20.182497 1.352829 l
|
||||||
|
20.185133 1.353352 20.186356 1.353657 20.186310 1.353676 c
|
||||||
|
h
|
||||||
|
19.870279 0.580111 m
|
||||||
|
19.872847 0.576271 l
|
||||||
|
19.872919 0.576242 19.872131 0.577499 19.870279 0.580111 c
|
||||||
|
h
|
||||||
|
5.559548 10.869629 m
|
||||||
|
3.694774 12.734404 l
|
||||||
|
3.435075 12.994102 3.435075 13.415156 3.694774 13.674854 c
|
||||||
|
3.954473 13.934553 4.375527 13.934553 4.635226 13.674854 c
|
||||||
|
7.635226 10.674855 l
|
||||||
|
7.894925 10.415156 7.894925 9.994102 7.635226 9.734403 c
|
||||||
|
4.635226 6.734404 l
|
||||||
|
4.375527 6.474705 3.954473 6.474705 3.694774 6.734404 c
|
||||||
|
3.435075 6.994102 3.435075 7.415156 3.694774 7.674854 c
|
||||||
|
5.559548 9.539629 l
|
||||||
|
0.665000 9.539629 l
|
||||||
|
0.297730 9.539629 0.000000 9.837359 0.000000 10.204629 c
|
||||||
|
0.000000 10.571898 0.297730 10.869629 0.665000 10.869629 c
|
||||||
|
5.559548 10.869629 l
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
5655
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000005745 00000 n
|
||||||
|
0000005768 00000 n
|
||||||
|
0000005941 00000 n
|
||||||
|
0000006015 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
6074
|
||||||
|
%%EOF
|
||||||
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/MoveToContacts.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/MoveToContacts.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "movetocontacts_24.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.335022 3.335083 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
11.664982 15.999956 m
|
||||||
|
9.732011 15.999956 7.974946 15.253136 6.664206 14.031046 c
|
||||||
|
6.395582 13.780590 5.974784 13.795319 5.724329 14.063942 c
|
||||||
|
5.473873 14.332565 5.488601 14.753363 5.757225 15.003819 c
|
||||||
|
7.304427 16.446377 9.382257 17.329956 11.664982 17.329956 c
|
||||||
|
16.450529 17.329956 20.329983 13.450504 20.329983 8.664956 c
|
||||||
|
20.329983 3.879409 16.450529 -0.000046 11.664982 -0.000046 c
|
||||||
|
9.467892 -0.000046 7.460371 0.818539 5.933279 2.166380 c
|
||||||
|
5.881387 2.212180 5.830048 2.258594 5.779273 2.305608 c
|
||||||
|
5.521967 2.543855 5.495585 2.938190 5.710920 3.207923 c
|
||||||
|
6.184868 3.968952 7.955641 6.329956 11.664997 6.329956 c
|
||||||
|
13.847623 6.329956 15.371711 5.506486 16.347773 4.669862 c
|
||||||
|
16.690248 4.376310 16.964067 4.082387 17.175648 3.823866 c
|
||||||
|
18.311298 5.115568 18.999981 6.809836 18.999981 8.664956 c
|
||||||
|
18.999981 12.715965 15.715990 15.999956 11.664982 15.999956 c
|
||||||
|
h
|
||||||
|
16.207489 2.905417 m
|
||||||
|
16.032719 3.127470 15.793164 3.393527 15.482220 3.660050 c
|
||||||
|
14.708282 4.323426 13.482370 4.999956 11.664997 4.999956 c
|
||||||
|
9.138727 4.999956 7.731341 3.681140 7.121977 2.905769 c
|
||||||
|
8.371500 1.918601 9.948810 1.329956 11.664982 1.329956 c
|
||||||
|
13.380391 1.329956 14.958264 1.918815 16.207489 2.905417 c
|
||||||
|
h
|
||||||
|
11.665061 12.499956 m
|
||||||
|
10.651619 12.499956 9.830061 11.678398 9.830061 10.664956 c
|
||||||
|
9.830061 9.651514 10.651619 8.829956 11.665061 8.829956 c
|
||||||
|
12.678503 8.829956 13.500061 9.651514 13.500061 10.664956 c
|
||||||
|
13.500061 11.678398 12.678503 12.499956 11.665061 12.499956 c
|
||||||
|
h
|
||||||
|
8.500061 10.664956 m
|
||||||
|
8.500061 12.412937 9.917080 13.829956 11.665061 13.829956 c
|
||||||
|
13.413042 13.829956 14.830061 12.412937 14.830061 10.664956 c
|
||||||
|
14.830061 8.916975 13.413042 7.499956 11.665061 7.499956 c
|
||||||
|
9.917080 7.499956 8.500061 8.916975 8.500061 10.664956 c
|
||||||
|
h
|
||||||
|
5.559548 7.999956 m
|
||||||
|
3.694774 6.135181 l
|
||||||
|
3.435075 5.875484 3.435075 5.454429 3.694774 5.194731 c
|
||||||
|
3.954473 4.935032 4.375527 4.935032 4.635226 5.194731 c
|
||||||
|
7.635226 8.194730 l
|
||||||
|
7.894925 8.454429 7.894925 8.875484 7.635226 9.135182 c
|
||||||
|
4.635226 12.135181 l
|
||||||
|
4.375527 12.394880 3.954473 12.394880 3.694774 12.135181 c
|
||||||
|
3.435075 11.875484 3.435075 11.454429 3.694774 11.194731 c
|
||||||
|
5.559548 9.329956 l
|
||||||
|
0.665000 9.329956 l
|
||||||
|
0.297730 9.329956 0.000000 9.032226 0.000000 8.664956 c
|
||||||
|
0.000000 8.297687 0.297730 7.999956 0.665000 7.999956 c
|
||||||
|
5.559548 7.999956 l
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
2353
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000002443 00000 n
|
||||||
|
0000002466 00000 n
|
||||||
|
0000002639 00000 n
|
||||||
|
0000002713 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2772
|
||||||
|
%%EOF
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "Stories.svg",
|
"filename" : "stories_30.pdf",
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="30" height="30" rx="7" fill="#FF2D55"/>
|
|
||||||
<path d="M15 23C10.5817 23 7 19.4183 7 15C7 10.5817 10.5817 7 15 7" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<circle cx="15" cy="15" r="8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-dasharray="1.32 4.32"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 397 B |
221
submodules/TelegramUI/Images.xcassets/Settings/Menu/Stories.imageset/stories_30.pdf
vendored
Normal file
221
submodules/TelegramUI/Images.xcassets/Settings/Menu/Stories.imageset/stories_30.pdf
vendored
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 2 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.996078 0.172549 0.333333 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 6.000000 6.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
8.819326 15.997714 m
|
||||||
|
8.879366 15.999235 8.939594 16.000000 9.000000 16.000000 c
|
||||||
|
9.217377 16.000000 9.432181 15.990122 9.644025 15.970834 c
|
||||||
|
10.194035 15.920755 10.680502 16.326031 10.730579 16.876040 c
|
||||||
|
10.780658 17.426048 10.375383 17.912516 9.825374 17.962595 c
|
||||||
|
9.553321 17.987366 9.278004 18.000000 9.000000 18.000000 c
|
||||||
|
4.029437 18.000000 0.000000 13.970563 0.000000 9.000000 c
|
||||||
|
0.000000 6.631216 0.915133 4.476173 2.411141 2.869128 c
|
||||||
|
2.583210 2.684289 2.762962 2.506699 2.949891 2.336866 c
|
||||||
|
4.481421 0.945414 6.494651 0.074747 8.709856 0.004589 c
|
||||||
|
8.806196 0.001537 8.902919 0.000000 9.000000 0.000000 c
|
||||||
|
9.278004 0.000000 9.553321 0.012634 9.825375 0.037405 c
|
||||||
|
10.375384 0.087484 10.780659 0.573952 10.730580 1.123960 c
|
||||||
|
10.680502 1.673969 10.194035 2.079245 9.644025 2.029166 c
|
||||||
|
9.432181 2.009878 9.217377 2.000000 9.000000 2.000000 c
|
||||||
|
8.935744 2.000000 8.871692 2.000866 8.807852 2.002586 c
|
||||||
|
7.071224 2.049389 5.492391 2.728685 4.293358 3.818473 c
|
||||||
|
4.126952 3.969717 3.967862 4.128867 3.816680 4.295331 c
|
||||||
|
2.725542 5.496764 2.046374 7.079132 2.002286 8.819326 c
|
||||||
|
2.000765 8.879366 2.000000 8.939594 2.000000 9.000000 c
|
||||||
|
2.000000 9.065163 2.000890 9.130116 2.002660 9.194851 c
|
||||||
|
2.051733 10.990423 2.776973 12.617154 3.932920 13.829583 c
|
||||||
|
4.027086 13.928350 4.124111 14.024369 4.223862 14.117506 c
|
||||||
|
5.433672 15.247099 7.044405 15.952745 8.819326 15.997714 c
|
||||||
|
h
|
||||||
|
14.753499 15.921051 m
|
||||||
|
14.328968 16.274309 13.698444 16.216534 13.345185 15.792002 c
|
||||||
|
12.991926 15.367472 13.049703 14.736948 13.474234 14.383689 c
|
||||||
|
13.804405 14.108947 14.108946 13.804406 14.383688 13.474234 c
|
||||||
|
14.736948 13.049704 15.367471 12.991926 15.792002 13.345186 c
|
||||||
|
16.216534 13.698445 16.274309 14.328969 15.921049 14.753500 c
|
||||||
|
15.568322 15.177391 15.177390 15.568323 14.753499 15.921051 c
|
||||||
|
h
|
||||||
|
17.962595 9.825375 m
|
||||||
|
17.912516 10.375383 17.426048 10.780659 16.876040 10.730579 c
|
||||||
|
16.326031 10.680502 15.920755 10.194035 15.970834 9.644025 c
|
||||||
|
15.990122 9.432182 16.000000 9.217377 16.000000 9.000000 c
|
||||||
|
16.000000 8.782623 15.990122 8.567819 15.970834 8.355975 c
|
||||||
|
15.920755 7.805965 16.326031 7.319498 16.876040 7.269421 c
|
||||||
|
17.426048 7.219342 17.912516 7.624617 17.962595 8.174626 c
|
||||||
|
17.987366 8.446679 18.000000 8.721996 18.000000 9.000000 c
|
||||||
|
18.000000 9.278004 17.987366 9.553321 17.962595 9.825375 c
|
||||||
|
h
|
||||||
|
15.921050 3.246501 m
|
||||||
|
16.274311 3.671032 16.216534 4.301556 15.792003 4.654815 c
|
||||||
|
15.367472 5.008074 14.736948 4.950297 14.383689 4.525766 c
|
||||||
|
14.108947 4.195595 13.804406 3.891054 13.474235 3.616312 c
|
||||||
|
13.049704 3.263052 12.991927 2.632529 13.345186 2.207998 c
|
||||||
|
13.698445 1.783466 14.328969 1.725691 14.753500 2.078951 c
|
||||||
|
15.177391 2.431678 15.568323 2.822610 15.921050 3.246501 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
3662
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 4 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
944
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /XObject << /X1 1 0 R >>
|
||||||
|
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||||
|
/G 3 0 R
|
||||||
|
/S /Alpha
|
||||||
|
>>
|
||||||
|
/Type /ExtGState
|
||||||
|
>> >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 7 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
/E1 gs
|
||||||
|
/X1 Do
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
46
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 5 0 R
|
||||||
|
/Contents 6 0 R
|
||||||
|
/Parent 9 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<< /Kids [ 8 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Pages 9 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000003920 00000 n
|
||||||
|
0000003943 00000 n
|
||||||
|
0000005135 00000 n
|
||||||
|
0000005157 00000 n
|
||||||
|
0000005455 00000 n
|
||||||
|
0000005557 00000 n
|
||||||
|
0000005578 00000 n
|
||||||
|
0000005751 00000 n
|
||||||
|
0000005825 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 11
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
5885
|
||||||
|
%%EOF
|
||||||
@ -1846,8 +1846,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return proxySettingsController(accountManager: sharedContext.accountManager, postbox: account.postbox, network: account.network, mode: .modal, presentationData: sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: sharedContext.presentationData)
|
return proxySettingsController(accountManager: sharedContext.accountManager, postbox: account.postbox, network: account.network, mode: .modal, presentationData: sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: sharedContext.presentationData)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode) -> ViewController {
|
public func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController {
|
||||||
return installedStickerPacksController(context: context, mode: mode)
|
return installedStickerPacksController(context: context, mode: mode, forceTheme: forceTheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -330,6 +330,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
let controller = MediaEditorScreen(
|
let controller = MediaEditorScreen(
|
||||||
context: context,
|
context: context,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
|
isEditing: false,
|
||||||
transitionIn: transitionIn,
|
transitionIn: transitionIn,
|
||||||
transitionOut: { finished, isNew in
|
transitionOut: { finished, isNew in
|
||||||
if finished, let transitionOut = transitionOut(finished), let destinationView = transitionOut.destinationView {
|
if finished, let transitionOut = transitionOut(finished), let destinationView = transitionOut.destinationView {
|
||||||
@ -347,8 +348,8 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}, completion: { [weak self] randomId, mediaResult, privacy, commit in
|
}, completion: { [weak self] randomId, mediaResult, caption, privacy, commit in
|
||||||
guard let self else {
|
guard let self, let mediaResult else {
|
||||||
dismissCameraImpl?()
|
dismissCameraImpl?()
|
||||||
commit({})
|
commit({})
|
||||||
return
|
return
|
||||||
@ -357,16 +358,15 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
if let chatListController = self.chatListController as? ChatListControllerImpl {
|
if let chatListController = self.chatListController as? ChatListControllerImpl {
|
||||||
chatListController.scrollToStories()
|
chatListController.scrollToStories()
|
||||||
switch mediaResult {
|
switch mediaResult {
|
||||||
case let .image(image, dimensions, caption):
|
case let .image(image, dimensions):
|
||||||
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
||||||
let text = caption ?? NSAttributedString()
|
let entities = generateChatInputTextEntities(caption)
|
||||||
let entities = generateChatInputTextEntities(text)
|
self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: caption.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, period: privacy.timeout, randomId: randomId)
|
||||||
self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: text.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, period: privacy.timeout, randomId: randomId)
|
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
commit({})
|
commit({})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .video(content, firstFrameImage, values, duration, dimensions, caption):
|
case let .video(content, firstFrameImage, values, duration, dimensions):
|
||||||
let adjustments: VideoMediaResourceAdjustments
|
let adjustments: VideoMediaResourceAdjustments
|
||||||
if let valuesData = try? JSONEncoder().encode(values) {
|
if let valuesData = try? JSONEncoder().encode(values) {
|
||||||
let data = MemoryBuffer(data: valuesData)
|
let data = MemoryBuffer(data: valuesData)
|
||||||
@ -383,9 +383,8 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments))
|
resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments))
|
||||||
}
|
}
|
||||||
let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) }
|
let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) }
|
||||||
let text = caption ?? NSAttributedString()
|
let entities = generateChatInputTextEntities(caption)
|
||||||
let entities = generateChatInputTextEntities(text)
|
self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), text: caption.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, period: privacy.timeout, randomId: randomId)
|
||||||
self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), text: text.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, period: privacy.timeout, randomId: randomId)
|
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
commit({})
|
commit({})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user