mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
[WIP] Stories
This commit is contained in:
parent
2e0efe4e31
commit
9ab61f350c
@ -2490,10 +2490,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) {
|
||||
let localRect = transitionView.convert(transitionView.bounds, to: self.view)
|
||||
|
||||
Queue.mainQueue().after(0.2 * UIView.animationDurationFactor, { [weak self] in
|
||||
/*Queue.mainQueue().after(0.2 * UIView.animationDurationFactor, { [weak self] in
|
||||
HapticFeedback().impact()
|
||||
self?.animateRipple(centerLocation: localRect.center)
|
||||
})
|
||||
})*/
|
||||
|
||||
return StoryContainerScreen.TransitionOut(
|
||||
destinationView: transitionView,
|
||||
|
@ -502,10 +502,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|
||||
})
|
||||
|> mapToSignal { coordinate -> Signal<PeersNearbyData?, NoError> in
|
||||
guard let coordinate = coordinate else {
|
||||
#if !DEBUG
|
||||
#error("fix")
|
||||
#endif
|
||||
preconditionFailure()
|
||||
return .single(nil)
|
||||
/*let peersNearbyContext = PeersNearbyContext(network: context.account.network, stateManager: context.account.stateManager, coordinate: nil)
|
||||
return peersNearbyContext.get()
|
||||
|> map { peersNearby -> PeersNearbyData in
|
||||
|
@ -13,6 +13,7 @@ public struct EngineStoryPrivacy: Equatable {
|
||||
case everyone
|
||||
case contacts
|
||||
case closeFriends
|
||||
case nobody
|
||||
}
|
||||
|
||||
public var base: Base
|
||||
@ -116,6 +117,8 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text:
|
||||
privacyRules = [.inputPrivacyValueAllowContacts]
|
||||
case .closeFriends:
|
||||
privacyRules = [.inputPrivacyValueAllowCloseFriends]
|
||||
case .nobody:
|
||||
privacyRules = [.inputPrivacyValueDisallowAll]
|
||||
}
|
||||
var privacyUsers: [Api.InputUser] = []
|
||||
var privacyChats: [Int64] = []
|
||||
|
@ -34,6 +34,7 @@ swift_library(
|
||||
"//submodules/TooltipUI",
|
||||
"//submodules/Components/BlurredBackgroundComponent",
|
||||
"//submodules/AvatarNode",
|
||||
"//submodules/TelegramUI/Components/ShareWithPeersScreen",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -19,6 +19,7 @@ import EntityKeyboard
|
||||
import TooltipUI
|
||||
import BlurredBackgroundComponent
|
||||
import AvatarNode
|
||||
import ShareWithPeersScreen
|
||||
|
||||
enum DrawingScreenType {
|
||||
case drawing
|
||||
@ -631,6 +632,8 @@ final class MediaEditorScreenComponent: Component {
|
||||
privacyText = "Close Friends"
|
||||
case .contacts:
|
||||
privacyText = "Contacts"
|
||||
case .nobody:
|
||||
privacyText = "Selected Contacts"
|
||||
}
|
||||
|
||||
|
||||
@ -1359,14 +1362,14 @@ public final class MediaEditorScreen: ViewController {
|
||||
public var sourceHint: SourceHint?
|
||||
|
||||
public var cancelled: () -> Void = {}
|
||||
public var completion: (MediaEditorScreen.Result, @escaping () -> Void) -> Void = { _, _ in }
|
||||
public var completion: (MediaEditorScreen.Result, @escaping () -> Void, EngineStoryPrivacy) -> Void = { _, _, _ in }
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
subject: Signal<Subject?, NoError>,
|
||||
transitionIn: TransitionIn?,
|
||||
transitionOut: @escaping (Bool) -> TransitionOut?,
|
||||
completion: @escaping (MediaEditorScreen.Result, @escaping () -> Void) -> Void
|
||||
completion: @escaping (MediaEditorScreen.Result, @escaping () -> Void, EngineStoryPrivacy) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
@ -1393,7 +1396,22 @@ public final class MediaEditorScreen: ViewController {
|
||||
}
|
||||
|
||||
func presentPrivacySettings() {
|
||||
enum AdditionalCategoryId: Int {
|
||||
let stateContext = ShareWithPeersScreen.StateContext(context: self.context)
|
||||
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.push(ShareWithPeersScreen(context: self.context, initialPrivacy: self.node.storyPrivacy, stateContext: stateContext, completion: { [weak self] privacy in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.node.storyPrivacy = privacy
|
||||
self.node.requestUpdate()
|
||||
}))
|
||||
})
|
||||
|
||||
/*enum AdditionalCategoryId: Int {
|
||||
case everyone
|
||||
case contacts
|
||||
case closeFriends
|
||||
@ -1464,7 +1482,7 @@ public final class MediaEditorScreen: ViewController {
|
||||
}
|
||||
self?.node.storyPrivacy = privacy
|
||||
self?.node.requestUpdate()
|
||||
})
|
||||
})*/
|
||||
}
|
||||
|
||||
func requestDismiss(animated: Bool) {
|
||||
@ -1514,7 +1532,7 @@ public final class MediaEditorScreen: ViewController {
|
||||
self?.node.animateOut(finished: true, completion: { [weak self] in
|
||||
self?.dismiss()
|
||||
})
|
||||
})
|
||||
}, self.node.storyPrivacy)
|
||||
} else {
|
||||
if let image = mediaEditor.resultImage {
|
||||
makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { resultImage in
|
||||
@ -1523,7 +1541,7 @@ public final class MediaEditorScreen: ViewController {
|
||||
self?.node.animateOut(finished: true, completion: { [weak self] in
|
||||
self?.dismiss()
|
||||
})
|
||||
})
|
||||
}, self.node.storyPrivacy)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -23,17 +23,20 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
let context: AccountContext
|
||||
let stateContext: ShareWithPeersScreen.StateContext
|
||||
let initialPrivacy: EngineStoryPrivacy
|
||||
let categoryItems: [CategoryItem]
|
||||
let completion: (EngineStoryPrivacy) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
stateContext: ShareWithPeersScreen.StateContext,
|
||||
initialPrivacy: EngineStoryPrivacy,
|
||||
categoryItems: [CategoryItem],
|
||||
completion: @escaping (EngineStoryPrivacy) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.stateContext = stateContext
|
||||
self.initialPrivacy = initialPrivacy
|
||||
self.categoryItems = categoryItems
|
||||
self.completion = completion
|
||||
}
|
||||
@ -45,6 +48,9 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
if lhs.stateContext !== rhs.stateContext {
|
||||
return false
|
||||
}
|
||||
if lhs.initialPrivacy != rhs.initialPrivacy {
|
||||
return false
|
||||
}
|
||||
if lhs.categoryItems != rhs.categoryItems {
|
||||
return false
|
||||
}
|
||||
@ -681,7 +687,16 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
let sideInset: CGFloat = 0.0
|
||||
|
||||
if self.component == nil {
|
||||
self.selectedCategories.insert(.everyone)
|
||||
switch component.initialPrivacy.base {
|
||||
case .everyone:
|
||||
self.selectedCategories.insert(.everyone)
|
||||
case .closeFriends:
|
||||
self.selectedCategories.insert(.closeFriends)
|
||||
case .contacts:
|
||||
self.selectedCategories.insert(.contacts)
|
||||
case .nobody:
|
||||
self.selectedCategories.insert(.selectedContacts)
|
||||
}
|
||||
|
||||
var applyState = false
|
||||
self.defaultStateValue = component.stateContext.stateValue
|
||||
@ -779,6 +794,9 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
} else if let peerId = tokenId.base as? EnginePeer.Id {
|
||||
self.selectedPeers.removeAll(where: { $0 == peerId })
|
||||
}
|
||||
if self.selectedCategories.isEmpty {
|
||||
self.selectedCategories.insert(.everyone)
|
||||
}
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.35, curve: .spring)))
|
||||
}
|
||||
)),
|
||||
@ -969,8 +987,21 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
let base: EngineStoryPrivacy.Base
|
||||
if self.selectedCategories.contains(.everyone) {
|
||||
base = .everyone
|
||||
} else if self.selectedCategories.contains(.closeFriends) {
|
||||
base = .closeFriends
|
||||
} else if self.selectedCategories.contains(.contacts) {
|
||||
base = .contacts
|
||||
} else if self.selectedCategories.contains(.selectedContacts) {
|
||||
base = .nobody
|
||||
} else {
|
||||
base = .nobody
|
||||
}
|
||||
|
||||
component.completion(EngineStoryPrivacy(
|
||||
base: .everyone,
|
||||
base: base,
|
||||
additionallyIncludePeers: self.selectedPeers
|
||||
))
|
||||
controller.dismiss()
|
||||
@ -1152,7 +1183,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
|
||||
private var isDismissed: Bool = false
|
||||
|
||||
public init(context: AccountContext, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void) {
|
||||
public init(context: AccountContext, initialPrivacy: EngineStoryPrivacy, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void) {
|
||||
self.context = context
|
||||
|
||||
var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = []
|
||||
@ -1188,6 +1219,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
super.init(context: context, component: ShareWithPeersScreenComponent(
|
||||
context: context,
|
||||
stateContext: stateContext,
|
||||
initialPrivacy: initialPrivacy,
|
||||
categoryItems: categoryItems,
|
||||
completion: completion
|
||||
), navigationBarAppearance: .none, theme: .dark)
|
||||
|
@ -1439,88 +1439,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
|
||||
private func openItemPrivacySettings() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) else {
|
||||
return
|
||||
}
|
||||
guard let storyItem = focusedItem.storyItem else {
|
||||
return
|
||||
}
|
||||
|
||||
enum AdditionalCategoryId: Int {
|
||||
case everyone
|
||||
case contacts
|
||||
case closeFriends
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
|
||||
let additionalCategories: [ChatListNodeAdditionalCategory] = [
|
||||
ChatListNodeAdditionalCategory(
|
||||
id: AdditionalCategoryId.everyone.rawValue,
|
||||
icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white), cornerRadius: nil, color: .blue),
|
||||
smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue),
|
||||
title: "Everyone",
|
||||
appearance: .option(sectionTitle: "WHO CAN VIEW FOR 24 HOURS")
|
||||
),
|
||||
ChatListNodeAdditionalCategory(
|
||||
id: AdditionalCategoryId.contacts.rawValue,
|
||||
icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Tabs/IconContacts"), color: .white), iconScale: 1.0 * 0.8, cornerRadius: nil, color: .yellow),
|
||||
smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Tabs/IconContacts"), color: .white), iconScale: 0.6 * 0.8, cornerRadius: 6.0, circleCorners: true, color: .yellow),
|
||||
title: presentationData.strings.ChatListFolder_CategoryContacts,
|
||||
appearance: .option(sectionTitle: "WHO CAN VIEW FOR 24 HOURS")
|
||||
),
|
||||
ChatListNodeAdditionalCategory(
|
||||
id: AdditionalCategoryId.closeFriends.rawValue,
|
||||
icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Call/StarHighlighted"), color: .white), iconScale: 1.0 * 0.6, cornerRadius: nil, color: .green),
|
||||
smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Call/StarHighlighted"), color: .white), iconScale: 0.6 * 0.6, cornerRadius: 6.0, circleCorners: true, color: .green),
|
||||
title: "Close Friends",
|
||||
appearance: .option(sectionTitle: "WHO CAN VIEW FOR 24 HOURS")
|
||||
)
|
||||
]
|
||||
|
||||
var selectedChats = Set<EnginePeer.Id>()
|
||||
var selectedCategories = Set<Int>()
|
||||
if let privacy = storyItem.privacy {
|
||||
selectedChats.formUnion(privacy.additionallyIncludePeers)
|
||||
switch privacy.base {
|
||||
case .everyone:
|
||||
selectedCategories.insert(AdditionalCategoryId.everyone.rawValue)
|
||||
case .contacts:
|
||||
selectedCategories.insert(AdditionalCategoryId.contacts.rawValue)
|
||||
case .closeFriends:
|
||||
selectedCategories.insert(AdditionalCategoryId.closeFriends.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
let selectionController = component.context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: component.context, mode: .chatSelection(ContactMultiselectionControllerMode.ChatSelection(
|
||||
title: "Share Story",
|
||||
searchPlaceholder: "Search contacts",
|
||||
selectedChats: selectedChats,
|
||||
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
||||
chatListFilters: nil,
|
||||
displayPresence: true
|
||||
)), options: [], filters: [.excludeSelf], alwaysEnabled: true, limit: 1000, reachedLimit: { _ in
|
||||
}))
|
||||
component.controller()?.present(selectionController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
|
||||
let _ = (selectionController.result
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak selectionController] result in
|
||||
guard case let .result(peerIds, additionalCategoryIds) = result else {
|
||||
selectionController?.dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
let _ = peerIds
|
||||
let _ = additionalCategoryIds
|
||||
|
||||
selectionController?.dismiss()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
|
@ -349,60 +349,48 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}, completion: { mediaResult, commit in
|
||||
let stateContext = ShareWithPeersScreen.StateContext(context: self.context)
|
||||
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let controller = self.viewControllers.last as? ViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
controller.push(ShareWithPeersScreen(context: self.context, stateContext: stateContext, completion: { [weak self] privacy in
|
||||
guard let self else {
|
||||
dismissCameraImpl?()
|
||||
commit()
|
||||
return
|
||||
}, completion: { [weak self] mediaResult, commit, privacy in
|
||||
guard let self else {
|
||||
dismissCameraImpl?()
|
||||
commit()
|
||||
return
|
||||
}
|
||||
|
||||
if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext {
|
||||
switch mediaResult {
|
||||
case let .image(image, dimensions, caption):
|
||||
if let data = image.jpegData(compressionQuality: 0.8) {
|
||||
storyListContext.upload(media: .image(dimensions: dimensions, data: data), text: caption?.string ?? "", entities: [], privacy: privacy)
|
||||
Queue.mainQueue().after(0.2, { [weak chatListController] in
|
||||
chatListController?.animateStoryUploadRipple()
|
||||
})
|
||||
}
|
||||
|
||||
if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext {
|
||||
switch mediaResult {
|
||||
case let .image(image, dimensions, caption):
|
||||
if let data = image.jpegData(compressionQuality: 0.8) {
|
||||
storyListContext.upload(media: .image(dimensions: dimensions, data: data), text: caption?.string ?? "", entities: [], privacy: privacy)
|
||||
Queue.mainQueue().after(0.3, { [weak chatListController] in
|
||||
chatListController?.animateStoryUploadRipple()
|
||||
})
|
||||
}
|
||||
case let .video(content, _, values, duration, dimensions, caption):
|
||||
let adjustments: VideoMediaResourceAdjustments
|
||||
if let valuesData = try? JSONEncoder().encode(values) {
|
||||
let data = MemoryBuffer(data: valuesData)
|
||||
let digest = MemoryBuffer(data: data.md5Digest())
|
||||
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))
|
||||
}
|
||||
storyListContext.upload(media: .video(dimensions: dimensions, duration: Int(duration), resource: resource), text: caption?.string ?? "", entities: [], privacy: privacy)
|
||||
Queue.mainQueue().after(0.3, { [weak chatListController] in
|
||||
chatListController?.animateStoryUploadRipple()
|
||||
})
|
||||
}
|
||||
case let .video(content, _, values, duration, dimensions, caption):
|
||||
let adjustments: VideoMediaResourceAdjustments
|
||||
if let valuesData = try? JSONEncoder().encode(values) {
|
||||
let data = MemoryBuffer(data: valuesData)
|
||||
let digest = MemoryBuffer(data: data.md5Digest())
|
||||
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))
|
||||
}
|
||||
storyListContext.upload(media: .video(dimensions: dimensions, duration: Int(duration), resource: resource), text: caption?.string ?? "", entities: [], privacy: privacy)
|
||||
Queue.mainQueue().after(0.2, { [weak chatListController] in
|
||||
chatListController?.animateStoryUploadRipple()
|
||||
})
|
||||
}
|
||||
|
||||
dismissCameraImpl?()
|
||||
commit()
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dismissCameraImpl?()
|
||||
commit()
|
||||
})
|
||||
controller.sourceHint = .camera
|
||||
controller.cancelled = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user