mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
e7c717867c
commit
5bf4a36d46
@ -9,6 +9,7 @@ import Display
|
|||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import FastBlur
|
import FastBlur
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
public struct MediaEditorPlayerState {
|
public struct MediaEditorPlayerState {
|
||||||
public let generationTimestamp: Double
|
public let generationTimestamp: Double
|
||||||
@ -41,6 +42,7 @@ public final class MediaEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let context: AccountContext
|
||||||
private let subject: Subject
|
private let subject: Subject
|
||||||
private var player: AVPlayer?
|
private var player: AVPlayer?
|
||||||
private var additionalPlayer: AVPlayer?
|
private var additionalPlayer: AVPlayer?
|
||||||
@ -253,7 +255,8 @@ public final class MediaEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(subject: Subject, values: MediaEditorValues? = nil, hasHistogram: Bool = false) {
|
public init(context: AccountContext, subject: Subject, values: MediaEditorValues? = nil, hasHistogram: Bool = false) {
|
||||||
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
if let values {
|
if let values {
|
||||||
self.values = values
|
self.values = values
|
||||||
@ -332,6 +335,7 @@ public final class MediaEditor {
|
|||||||
print("error")
|
print("error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let context = self.context
|
||||||
let textureSource: Signal<(TextureSource, UIImage?, AVPlayer?, AVPlayer?, UIColor, UIColor), NoError>
|
let textureSource: Signal<(TextureSource, UIImage?, AVPlayer?, AVPlayer?, UIColor, UIColor), NoError>
|
||||||
switch subject {
|
switch subject {
|
||||||
case let .image(image, _):
|
case let .image(image, _):
|
||||||
@ -340,7 +344,7 @@ public final class MediaEditor {
|
|||||||
case let .draft(draft):
|
case let .draft(draft):
|
||||||
if draft.isVideo {
|
if draft.isVideo {
|
||||||
textureSource = Signal { subscriber in
|
textureSource = Signal { subscriber in
|
||||||
let url = URL(fileURLWithPath: draft.fullPath())
|
let url = URL(fileURLWithPath: draft.fullPath(engine: context.engine))
|
||||||
let asset = AVURLAsset(url: url)
|
let asset = AVURLAsset(url: url)
|
||||||
|
|
||||||
let playerItem = AVPlayerItem(asset: asset)
|
let playerItem = AVPlayerItem(asset: asset)
|
||||||
@ -372,7 +376,7 @@ public final class MediaEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
guard let image = UIImage(contentsOfFile: draft.fullPath()) else {
|
guard let image = UIImage(contentsOfFile: draft.fullPath(engine: context.engine)) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let colors: (UIColor, UIColor)
|
let colors: (UIColor, UIColor)
|
||||||
|
@ -70,6 +70,7 @@ public final class MediaEditorDraft: Codable, Equatable {
|
|||||||
case timestamp
|
case timestamp
|
||||||
case locationLatitude
|
case locationLatitude
|
||||||
case locationLongitude
|
case locationLongitude
|
||||||
|
case expiresOn
|
||||||
}
|
}
|
||||||
|
|
||||||
public let path: String
|
public let path: String
|
||||||
@ -82,8 +83,9 @@ public final class MediaEditorDraft: Codable, Equatable {
|
|||||||
public let privacy: MediaEditorResultPrivacy?
|
public let privacy: MediaEditorResultPrivacy?
|
||||||
public let timestamp: Int32
|
public let timestamp: Int32
|
||||||
public let location: CLLocationCoordinate2D?
|
public let location: CLLocationCoordinate2D?
|
||||||
|
public let expiresOn: Int32?
|
||||||
|
|
||||||
public init(path: String, isVideo: Bool, thumbnail: UIImage, dimensions: PixelDimensions, duration: Double?, values: MediaEditorValues, caption: NSAttributedString, privacy: MediaEditorResultPrivacy?, timestamp: Int32, location: CLLocationCoordinate2D?) {
|
public init(path: String, isVideo: Bool, thumbnail: UIImage, dimensions: PixelDimensions, duration: Double?, values: MediaEditorValues, caption: NSAttributedString, privacy: MediaEditorResultPrivacy?, timestamp: Int32, location: CLLocationCoordinate2D?, expiresOn: Int32?) {
|
||||||
self.path = path
|
self.path = path
|
||||||
self.isVideo = isVideo
|
self.isVideo = isVideo
|
||||||
self.thumbnail = thumbnail
|
self.thumbnail = thumbnail
|
||||||
@ -94,6 +96,7 @@ public final class MediaEditorDraft: Codable, Equatable {
|
|||||||
self.privacy = privacy
|
self.privacy = privacy
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.location = location
|
self.location = location
|
||||||
|
self.expiresOn = expiresOn
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -133,6 +136,8 @@ public final class MediaEditorDraft: Codable, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
self.location = nil
|
self.location = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.expiresOn = try container.decodeIfPresent(Int32.self, forKey: .expiresOn)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -170,6 +175,7 @@ public final class MediaEditorDraft: Codable, Equatable {
|
|||||||
try container.encodeNil(forKey: .locationLatitude)
|
try container.encodeNil(forKey: .locationLatitude)
|
||||||
try container.encodeNil(forKey: .locationLongitude)
|
try container.encodeNil(forKey: .locationLongitude)
|
||||||
}
|
}
|
||||||
|
try container.encodeIfPresent(self.expiresOn, forKey: .expiresOn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,14 +213,25 @@ public func addStoryDraft(engine: TelegramEngine, item: MediaEditorDraft) {
|
|||||||
|
|
||||||
public func removeStoryDraft(engine: TelegramEngine, path: String, delete: Bool) {
|
public func removeStoryDraft(engine: TelegramEngine, path: String, delete: Bool) {
|
||||||
if delete {
|
if delete {
|
||||||
try? FileManager.default.removeItem(atPath: fullDraftPath(path))
|
try? FileManager.default.removeItem(atPath: fullDraftPath(engine: engine, path: path))
|
||||||
}
|
}
|
||||||
let itemId = MediaEditorDraftItemId(path.persistentHashValue)
|
let itemId = MediaEditorDraftItemId(path.persistentHashValue)
|
||||||
let _ = engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts, id: itemId.rawValue).start()
|
let _ = engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts, id: itemId.rawValue).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func clearStoryDrafts(engine: TelegramEngine) {
|
public func clearStoryDrafts(engine: TelegramEngine) {
|
||||||
let _ = engine.orderedLists.clear(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts).start()
|
let _ = engine.data.get(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts)).start(next: { items in
|
||||||
|
for item in items {
|
||||||
|
if let draft = item.contents.get(MediaEditorDraft.self) {
|
||||||
|
removeStoryDraft(engine: engine, path: draft.path, delete: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deleteAllStoryDrafts(peerId: EnginePeer.Id) {
|
||||||
|
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(peerId.toInt64())/"
|
||||||
|
try? FileManager.default.removeItem(atPath: path)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func storyDrafts(engine: TelegramEngine) -> Signal<[MediaEditorDraft], NoError> {
|
public func storyDrafts(engine: TelegramEngine) -> Signal<[MediaEditorDraft], NoError> {
|
||||||
@ -230,12 +247,27 @@ public func storyDrafts(engine: TelegramEngine) -> Signal<[MediaEditorDraft], No
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateStoryDrafts(engine: TelegramEngine) {
|
||||||
|
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
let _ = engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts)
|
||||||
|
).start(next: { items in
|
||||||
|
for item in items {
|
||||||
|
if let draft = item.contents.get(MediaEditorDraft.self) {
|
||||||
|
if let expiresOn = draft.expiresOn, expiresOn < currentTimestamp {
|
||||||
|
removeStoryDraft(engine: engine, path: draft.path, delete: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public extension MediaEditorDraft {
|
public extension MediaEditorDraft {
|
||||||
func fullPath() -> String {
|
func fullPath(engine: TelegramEngine) -> String {
|
||||||
return fullDraftPath(self.path)
|
return fullDraftPath(engine: engine, path: self.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fullDraftPath(_ path: String) -> String {
|
private func fullDraftPath(engine: TelegramEngine, path: String) -> String {
|
||||||
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts/" + path
|
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())/" + path
|
||||||
}
|
}
|
||||||
|
@ -1886,7 +1886,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mediaEditor = MediaEditor(subject: subject.editorSubject, values: initialValues, hasHistogram: true)
|
let mediaEditor = MediaEditor(context: self.context, subject: subject.editorSubject, values: initialValues, hasHistogram: true)
|
||||||
if let initialVideoPosition = self.controller?.initialVideoPosition {
|
if let initialVideoPosition = self.controller?.initialVideoPosition {
|
||||||
mediaEditor.seek(initialVideoPosition, andPlay: true)
|
mediaEditor.seek(initialVideoPosition, andPlay: true)
|
||||||
}
|
}
|
||||||
@ -3441,6 +3441,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateStorySources(engine: self.context.engine)
|
||||||
|
updateStoryDrafts(engine: self.context.engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -3827,23 +3830,35 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
guard let subject = self.node.subject, let mediaEditor = self.node.mediaEditor else {
|
guard let subject = self.node.subject, let mediaEditor = self.node.mediaEditor else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try? FileManager.default.createDirectory(atPath: draftPath(), withIntermediateDirectories: true)
|
try? FileManager.default.createDirectory(atPath: draftPath(engine: self.context.engine), withIntermediateDirectories: true)
|
||||||
|
|
||||||
let values = mediaEditor.values
|
let values = mediaEditor.values
|
||||||
let privacy = self.state.privacy
|
let privacy = self.state.privacy
|
||||||
let caption = self.getCaption()
|
let caption = self.getCaption()
|
||||||
let duration = mediaEditor.duration ?? 0.0
|
let duration = mediaEditor.duration ?? 0.0
|
||||||
|
|
||||||
|
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
var timestamp: Int32
|
var timestamp: Int32
|
||||||
var location: CLLocationCoordinate2D?
|
var location: CLLocationCoordinate2D?
|
||||||
|
let expiresOn: Int32
|
||||||
if case let .draft(draft, _) = subject {
|
if case let .draft(draft, _) = subject {
|
||||||
timestamp = draft.timestamp
|
timestamp = draft.timestamp
|
||||||
location = draft.location
|
location = draft.location
|
||||||
|
if let _ = id {
|
||||||
|
expiresOn = draft.expiresOn ?? currentTimestamp + 3600 * 24 * 7
|
||||||
|
} else {
|
||||||
|
expiresOn = currentTimestamp + 3600 * 24 * 7
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
timestamp = currentTimestamp
|
||||||
if case let .asset(asset) = subject {
|
if case let .asset(asset) = subject {
|
||||||
location = asset.location?.coordinate
|
location = asset.location?.coordinate
|
||||||
}
|
}
|
||||||
|
if let _ = id {
|
||||||
|
expiresOn = currentTimestamp + Int32(self.state.privacy.timeout)
|
||||||
|
} else {
|
||||||
|
expiresOn = currentTimestamp + 3600 * 24 * 7
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let resultImage = mediaEditor.resultImage {
|
if let resultImage = mediaEditor.resultImage {
|
||||||
@ -3859,8 +3874,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
||||||
let path = "\(Int64.random(in: .min ... .max)).jpg"
|
let path = "\(Int64.random(in: .min ... .max)).jpg"
|
||||||
if let data = image.jpegData(compressionQuality: 0.87) {
|
if let data = image.jpegData(compressionQuality: 0.87) {
|
||||||
let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location)
|
let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location, expiresOn: expiresOn)
|
||||||
try? data.write(to: URL(fileURLWithPath: draft.fullPath()))
|
try? data.write(to: URL(fileURLWithPath: draft.fullPath(engine: context.engine)))
|
||||||
if let id {
|
if let id {
|
||||||
saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id)
|
saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id)
|
||||||
} else {
|
} else {
|
||||||
@ -3873,8 +3888,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let saveVideoDraft: (String, PixelDimensions, Double) -> Void = { videoPath, dimensions, duration in
|
let saveVideoDraft: (String, PixelDimensions, Double) -> Void = { videoPath, dimensions, duration in
|
||||||
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
||||||
let path = "\(Int64.random(in: .min ... .max)).mp4"
|
let path = "\(Int64.random(in: .min ... .max)).mp4"
|
||||||
let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location)
|
let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy, timestamp: timestamp, location: location, expiresOn: expiresOn)
|
||||||
try? FileManager.default.moveItem(atPath: videoPath, toPath: draft.fullPath())
|
try? FileManager.default.moveItem(atPath: videoPath, toPath: draft.fullPath(engine: context.engine))
|
||||||
if let id {
|
if let id {
|
||||||
saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id)
|
saveStorySource(engine: context.engine, item: draft, peerId: context.account.peerId, id: id)
|
||||||
} else {
|
} else {
|
||||||
@ -3906,8 +3921,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
case let .draft(draft, _):
|
case let .draft(draft, _):
|
||||||
if draft.isVideo {
|
if draft.isVideo {
|
||||||
saveVideoDraft(draft.fullPath(), draft.dimensions, draft.duration ?? 0.0)
|
saveVideoDraft(draft.fullPath(engine: context.engine), draft.dimensions, draft.duration ?? 0.0)
|
||||||
} else if let image = UIImage(contentsOfFile: draft.fullPath()) {
|
} else if let image = UIImage(contentsOfFile: draft.fullPath(engine: context.engine)) {
|
||||||
saveImageDraft(image, draft.dimensions)
|
saveImageDraft(image, draft.dimensions)
|
||||||
}
|
}
|
||||||
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
|
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
|
||||||
@ -3943,6 +3958,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
mediaEditor.invalidate()
|
mediaEditor.invalidate()
|
||||||
self.node.entitiesView.invalidate()
|
self.node.entitiesView.invalidate()
|
||||||
|
|
||||||
|
let context = self.context
|
||||||
if let navigationController = self.navigationController as? NavigationController {
|
if let navigationController = self.navigationController as? NavigationController {
|
||||||
navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate)
|
navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate)
|
||||||
}
|
}
|
||||||
@ -4109,15 +4125,16 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}
|
}
|
||||||
case let .draft(draft, _):
|
case let .draft(draft, _):
|
||||||
|
let draftPath = draft.fullPath(engine: context.engine)
|
||||||
if draft.isVideo {
|
if draft.isVideo {
|
||||||
videoResult = .videoFile(path: draft.fullPath())
|
videoResult = .videoFile(path: draftPath)
|
||||||
if let videoTrimRange = mediaEditor.values.videoTrimRange {
|
if let videoTrimRange = mediaEditor.values.videoTrimRange {
|
||||||
duration = videoTrimRange.upperBound - videoTrimRange.lowerBound
|
duration = videoTrimRange.upperBound - videoTrimRange.lowerBound
|
||||||
} else {
|
} else {
|
||||||
duration = min(draft.duration ?? 5.0, storyMaxVideoDuration)
|
duration = min(draft.duration ?? 5.0, storyMaxVideoDuration)
|
||||||
}
|
}
|
||||||
firstFrame = Signal<(UIImage?, UIImage?), NoError> { subscriber in
|
firstFrame = Signal<(UIImage?, UIImage?), NoError> { subscriber in
|
||||||
let avAsset = AVURLAsset(url: URL(fileURLWithPath: draft.fullPath()))
|
let avAsset = AVURLAsset(url: URL(fileURLWithPath: draftPath))
|
||||||
let avAssetGenerator = AVAssetImageGenerator(asset: avAsset)
|
let avAssetGenerator = AVAssetImageGenerator(asset: avAsset)
|
||||||
avAssetGenerator.appliesPreferredTrackTransform = true
|
avAssetGenerator.appliesPreferredTrackTransform = true
|
||||||
avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, cgImage, _, _, _ in
|
avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, cgImage, _, _, _ in
|
||||||
@ -4131,10 +4148,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
videoResult = .imageFile(path: draft.fullPath())
|
videoResult = .imageFile(path: draftPath)
|
||||||
duration = 5.0
|
duration = 5.0
|
||||||
|
|
||||||
if let image = UIImage(contentsOfFile: draft.fullPath()) {
|
if let image = UIImage(contentsOfFile: draftPath) {
|
||||||
firstFrame = .single((image, nil))
|
firstFrame = .single((image, nil))
|
||||||
} else {
|
} else {
|
||||||
firstFrame = .single((UIImage(), nil))
|
firstFrame = .single((UIImage(), nil))
|
||||||
@ -4228,6 +4245,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let context = self.context
|
||||||
|
|
||||||
let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) }
|
let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) }
|
||||||
let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView)
|
let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView)
|
||||||
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
||||||
@ -4292,10 +4311,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
case let .draft(draft, _):
|
case let .draft(draft, _):
|
||||||
if draft.isVideo {
|
if draft.isVideo {
|
||||||
let asset = AVURLAsset(url: NSURL(fileURLWithPath: draft.fullPath()) as URL)
|
let asset = AVURLAsset(url: NSURL(fileURLWithPath: draft.fullPath(engine: context.engine)) as URL)
|
||||||
exportSubject = .single(.video(asset))
|
exportSubject = .single(.video(asset))
|
||||||
} else {
|
} else {
|
||||||
if let image = UIImage(contentsOfFile: draft.fullPath()) {
|
if let image = UIImage(contentsOfFile: draft.fullPath(engine: context.engine)) {
|
||||||
exportSubject = .single(.image(image))
|
exportSubject = .single(.image(image))
|
||||||
} else {
|
} else {
|
||||||
fatalError()
|
fatalError()
|
||||||
@ -4768,8 +4787,8 @@ public final class BlurredGradientComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func draftPath() -> String {
|
func draftPath(engine: TelegramEngine) -> String {
|
||||||
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts"
|
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())"
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasFirstResponder(_ view: UIView) -> Bool {
|
func hasFirstResponder(_ view: UIView) -> Bool {
|
||||||
|
@ -6,24 +6,79 @@ import TelegramUIPreferences
|
|||||||
import MediaEditor
|
import MediaEditor
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public func saveStorySource(engine: TelegramEngine, item: MediaEditorDraft, peerId: EnginePeer.Id, id: Int64) {
|
public func updateStorySources(engine: TelegramEngine) {
|
||||||
|
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
let _ = engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: ApplicationSpecificOrderedItemListCollectionId.storySources)
|
||||||
|
).start(next: { items in
|
||||||
|
for item in items {
|
||||||
|
let key = EngineDataBuffer(item.id)
|
||||||
|
let _ = getStorySource(engine: engine, key: key).start(next: { source in
|
||||||
|
if let source {
|
||||||
|
if let expiresOn = source.expiresOn, expiresOn < currentTimestamp {
|
||||||
|
let _ = removeStorySource(engine: engine, key: key, delete: true).start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private func key(peerId: EnginePeer.Id, id: Int64) -> EngineDataBuffer {
|
||||||
let key = EngineDataBuffer(length: 16)
|
let key = EngineDataBuffer(length: 16)
|
||||||
key.setInt64(0, value: peerId.toInt64())
|
key.setInt64(0, value: peerId.toInt64())
|
||||||
key.setInt64(8, value: id)
|
key.setInt64(8, value: id)
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StorySourceItem: Codable {
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addStorySource(engine: TelegramEngine, key: EngineDataBuffer) {
|
||||||
|
let _ = engine.orderedLists.addOrMoveToFirstPosition(
|
||||||
|
collectionId: ApplicationSpecificOrderedItemListCollectionId.storySources,
|
||||||
|
id: key.toMemoryBuffer(),
|
||||||
|
item: StorySourceItem(),
|
||||||
|
removeTailIfCountExceeds: nil
|
||||||
|
).start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeStorySource(engine: TelegramEngine, peerId: EnginePeer.Id, id: Int64, delete: Bool) -> Signal<Never, NoError> {
|
||||||
|
let key = key(peerId: peerId, id: id)
|
||||||
|
return getStorySource(engine: engine, peerId: peerId, id: id)
|
||||||
|
|> mapToSignal { source in
|
||||||
|
if let source {
|
||||||
|
let _ = engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key).start()
|
||||||
|
removeStoryDraft(engine: engine, path: source.path, delete: delete)
|
||||||
|
}
|
||||||
|
return engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.storySources, id: key.toMemoryBuffer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeStorySource(engine: TelegramEngine, key: EngineDataBuffer, delete: Bool) -> Signal<Never, NoError> {
|
||||||
|
return getStorySource(engine: engine, key: key)
|
||||||
|
|> mapToSignal { source in
|
||||||
|
if let source {
|
||||||
|
let _ = engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key).start()
|
||||||
|
removeStoryDraft(engine: engine, path: source.path, delete: delete)
|
||||||
|
}
|
||||||
|
return engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.storySources, id: key.toMemoryBuffer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func saveStorySource(engine: TelegramEngine, item: MediaEditorDraft, peerId: EnginePeer.Id, id: Int64) {
|
||||||
|
let key = key(peerId: peerId, id: id)
|
||||||
|
addStorySource(engine: engine, key: key)
|
||||||
let _ = engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key, item: item).start()
|
let _ = engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key, item: item).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func removeStorySource(engine: TelegramEngine, peerId: EnginePeer.Id, id: Int64) {
|
public func getStorySource(engine: TelegramEngine, peerId: EnginePeer.Id, id: Int64) -> Signal<MediaEditorDraft?, NoError> {
|
||||||
let key = EngineDataBuffer(length: 16)
|
let key = key(peerId: peerId, id: id)
|
||||||
key.setInt64(0, value: peerId.toInt64())
|
return getStorySource(engine: engine, key: key)
|
||||||
key.setInt64(8, value: id)
|
|
||||||
let _ = engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key).start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getStorySource(engine: TelegramEngine, peerId: EnginePeer.Id, id: Int64) -> Signal<MediaEditorDraft?, NoError> {
|
private func getStorySource(engine: TelegramEngine, key: EngineDataBuffer) -> Signal<MediaEditorDraft?, NoError> {
|
||||||
let key = EngineDataBuffer(length: 16)
|
|
||||||
key.setInt64(0, value: peerId.toInt64())
|
|
||||||
key.setInt64(8, value: id)
|
|
||||||
return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key))
|
return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key))
|
||||||
|> map { result -> MediaEditorDraft? in
|
|> map { result -> MediaEditorDraft? in
|
||||||
return result?.get(MediaEditorDraft.self)
|
return result?.get(MediaEditorDraft.self)
|
||||||
@ -31,20 +86,16 @@ public func getStorySource(engine: TelegramEngine, peerId: EnginePeer.Id, id: In
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func moveStorySource(engine: TelegramEngine, peerId: EnginePeer.Id, from fromId: Int64, to toId: Int64) {
|
public func moveStorySource(engine: TelegramEngine, peerId: EnginePeer.Id, from fromId: Int64, to toId: Int64) {
|
||||||
let fromKey = EngineDataBuffer(length: 16)
|
let fromKey = key(peerId: peerId, id: fromId)
|
||||||
fromKey.setInt64(0, value: peerId.toInt64())
|
let toKey = key(peerId: peerId, id: toId)
|
||||||
fromKey.setInt64(8, value: fromId)
|
|
||||||
|
|
||||||
let toKey = EngineDataBuffer(length: 16)
|
|
||||||
toKey.setInt64(0, value: peerId.toInt64())
|
|
||||||
toKey.setInt64(8, value: toId)
|
|
||||||
|
|
||||||
let _ = (engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: fromKey))
|
let _ = (engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: fromKey))
|
||||||
|> mapToSignal { item -> Signal<Never, NoError> in
|
|> mapToSignal { item -> Signal<Never, NoError> in
|
||||||
if let item = item?.get(MediaEditorDraft.self) {
|
if let item = item?.get(MediaEditorDraft.self) {
|
||||||
|
addStorySource(engine: engine, key: toKey)
|
||||||
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: toKey, item: item)
|
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: toKey, item: item)
|
||||||
|> then(
|
|> then(
|
||||||
engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: fromKey)
|
removeStorySource(engine: engine, key: fromKey, delete: false)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
|
@ -38,6 +38,7 @@ import PhoneNumberFormat
|
|||||||
import AuthorizationUI
|
import AuthorizationUI
|
||||||
import ManagedFile
|
import ManagedFile
|
||||||
import DeviceProximity
|
import DeviceProximity
|
||||||
|
import MediaEditor
|
||||||
|
|
||||||
#if canImport(AppCenter)
|
#if canImport(AppCenter)
|
||||||
import AppCenter
|
import AppCenter
|
||||||
@ -1291,6 +1292,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
let _ = (updateIntentsSettingsInteractively(accountManager: accountManager) { current in
|
let _ = (updateIntentsSettingsInteractively(accountManager: accountManager) { current in
|
||||||
var updated = current
|
var updated = current
|
||||||
for peerId in loggedOutAccountPeerIds {
|
for peerId in loggedOutAccountPeerIds {
|
||||||
|
deleteAllStoryDrafts(peerId: peerId)
|
||||||
if peerId == updated.account {
|
if peerId == updated.account {
|
||||||
deleteAllSendMessageIntents()
|
deleteAllSendMessageIntents()
|
||||||
updated = updated.withUpdatedAccount(nil)
|
updated = updated.withUpdatedAccount(nil)
|
||||||
|
@ -100,6 +100,7 @@ private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
|
|||||||
case settingsSearchRecentItems = 2
|
case settingsSearchRecentItems = 2
|
||||||
case localThemes = 3
|
case localThemes = 3
|
||||||
case storyDrafts = 4
|
case storyDrafts = 4
|
||||||
|
case storySources = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificOrderedItemListCollectionId {
|
public struct ApplicationSpecificOrderedItemListCollectionId {
|
||||||
@ -108,4 +109,5 @@ public struct ApplicationSpecificOrderedItemListCollectionId {
|
|||||||
public static let settingsSearchRecentItems = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.settingsSearchRecentItems.rawValue)
|
public static let settingsSearchRecentItems = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.settingsSearchRecentItems.rawValue)
|
||||||
public static let localThemes = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.localThemes.rawValue)
|
public static let localThemes = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.localThemes.rawValue)
|
||||||
public static let storyDrafts = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.storyDrafts.rawValue)
|
public static let storyDrafts = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.storyDrafts.rawValue)
|
||||||
|
public static let storySources = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.storySources.rawValue)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user