[WIP] Stories

This commit is contained in:
Ali 2023-05-16 23:04:40 +04:00
parent 2e0efe4e31
commit 9ab61f350c
8 changed files with 105 additions and 147 deletions

View File

@ -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,

View File

@ -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

View File

@ -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] = []

View File

@ -34,6 +34,7 @@ swift_library(
"//submodules/TooltipUI",
"//submodules/Components/BlurredBackgroundComponent",
"//submodules/AvatarNode",
"//submodules/TelegramUI/Components/ShareWithPeersScreen",
],
visibility = [
"//visibility:public",

View File

@ -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)
}
})
}

View File

@ -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)

View File

@ -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 {

View File

@ -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 = {