mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Refactoring [skip ci]
This commit is contained in:
parent
b4c5ca2b4d
commit
00b6826303
@ -130,7 +130,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let resource = MapSnapshotMediaResource(latitude: map.latitude, longitude: map.longitude, width: Int32(dimensions.width), height: Int32(dimensions.height))
|
let resource = MapSnapshotMediaResource(latitude: map.latitude, longitude: map.longitude, width: Int32(dimensions.width), height: Int32(dimensions.height))
|
||||||
self.imageNode.setSignal(chatMapSnapshotImage(account: context.account, resource: resource))
|
self.imageNode.setSignal(chatMapSnapshotImage(engine: context.engine, resource: resource))
|
||||||
} else if let webPage = media.media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content, let image = content.image {
|
} else if let webPage = media.media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content, let image = content.image {
|
||||||
let imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
|
let imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
|
||||||
self.imageNode.setSignal(chatMessagePhoto(postbox: context.account.postbox, photoReference: imageReference))
|
self.imageNode.setSignal(chatMessagePhoto(postbox: context.account.postbox, photoReference: imageReference))
|
||||||
|
@ -11,7 +11,7 @@ import ShimmerEffect
|
|||||||
|
|
||||||
public final class ItemListVenueItem: ListViewItem, ItemListItem {
|
public final class ItemListVenueItem: ListViewItem, ItemListItem {
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
let account: Account
|
let engine: TelegramEngine
|
||||||
let venue: TelegramMediaMap?
|
let venue: TelegramMediaMap?
|
||||||
let title: String?
|
let title: String?
|
||||||
let subtitle: String?
|
let subtitle: String?
|
||||||
@ -22,9 +22,9 @@ public final class ItemListVenueItem: ListViewItem, ItemListItem {
|
|||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let header: ListViewItemHeader?
|
let header: ListViewItemHeader?
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, account: Account, venue: TelegramMediaMap?, title: String? = nil, subtitle: String? = nil, sectionId: ItemListSectionId = 0, style: ItemListStyle, action: (() -> Void)?, infoAction: (() -> Void)? = nil, header: ListViewItemHeader? = nil) {
|
public init(presentationData: ItemListPresentationData, engine: TelegramEngine, venue: TelegramMediaMap?, title: String? = nil, subtitle: String? = nil, sectionId: ItemListSectionId = 0, style: ItemListStyle, action: (() -> Void)?, infoAction: (() -> Void)? = nil, header: ListViewItemHeader? = nil) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.account = account
|
self.engine = engine
|
||||||
self.venue = venue
|
self.venue = venue
|
||||||
self.title = title
|
self.title = title
|
||||||
self.subtitle = subtitle
|
self.subtitle = subtitle
|
||||||
@ -281,7 +281,7 @@ public class ItemListVenueItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
let _ = addressApply()
|
let _ = addressApply()
|
||||||
|
|
||||||
if let updatedVenueType = updatedVenueType {
|
if let updatedVenueType = updatedVenueType {
|
||||||
strongSelf.iconNode.setSignal(venueIcon(postbox: item.account.postbox, type: updatedVenueType, background: true))
|
strongSelf.iconNode.setSignal(venueIcon(engine: item.engine, type: updatedVenueType, background: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
let iconApply = iconLayout(TransformImageArguments(corners: ImageCorners(), imageSize: CGSize(width: iconSize, height: iconSize), boundingSize: CGSize(width: iconSize, height: iconSize), intrinsicInsets: UIEdgeInsets()))
|
let iconApply = iconLayout(TransformImageArguments(corners: ImageCorners(), imageSize: CGSize(width: iconSize, height: iconSize), boundingSize: CGSize(width: iconSize, height: iconSize), intrinsicInsets: UIEdgeInsets()))
|
||||||
|
@ -217,7 +217,7 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode {
|
|||||||
|
|
||||||
if let updatedVenueType = updatedVenueType {
|
if let updatedVenueType = updatedVenueType {
|
||||||
strongSelf.venueType = updatedVenueType
|
strongSelf.venueType = updatedVenueType
|
||||||
strongSelf.iconNode.setSignal(venueIcon(postbox: context.account.postbox, type: updatedVenueType, background: false))
|
strongSelf.iconNode.setSignal(venueIcon(engine: context.engine, type: updatedVenueType, background: false))
|
||||||
}
|
}
|
||||||
|
|
||||||
let arguments = VenueIconArguments(defaultBackgroundColor: theme.chat.inputPanel.actionControlFillColor, defaultForegroundColor: theme.chat.inputPanel.actionControlForegroundColor)
|
let arguments = VenueIconArguments(defaultBackgroundColor: theme.chat.inputPanel.actionControlFillColor, defaultForegroundColor: theme.chat.inputPanel.actionControlForegroundColor)
|
||||||
|
@ -11,10 +11,10 @@ swift_library(
|
|||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
"//submodules/Postbox:Postbox",
|
|
||||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/Display:Display",
|
"//submodules/Display:Display",
|
||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
|
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
import Postbox
|
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import MapKit
|
import MapKit
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
@ -21,7 +20,7 @@ public struct MapSnapshotMediaResourceId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MapSnapshotMediaResource: TelegramMediaResource {
|
public class MapSnapshotMediaResource {
|
||||||
public let latitude: Double
|
public let latitude: Double
|
||||||
public let longitude: Double
|
public let longitude: Double
|
||||||
public let width: Int32
|
public let width: Int32
|
||||||
@ -34,49 +33,8 @@ public class MapSnapshotMediaResource: TelegramMediaResource {
|
|||||||
self.height = height
|
self.height = height
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(decoder: PostboxDecoder) {
|
public var id: EngineMediaResource.Id {
|
||||||
self.latitude = decoder.decodeDoubleForKey("lt", orElse: 0.0)
|
return EngineMediaResource.Id(MapSnapshotMediaResourceId(latitude: self.latitude, longitude: self.longitude, width: self.width, height: self.height).uniqueId)
|
||||||
self.longitude = decoder.decodeDoubleForKey("ln", orElse: 0.0)
|
|
||||||
self.width = decoder.decodeInt32ForKey("w", orElse: 0)
|
|
||||||
self.height = decoder.decodeInt32ForKey("h", orElse: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
|
||||||
encoder.encodeDouble(self.latitude, forKey: "lt")
|
|
||||||
encoder.encodeDouble(self.longitude, forKey: "ln")
|
|
||||||
encoder.encodeInt32(self.width, forKey: "w")
|
|
||||||
encoder.encodeInt32(self.height, forKey: "h")
|
|
||||||
}
|
|
||||||
|
|
||||||
public var id: MediaResourceId {
|
|
||||||
return MediaResourceId(MapSnapshotMediaResourceId(latitude: self.latitude, longitude: self.longitude, width: self.width, height: self.height).uniqueId)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to: MediaResource) -> Bool {
|
|
||||||
if let to = to as? MapSnapshotMediaResource {
|
|
||||||
return self.latitude == to.latitude && self.longitude == to.longitude && self.width == to.width && self.height == to.height
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class MapSnapshotMediaResourceRepresentation: CachedMediaResourceRepresentation {
|
|
||||||
public let keepDuration: CachedMediaRepresentationKeepDuration = .shortLived
|
|
||||||
|
|
||||||
public var uniqueId: String {
|
|
||||||
return "cached"
|
|
||||||
}
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to: CachedMediaResourceRepresentation) -> Bool {
|
|
||||||
if to is MapSnapshotMediaResourceRepresentation {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +54,7 @@ private func adjustGMapLatitude(_ latitude: Double, offset: Int, zoom: Int) -> D
|
|||||||
return yToLatitude(latitudeToY(latitude) + t)
|
return yToLatitude(latitudeToY(latitude) + t)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchMapSnapshotResource(resource: MapSnapshotMediaResource) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
private func fetchMapSnapshotResource(resource: MapSnapshotMediaResource) -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
@ -113,9 +71,9 @@ public func fetchMapSnapshotResource(resource: MapSnapshotMediaResource) -> Sign
|
|||||||
snapshotter.start(with: DispatchQueue.global(), completionHandler: { result, error in
|
snapshotter.start(with: DispatchQueue.global(), completionHandler: { result, error in
|
||||||
if let image = result?.image {
|
if let image = result?.image {
|
||||||
if let data = image.jpegData(compressionQuality: 0.9) {
|
if let data = image.jpegData(compressionQuality: 0.9) {
|
||||||
let tempFile = TempBox.shared.tempFile(fileName: "image.jpg")
|
let tempFile = EngineTempBox.shared.tempFile(fileName: "image.jpg")
|
||||||
if let _ = try? data.write(to: URL(fileURLWithPath: tempFile.path), options: .atomic) {
|
if let _ = try? data.write(to: URL(fileURLWithPath: tempFile.path), options: .atomic) {
|
||||||
subscriber.putNext(.tempFile(tempFile))
|
subscriber.putNext(.moveTempFile(file: tempFile))
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,11 +88,17 @@ public func fetchMapSnapshotResource(resource: MapSnapshotMediaResource) -> Sign
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatMapSnapshotData(account: Account, resource: MapSnapshotMediaResource) -> Signal<Data?, NoError> {
|
public func chatMapSnapshotData(engine: TelegramEngine, resource: MapSnapshotMediaResource) -> Signal<Data?, NoError> {
|
||||||
return Signal<Data?, NoError> { subscriber in
|
return Signal<Data?, NoError> { subscriber in
|
||||||
let dataDisposable = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: MapSnapshotMediaResourceRepresentation(), complete: true).start(next: { next in
|
let dataDisposable = engine.resources.custom(
|
||||||
if next.size != 0 {
|
id: resource.id.stringRepresentation,
|
||||||
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
|
fetch: EngineMediaResource.Fetch {
|
||||||
|
return fetchMapSnapshotResource(resource: resource)
|
||||||
|
},
|
||||||
|
cacheTimeout: .shortLived
|
||||||
|
).start(next: { next in
|
||||||
|
if next.availableSize != 0 {
|
||||||
|
subscriber.putNext(next.availableSize == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
|
||||||
}
|
}
|
||||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||||
|
|
||||||
@ -144,8 +108,8 @@ public func chatMapSnapshotData(account: Account, resource: MapSnapshotMediaReso
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatMapSnapshotImage(account: Account, resource: MapSnapshotMediaResource) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func chatMapSnapshotImage(engine: TelegramEngine, resource: MapSnapshotMediaResource) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
let signal = chatMapSnapshotData(account: account, resource: resource)
|
let signal = chatMapSnapshotData(engine: engine, resource: resource)
|
||||||
|
|
||||||
return signal |> map { fullSizeData in
|
return signal |> map { fullSizeData in
|
||||||
return { arguments in
|
return { arguments in
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
import Postbox
|
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
import PersistentStringHash
|
||||||
|
|
||||||
public struct VenueIconResourceId {
|
public struct VenueIconResourceId {
|
||||||
public let type: String
|
public let type: String
|
||||||
@ -22,79 +22,51 @@ public struct VenueIconResourceId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VenueIconResource: TelegramMediaResource {
|
public class VenueIconResource {
|
||||||
public let type: String
|
public let type: String
|
||||||
|
|
||||||
public init(type: String) {
|
public init(type: String) {
|
||||||
self.type = type
|
self.type = type
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(decoder: PostboxDecoder) {
|
public var id: EngineMediaResource.Id {
|
||||||
self.type = decoder.decodeStringForKey("t", orElse: "")
|
return EngineMediaResource.Id(VenueIconResourceId(type: self.type).uniqueId)
|
||||||
}
|
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
|
||||||
encoder.encodeString(self.type, forKey: "t")
|
|
||||||
}
|
|
||||||
|
|
||||||
public var id: MediaResourceId {
|
|
||||||
return MediaResourceId(VenueIconResourceId(type: self.type).uniqueId)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to: MediaResource) -> Bool {
|
|
||||||
if let to = to as? VenueIconResource {
|
|
||||||
return self.type == to.type
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchVenueIconResource(account: Account, resource: VenueIconResource) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
private func fetchVenueIconResource(engine: TelegramEngine, resource: VenueIconResource) -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
subscriber.putNext(.reset)
|
|
||||||
|
|
||||||
let url = "https://ss3.4sqi.net/img/categories_v2/\(resource.type)_88.png"
|
let url = "https://ss3.4sqi.net/img/categories_v2/\(resource.type)_88.png"
|
||||||
|
|
||||||
let fetchDisposable = MetaDisposable()
|
return engine.resources.httpData(url: url).start(next: { data in
|
||||||
fetchDisposable.set(fetchHttpResource(url: url).start(next: { next in
|
let file = EngineTempBox.shared.tempFile(fileName: "file.png")
|
||||||
subscriber.putNext(next)
|
let _ = try? data.write(to: URL(fileURLWithPath: file.path))
|
||||||
|
subscriber.putNext(.moveTempFile(file: file))
|
||||||
}, completed: {
|
}, completed: {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}))
|
})
|
||||||
|
|
||||||
return ActionDisposable {
|
|
||||||
fetchDisposable.dispose()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func venueIconData(postbox: Postbox, resource: MediaResource) -> Signal<Data?, NoError> {
|
private func venueIconData(engine: TelegramEngine, resource: VenueIconResource) -> Signal<Data?, NoError> {
|
||||||
let resourceData = postbox.mediaBox.resourceData(resource)
|
let resourceData = engine.resources.custom(
|
||||||
|
id: resource.id.stringRepresentation,
|
||||||
|
fetch: EngineMediaResource.Fetch {
|
||||||
|
return fetchVenueIconResource(engine: engine, resource: resource)
|
||||||
|
},
|
||||||
|
cacheTimeout: .shortLived
|
||||||
|
)
|
||||||
|
|
||||||
let signal = resourceData
|
let signal = resourceData
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { maybeData -> Signal<Data?, NoError> in
|
|> mapToSignal { maybeData -> Signal<Data?, NoError> in
|
||||||
if maybeData.complete {
|
if maybeData.isComplete {
|
||||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
return .single(try? Data(contentsOf: URL(fileURLWithPath: maybeData.path)))
|
||||||
return .single((loadedData))
|
|
||||||
} else {
|
} else {
|
||||||
let fetched = postbox.mediaBox.fetchedResource(resource, parameters: nil)
|
return .single(nil)
|
||||||
let data = Signal<Data?, NoError> { subscriber in
|
|
||||||
let fetchedDisposable = fetched.start()
|
|
||||||
let resourceDisposable = resourceData.start(next: { next in
|
|
||||||
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
|
|
||||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
|
||||||
|
|
||||||
return ActionDisposable {
|
|
||||||
fetchedDisposable.dispose()
|
|
||||||
resourceDisposable.dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
} |> distinctUntilChanged(isEqual: { lhs, rhs in
|
}
|
||||||
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
if lhs == nil && rhs == nil {
|
if lhs == nil && rhs == nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@ -141,7 +113,7 @@ public func venueIconColor(type: String) -> UIColor {
|
|||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = Int(abs(persistentHash32(type)) % Int32(randomColors.count))
|
let index = Int(abs(Int32(bitPattern: UInt32(clamping: type.persistentHashValue))) % Int32(randomColors.count))
|
||||||
return randomColors[index]
|
return randomColors[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,9 +134,9 @@ public struct VenueIconArguments: TransformImageCustomArguments {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func venueIcon(postbox: Postbox, type: String, background: Bool) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func venueIcon(engine: TelegramEngine, type: String, background: Bool) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
let isBuiltinIcon = ["", "home", "work"].contains(type)
|
let isBuiltinIcon = ["", "home", "work"].contains(type)
|
||||||
let data: Signal<Data?, NoError> = isBuiltinIcon ? .single(nil) : venueIconData(postbox: postbox, resource: VenueIconResource(type: type))
|
let data: Signal<Data?, NoError> = isBuiltinIcon ? .single(nil) : venueIconData(engine: engine, resource: VenueIconResource(type: type))
|
||||||
return data |> map { data in
|
return data |> map { data in
|
||||||
return { arguments in
|
return { arguments in
|
||||||
let context = DrawingContext(size: arguments.drawingSize, clear: true)
|
let context = DrawingContext(size: arguments.drawingSize, clear: true)
|
||||||
|
@ -81,7 +81,7 @@ private func generateLiveLocationIcon(theme: PresentationTheme, stop: Bool) -> U
|
|||||||
|
|
||||||
final class LocationActionListItem: ListViewItem {
|
final class LocationActionListItem: ListViewItem {
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
let account: Account
|
let engine: TelegramEngine
|
||||||
let title: String
|
let title: String
|
||||||
let subtitle: String
|
let subtitle: String
|
||||||
let icon: LocationActionListItemIcon
|
let icon: LocationActionListItemIcon
|
||||||
@ -89,9 +89,9 @@ final class LocationActionListItem: ListViewItem {
|
|||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let highlighted: (Bool) -> Void
|
let highlighted: (Bool) -> Void
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, account: Account, title: String, subtitle: String, icon: LocationActionListItemIcon, beginTimeAndTimeout: (Double, Double)?, action: @escaping () -> Void, highlighted: @escaping (Bool) -> Void = { _ in }) {
|
public init(presentationData: ItemListPresentationData, engine: TelegramEngine, title: String, subtitle: String, icon: LocationActionListItemIcon, beginTimeAndTimeout: (Double, Double)?, action: @escaping () -> Void, highlighted: @escaping (Bool) -> Void = { _ in }) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.account = account
|
self.engine = engine
|
||||||
self.title = title
|
self.title = title
|
||||||
self.subtitle = subtitle
|
self.subtitle = subtitle
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
@ -279,7 +279,7 @@ final class LocationActionListItemNode: ListViewItemNode {
|
|||||||
case let .venue(venue):
|
case let .venue(venue):
|
||||||
strongSelf.iconNode.isHidden = true
|
strongSelf.iconNode.isHidden = true
|
||||||
strongSelf.venueIconNode.isHidden = false
|
strongSelf.venueIconNode.isHidden = false
|
||||||
strongSelf.venueIconNode.setSignal(venueIcon(postbox: item.account.postbox, type: venue.venue?.type ?? "", background: true))
|
strongSelf.venueIconNode.setSignal(venueIcon(engine: item.engine, type: venue.venue?.type ?? "", background: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
if updatedIcon == .stopLiveLocation {
|
if updatedIcon == .stopLiveLocation {
|
||||||
|
@ -307,8 +307,8 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
let venueType = location.venue?.type ?? ""
|
let venueType = location.venue?.type ?? ""
|
||||||
let color = venueType.isEmpty ? annotation.theme.list.itemAccentColor : venueIconColor(type: venueType)
|
let color = venueType.isEmpty ? annotation.theme.list.itemAccentColor : venueIconColor(type: venueType)
|
||||||
self.backgroundNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/PinBackground"), color: color)
|
self.backgroundNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/PinBackground"), color: color)
|
||||||
self.iconNode.setSignal(venueIcon(postbox: annotation.context.account.postbox, type: venueType, background: false))
|
self.iconNode.setSignal(venueIcon(engine: annotation.context.engine, type: venueType, background: false))
|
||||||
self.smallIconNode.setSignal(venueIcon(postbox: annotation.context.account.postbox, type: venueType, background: false))
|
self.smallIconNode.setSignal(venueIcon(engine: annotation.context.engine, type: venueType, background: false))
|
||||||
self.smallNode.image = generateSmallBackgroundImage(color: color)
|
self.smallNode.image = generateSmallBackgroundImage(color: color)
|
||||||
self.dotNode.image = generateFilledCircleImage(diameter: 6.0, color: color)
|
self.dotNode.image = generateFilledCircleImage(diameter: 6.0, color: color)
|
||||||
|
|
||||||
@ -619,7 +619,7 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
|
|
||||||
func setCustom(_ custom: Bool, animated: Bool) {
|
func setCustom(_ custom: Bool, animated: Bool) {
|
||||||
if let annotation = self.annotation as? LocationPinAnnotation {
|
if let annotation = self.annotation as? LocationPinAnnotation {
|
||||||
self.iconNode.setSignal(venueIcon(postbox: annotation.context.account.postbox, type: "", background: false))
|
self.iconNode.setSignal(venueIcon(engine: annotation.context.engine, type: "", background: false))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let avatarNode = self.avatarNode {
|
if let avatarNode = self.avatarNode {
|
||||||
|
@ -13,7 +13,7 @@ import SolidRoundedButtonNode
|
|||||||
|
|
||||||
final class LocationInfoListItem: ListViewItem {
|
final class LocationInfoListItem: ListViewItem {
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
let account: Account
|
let engine: TelegramEngine
|
||||||
let location: TelegramMediaMap
|
let location: TelegramMediaMap
|
||||||
let address: String?
|
let address: String?
|
||||||
let distance: String?
|
let distance: String?
|
||||||
@ -21,9 +21,9 @@ final class LocationInfoListItem: ListViewItem {
|
|||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let getDirections: () -> Void
|
let getDirections: () -> Void
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, account: Account, location: TelegramMediaMap, address: String?, distance: String?, eta: String?, action: @escaping () -> Void, getDirections: @escaping () -> Void) {
|
public init(presentationData: ItemListPresentationData, engine: TelegramEngine, location: TelegramMediaMap, address: String?, distance: String?, eta: String?, action: @escaping () -> Void, getDirections: @escaping () -> Void) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.account = account
|
self.engine = engine
|
||||||
self.location = location
|
self.location = location
|
||||||
self.address = address
|
self.address = address
|
||||||
self.distance = distance
|
self.distance = distance
|
||||||
@ -192,7 +192,7 @@ final class LocationInfoListItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
let arguments = VenueIconArguments(defaultBackgroundColor: item.presentationData.theme.chat.inputPanel.actionControlFillColor, defaultForegroundColor: item.presentationData.theme.chat.inputPanel.actionControlForegroundColor)
|
let arguments = VenueIconArguments(defaultBackgroundColor: item.presentationData.theme.chat.inputPanel.actionControlFillColor, defaultForegroundColor: item.presentationData.theme.chat.inputPanel.actionControlForegroundColor)
|
||||||
if let updatedLocation = updatedLocation {
|
if let updatedLocation = updatedLocation {
|
||||||
strongSelf.venueIconNode.setSignal(venueIcon(postbox: item.account.postbox, type: updatedLocation.venue?.type ?? "", background: true))
|
strongSelf.venueIconNode.setSignal(venueIcon(engine: item.engine, type: updatedLocation.venue?.type ?? "", background: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
let iconApply = iconLayout(TransformImageArguments(corners: ImageCorners(), imageSize: CGSize(width: iconSize, height: iconSize), boundingSize: CGSize(width: iconSize, height: iconSize), intrinsicInsets: UIEdgeInsets(), custom: arguments))
|
let iconApply = iconLayout(TransformImageArguments(corners: ImageCorners(), imageSize: CGSize(width: iconSize, height: iconSize), boundingSize: CGSize(width: iconSize, height: iconSize), intrinsicInsets: UIEdgeInsets(), custom: arguments))
|
||||||
|
@ -129,7 +129,7 @@ private enum LocationPickerEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(account: Account, presentationData: PresentationData, interaction: LocationPickerInteraction?) -> ListViewItem {
|
func item(engine: TelegramEngine, presentationData: PresentationData, interaction: LocationPickerInteraction?) -> ListViewItem {
|
||||||
switch self {
|
switch self {
|
||||||
case let .location(_, title, subtitle, venue, coordinate):
|
case let .location(_, title, subtitle, venue, coordinate):
|
||||||
let icon: LocationActionListItemIcon
|
let icon: LocationActionListItemIcon
|
||||||
@ -138,7 +138,7 @@ private enum LocationPickerEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
icon = .location
|
icon = .location
|
||||||
}
|
}
|
||||||
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: icon, beginTimeAndTimeout: nil, action: {
|
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), engine: engine, title: title, subtitle: subtitle, icon: icon, beginTimeAndTimeout: nil, action: {
|
||||||
if let venue = venue {
|
if let venue = venue {
|
||||||
interaction?.sendVenue(venue)
|
interaction?.sendVenue(venue)
|
||||||
} else if let coordinate = coordinate {
|
} else if let coordinate = coordinate {
|
||||||
@ -148,7 +148,7 @@ private enum LocationPickerEntry: Comparable, Identifiable {
|
|||||||
interaction?.updateSendActionHighlight(highlighted)
|
interaction?.updateSendActionHighlight(highlighted)
|
||||||
})
|
})
|
||||||
case let .liveLocation(_, title, subtitle, coordinate):
|
case let .liveLocation(_, title, subtitle, coordinate):
|
||||||
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: .liveLocation, beginTimeAndTimeout: nil, action: {
|
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), engine: engine, title: title, subtitle: subtitle, icon: .liveLocation, beginTimeAndTimeout: nil, action: {
|
||||||
if let coordinate = coordinate {
|
if let coordinate = coordinate {
|
||||||
interaction?.sendLiveLocation(coordinate)
|
interaction?.sendLiveLocation(coordinate)
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ private enum LocationPickerEntry: Comparable, Identifiable {
|
|||||||
return LocationSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title)
|
return LocationSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title)
|
||||||
case let .venue(_, venue, _):
|
case let .venue(_, venue, _):
|
||||||
let venueType = venue?.venue?.type ?? ""
|
let venueType = venue?.venue?.type ?? ""
|
||||||
return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), account: account, venue: venue, style: .plain, action: venue.flatMap { venue in
|
return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), engine: engine, venue: venue, style: .plain, action: venue.flatMap { venue in
|
||||||
return { interaction?.sendVenue(venue) }
|
return { interaction?.sendVenue(venue) }
|
||||||
}, infoAction: ["home", "work"].contains(venueType) ? {
|
}, infoAction: ["home", "work"].contains(venueType) ? {
|
||||||
interaction?.openHomeWorkInfo()
|
interaction?.openHomeWorkInfo()
|
||||||
@ -168,12 +168,12 @@ private enum LocationPickerEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedTransition(from fromEntries: [LocationPickerEntry], to toEntries: [LocationPickerEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, account: Account, presentationData: PresentationData, interaction: LocationPickerInteraction?) -> LocationPickerTransaction {
|
private func preparedTransition(from fromEntries: [LocationPickerEntry], to toEntries: [LocationPickerEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, engine: TelegramEngine, presentationData: PresentationData, interaction: LocationPickerInteraction?) -> LocationPickerTransaction {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(engine: engine, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(engine: engine, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||||
|
|
||||||
return LocationPickerTransaction(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade)
|
return LocationPickerTransaction(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade)
|
||||||
}
|
}
|
||||||
@ -527,7 +527,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
|||||||
crossFade = true
|
crossFade = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let transition = preparedTransition(from: previousEntries ?? [], to: entries, isLoading: displayedVenues == nil, isEmpty: displayedVenues?.isEmpty ?? false, crossFade: crossFade, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction)
|
let transition = preparedTransition(from: previousEntries ?? [], to: entries, isLoading: displayedVenues == nil, isEmpty: displayedVenues?.isEmpty ?? false, crossFade: crossFade, engine: context.engine, presentationData: presentationData, interaction: strongSelf.interaction)
|
||||||
strongSelf.enqueueTransition(transition)
|
strongSelf.enqueueTransition(transition)
|
||||||
|
|
||||||
var displayingPlacesButton = false
|
var displayingPlacesButton = false
|
||||||
|
@ -50,7 +50,7 @@ private struct LocationSearchEntry: Identifiable, Comparable {
|
|||||||
return lhs.index < rhs.index
|
return lhs.index < rhs.index
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(account: Account, presentationData: PresentationData, sendVenue: @escaping (TelegramMediaMap) -> Void) -> ListViewItem {
|
func item(engine: TelegramEngine, presentationData: PresentationData, sendVenue: @escaping (TelegramMediaMap) -> Void) -> ListViewItem {
|
||||||
let venue = self.location
|
let venue = self.location
|
||||||
let header: ChatListSearchItemHeader
|
let header: ChatListSearchItemHeader
|
||||||
let subtitle: String?
|
let subtitle: String?
|
||||||
@ -61,7 +61,7 @@ private struct LocationSearchEntry: Identifiable, Comparable {
|
|||||||
header = ChatListSearchItemHeader(type: .mapAddress, theme: presentationData.theme, strings: presentationData.strings)
|
header = ChatListSearchItemHeader(type: .mapAddress, theme: presentationData.theme, strings: presentationData.strings)
|
||||||
subtitle = presentationData.strings.Map_DistanceAway(stringForDistance(strings: presentationData.strings, distance: self.distance)).string
|
subtitle = presentationData.strings.Map_DistanceAway(stringForDistance(strings: presentationData.strings, distance: self.distance)).string
|
||||||
}
|
}
|
||||||
return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), account: account, venue: self.location, title: self.title, subtitle: subtitle, style: .plain, action: {
|
return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), engine: engine, venue: self.location, title: self.title, subtitle: subtitle, style: .plain, action: {
|
||||||
sendVenue(venue)
|
sendVenue(venue)
|
||||||
}, header: header)
|
}, header: header)
|
||||||
}
|
}
|
||||||
@ -76,12 +76,12 @@ struct LocationSearchContainerTransition {
|
|||||||
let isEmpty: Bool
|
let isEmpty: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
private func locationSearchContainerPreparedTransition(from fromEntries: [LocationSearchEntry], to toEntries: [LocationSearchEntry], query: String, isSearching: Bool, isEmpty: Bool, account: Account, presentationData: PresentationData, sendVenue: @escaping (TelegramMediaMap) -> Void) -> LocationSearchContainerTransition {
|
private func locationSearchContainerPreparedTransition(from fromEntries: [LocationSearchEntry], to toEntries: [LocationSearchEntry], query: String, isSearching: Bool, isEmpty: Bool, engine: TelegramEngine, presentationData: PresentationData, sendVenue: @escaping (TelegramMediaMap) -> Void) -> LocationSearchContainerTransition {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(engine: engine, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(engine: engine, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
||||||
|
|
||||||
return LocationSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, query: query, isSearching: isSearching, isEmpty: isEmpty)
|
return LocationSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, query: query, isSearching: isSearching, isEmpty: isEmpty)
|
||||||
}
|
}
|
||||||
@ -211,7 +211,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let (items, query) = itemsAndQuery ?? (nil, "")
|
let (items, query) = itemsAndQuery ?? (nil, "")
|
||||||
let previousItems = previousSearchItems.swap(items ?? [])
|
let previousItems = previousSearchItems.swap(items ?? [])
|
||||||
let transition = locationSearchContainerPreparedTransition(from: previousItems, to: items ?? [], query: query, isSearching: items != nil, isEmpty: items?.isEmpty ?? false, account: context.account, presentationData: strongSelf.presentationData, sendVenue: { venue in self?.listNode.clearHighlightAnimated(true)
|
let transition = locationSearchContainerPreparedTransition(from: previousItems, to: items ?? [], query: query, isSearching: items != nil, isEmpty: items?.isEmpty ?? false, engine: context.engine, presentationData: strongSelf.presentationData, sendVenue: { venue in self?.listNode.clearHighlightAnimated(true)
|
||||||
if let _ = venue.venue {
|
if let _ = venue.venue {
|
||||||
self?.interaction.sendVenue(venue)
|
self?.interaction.sendVenue(venue)
|
||||||
} else {
|
} else {
|
||||||
|
@ -126,7 +126,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
|||||||
distanceString = nil
|
distanceString = nil
|
||||||
}
|
}
|
||||||
let eta = time.flatMap { stringForEstimatedDuration(strings: presentationData.strings, eta: $0) }
|
let eta = time.flatMap { stringForEstimatedDuration(strings: presentationData.strings, eta: $0) }
|
||||||
return LocationInfoListItem(presentationData: ItemListPresentationData(presentationData), account: context.account, location: location, address: addressString, distance: distanceString, eta: eta, action: {
|
return LocationInfoListItem(presentationData: ItemListPresentationData(presentationData), engine: context.engine, location: location, address: addressString, distance: distanceString, eta: eta, action: {
|
||||||
interaction?.goToCoordinate(location.coordinate)
|
interaction?.goToCoordinate(location.coordinate)
|
||||||
}, getDirections: {
|
}, getDirections: {
|
||||||
interaction?.requestDirections()
|
interaction?.requestDirections()
|
||||||
@ -138,7 +138,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
beginTimeAndTimeout = nil
|
beginTimeAndTimeout = nil
|
||||||
}
|
}
|
||||||
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: context.account, title: title, subtitle: subtitle, icon: beginTimeAndTimeout != nil ? .stopLiveLocation : .liveLocation, beginTimeAndTimeout: beginTimeAndTimeout, action: {
|
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), engine: context.engine, title: title, subtitle: subtitle, icon: beginTimeAndTimeout != nil ? .stopLiveLocation : .liveLocation, beginTimeAndTimeout: beginTimeAndTimeout, action: {
|
||||||
if beginTimeAndTimeout != nil {
|
if beginTimeAndTimeout != nil {
|
||||||
interaction?.stopLiveLocation()
|
interaction?.stopLiveLocation()
|
||||||
} else {
|
} else {
|
||||||
|
@ -614,7 +614,7 @@ private func filteredContactData(contactData: DeviceContactExtendedData, exclude
|
|||||||
return DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumbers: phoneNumbers), middleName: contactData.middleName, prefix: contactData.prefix, suffix: contactData.suffix, organization: includeJob ? contactData.organization : "", jobTitle: includeJob ? contactData.jobTitle : "", department: includeJob ? contactData.department : "", emailAddresses: emailAddresses, urls: urls, addresses: addresses, birthdayDate: includeBirthday ? contactData.birthdayDate : nil, socialProfiles: socialProfiles, instantMessagingProfiles: instantMessagingProfiles, note: includeNote ? contactData.note : "")
|
return DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumbers: phoneNumbers), middleName: contactData.middleName, prefix: contactData.prefix, suffix: contactData.suffix, organization: includeJob ? contactData.organization : "", jobTitle: includeJob ? contactData.jobTitle : "", department: includeJob ? contactData.department : "", emailAddresses: emailAddresses, urls: urls, addresses: addresses, birthdayDate: includeBirthday ? contactData.birthdayDate : nil, socialProfiles: socialProfiles, instantMessagingProfiles: instantMessagingProfiles, note: includeNote ? contactData.note : "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func deviceContactInfoEntries(account: Account, presentationData: PresentationData, peer: Peer?, isShare: Bool, shareViaException: Bool, contactData: DeviceContactExtendedData, isContact: Bool, state: DeviceContactInfoState, selecting: Bool, editingPhoneNumbers: Bool) -> [DeviceContactInfoEntry] {
|
private func deviceContactInfoEntries(account: Account, engine: TelegramEngine, presentationData: PresentationData, peer: Peer?, isShare: Bool, shareViaException: Bool, contactData: DeviceContactExtendedData, isContact: Bool, state: DeviceContactInfoState, selecting: Bool, editingPhoneNumbers: Bool) -> [DeviceContactInfoEntry] {
|
||||||
var entries: [DeviceContactInfoEntry] = []
|
var entries: [DeviceContactInfoEntry] = []
|
||||||
|
|
||||||
var editingName: ItemListAvatarAndNameInfoItemName?
|
var editingName: ItemListAvatarAndNameInfoItemName?
|
||||||
@ -731,7 +731,7 @@ private func deviceContactInfoEntries(account: Account, presentationData: Presen
|
|||||||
|> mapToSignal { coordinates -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
|> mapToSignal { coordinates -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
||||||
if let (latitude, longitude) = coordinates {
|
if let (latitude, longitude) = coordinates {
|
||||||
let resource = MapSnapshotMediaResource(latitude: latitude, longitude: longitude, width: 90, height: 90)
|
let resource = MapSnapshotMediaResource(latitude: latitude, longitude: longitude, width: 90, height: 90)
|
||||||
return chatMapSnapshotImage(account: account, resource: resource)
|
return chatMapSnapshotImage(engine: engine, resource: resource)
|
||||||
} else {
|
} else {
|
||||||
return .single({ _ in return nil })
|
return .single({ _ in return nil })
|
||||||
}
|
}
|
||||||
@ -1220,7 +1220,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
focusItemTag = DeviceContactInfoEntryTag.editingPhone(insertedPhoneId)
|
focusItemTag = DeviceContactInfoEntryTag.editingPhone(insertedPhoneId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deviceContactInfoEntries(account: context.account, presentationData: presentationData, peer: peerAndContactData.0, isShare: isShare, shareViaException: shareViaException, contactData: peerAndContactData.2, isContact: peerAndContactData.1 != nil, state: state, selecting: selecting, editingPhoneNumbers: editingPhones), style: isShare ? .blocks : .plain, focusItemTag: focusItemTag)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deviceContactInfoEntries(account: context.account, engine: context.engine, presentationData: presentationData, peer: peerAndContactData.0, isShare: isShare, shareViaException: shareViaException, contactData: peerAndContactData.2, isContact: peerAndContactData.1 != nil, state: state, selecting: selecting, editingPhoneNumbers: editingPhones), style: isShare ? .blocks : .plain, focusItemTag: focusItemTag)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -98,17 +98,8 @@ public enum CachedMediaRepresentationKeepDuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private struct CachedMediaResourceRepresentationKey: Hashable {
|
private struct CachedMediaResourceRepresentationKey: Hashable {
|
||||||
let resourceId: MediaResourceId
|
let resourceId: String?
|
||||||
let representation: CachedMediaResourceRepresentation
|
let representation: String
|
||||||
|
|
||||||
static func ==(lhs: CachedMediaResourceRepresentationKey, rhs: CachedMediaResourceRepresentationKey) -> Bool {
|
|
||||||
return lhs.resourceId == rhs.resourceId && lhs.representation.isEqual(to: rhs.representation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
hasher.combine(self.resourceId.hashValue)
|
|
||||||
hasher.combine(self.representation.uniqueId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class CachedMediaResourceRepresentationSubscriber {
|
private final class CachedMediaResourceRepresentationSubscriber {
|
||||||
@ -157,7 +148,7 @@ public final class MediaBox {
|
|||||||
private var keepResourceContexts: [MediaResourceId: MediaBoxKeepResourceContext] = [:]
|
private var keepResourceContexts: [MediaResourceId: MediaBoxKeepResourceContext] = [:]
|
||||||
|
|
||||||
private var wrappedFetchResource = Promise<(MediaResource, Signal<[(Range<Int>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>>()
|
private var wrappedFetchResource = Promise<(MediaResource, Signal<[(Range<Int>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>>()
|
||||||
public var preFetchedResourcePath: (MediaResource) -> String? = { _ in return nil }
|
|
||||||
public var fetchResource: ((MediaResource, Signal<[(Range<Int>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>)? {
|
public var fetchResource: ((MediaResource, Signal<[(Range<Int>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>)? {
|
||||||
didSet {
|
didSet {
|
||||||
if let fetchResource = self.fetchResource {
|
if let fetchResource = self.fetchResource {
|
||||||
@ -222,15 +213,15 @@ public final class MediaBox {
|
|||||||
return ResourceStorePaths(partial: "\(fileNameForId(id))_partial", complete: "\(fileNameForId(id))")
|
return ResourceStorePaths(partial: "\(fileNameForId(id))_partial", complete: "\(fileNameForId(id))")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cachedRepresentationPathsForId(_ id: MediaResourceId, representation: CachedMediaResourceRepresentation) -> ResourceStorePaths {
|
private func cachedRepresentationPathsForId(_ id: MediaResourceId, representationId: String, keepDuration: CachedMediaRepresentationKeepDuration) -> ResourceStorePaths {
|
||||||
let cacheString: String
|
let cacheString: String
|
||||||
switch representation.keepDuration {
|
switch keepDuration {
|
||||||
case .general:
|
case .general:
|
||||||
cacheString = "cache"
|
cacheString = "cache"
|
||||||
case .shortLived:
|
case .shortLived:
|
||||||
cacheString = "short-cache"
|
cacheString = "short-cache"
|
||||||
}
|
}
|
||||||
return ResourceStorePaths(partial: "\(self.basePath)/\(cacheString)/\(fileNameForId(id))_partial:\(representation.uniqueId)", complete: "\(self.basePath)/\(cacheString)/\(fileNameForId(id)):\(representation.uniqueId)")
|
return ResourceStorePaths(partial: "\(self.basePath)/\(cacheString)/\(fileNameForId(id))_partial:\(representationId)", complete: "\(self.basePath)/\(cacheString)/\(fileNameForId(id)):\(representationId)")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cachedRepresentationPathForId(_ id: String, representationId: String, keepDuration: CachedMediaRepresentationKeepDuration) -> String {
|
public func cachedRepresentationPathForId(_ id: String, representationId: String, keepDuration: CachedMediaRepresentationKeepDuration) -> String {
|
||||||
@ -317,12 +308,6 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func maybeCopiedPreFetchedResource(completePath: String, resource: MediaResource) {
|
|
||||||
if let path = self.preFetchedResourcePath(resource) {
|
|
||||||
let _ = try? FileManager.default.copyItem(atPath: path, toPath: completePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func resourceStatus(_ resource: MediaResource, approximateSynchronousValue: Bool = false) -> Signal<MediaResourceStatus, NoError> {
|
public func resourceStatus(_ resource: MediaResource, approximateSynchronousValue: Bool = false) -> Signal<MediaResourceStatus, NoError> {
|
||||||
let signal = Signal<MediaResourceStatus, NoError> { subscriber in
|
let signal = Signal<MediaResourceStatus, NoError> { subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
@ -337,16 +322,6 @@ public final class MediaBox {
|
|||||||
subscriber.putNext(.Local)
|
subscriber.putNext(.Local)
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
} else {
|
} else {
|
||||||
self.maybeCopiedPreFetchedResource(completePath: paths.complete, resource: resource)
|
|
||||||
if let _ = fileSize(paths.complete) {
|
|
||||||
self.timeBasedCleanup.touch(paths: [
|
|
||||||
paths.complete
|
|
||||||
])
|
|
||||||
subscriber.putNext(.Local)
|
|
||||||
subscriber.putCompletion()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.statusQueue.async {
|
self.statusQueue.async {
|
||||||
let resourceId = resource.id
|
let resourceId = resource.id
|
||||||
let statusContext: ResourceStatusContext
|
let statusContext: ResourceStatusContext
|
||||||
@ -371,7 +346,7 @@ public final class MediaBox {
|
|||||||
if let statusUpdateDisposable = statusUpdateDisposable {
|
if let statusUpdateDisposable = statusUpdateDisposable {
|
||||||
let statusQueue = self.statusQueue
|
let statusQueue = self.statusQueue
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
if let (fileContext, releaseContext) = self.fileContext(for: resource) {
|
if let (fileContext, releaseContext) = self.fileContext(for: resource.id) {
|
||||||
let statusDisposable = fileContext.status(next: { [weak statusContext] value in
|
let statusDisposable = fileContext.status(next: { [weak statusContext] value in
|
||||||
statusQueue.async {
|
statusQueue.async {
|
||||||
if let current = self.statusContexts[resourceId], current === statusContext, current.status != value {
|
if let current = self.statusContexts[resourceId], current === statusContext, current.status != value {
|
||||||
@ -459,17 +434,15 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func resourceData(_ resource: MediaResource, pathExtension: String? = nil, option: ResourceDataRequestOption = .complete(waitUntilFetchStatus: false), attemptSynchronously: Bool = false) -> Signal<MediaResourceData, NoError> {
|
public func resourceData(_ resource: MediaResource, pathExtension: String? = nil, option: ResourceDataRequestOption = .complete(waitUntilFetchStatus: false), attemptSynchronously: Bool = false) -> Signal<MediaResourceData, NoError> {
|
||||||
|
return self.resourceData(id: resource.id, pathExtension: pathExtension, option: option, attemptSynchronously: attemptSynchronously)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func resourceData(id: MediaResourceId, pathExtension: String? = nil, option: ResourceDataRequestOption = .complete(waitUntilFetchStatus: false), attemptSynchronously: Bool = false) -> Signal<MediaResourceData, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
let begin: () -> Void = {
|
let begin: () -> Void = {
|
||||||
let paths = self.storePathsForId(resource.id)
|
let paths = self.storePathsForId(id)
|
||||||
|
|
||||||
var completeSize = fileSize(paths.complete)
|
|
||||||
if completeSize == nil {
|
|
||||||
self.maybeCopiedPreFetchedResource(completePath: paths.complete, resource: resource)
|
|
||||||
completeSize = fileSize(paths.complete)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let completeSize = fileSize(paths.complete) {
|
if let completeSize = fileSize(paths.complete) {
|
||||||
self.timeBasedCleanup.touch(paths: [
|
self.timeBasedCleanup.touch(paths: [
|
||||||
@ -491,7 +464,7 @@ public final class MediaBox {
|
|||||||
subscriber.putNext(MediaResourceData(path: paths.partial, offset: 0, size: fileSize(paths.partial) ?? 0, complete: false))
|
subscriber.putNext(MediaResourceData(path: paths.partial, offset: 0, size: fileSize(paths.partial) ?? 0, complete: false))
|
||||||
}
|
}
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
if let (fileContext, releaseContext) = self.fileContext(for: resource) {
|
if let (fileContext, releaseContext) = self.fileContext(for: id) {
|
||||||
let waitUntilAfterInitialFetch: Bool
|
let waitUntilAfterInitialFetch: Bool
|
||||||
switch option {
|
switch option {
|
||||||
case let .complete(waitUntilFetchStatus):
|
case let .complete(waitUntilFetchStatus):
|
||||||
@ -535,16 +508,16 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fileContext(for resource: MediaResource) -> (MediaBoxFileContext, () -> Void)? {
|
private func fileContext(for id: MediaResourceId) -> (MediaBoxFileContext, () -> Void)? {
|
||||||
assert(self.dataQueue.isCurrent())
|
assert(self.dataQueue.isCurrent())
|
||||||
|
|
||||||
let resourceId = resource.id
|
let resourceId = id
|
||||||
|
|
||||||
var context: MediaBoxFileContext?
|
var context: MediaBoxFileContext?
|
||||||
if let current = self.fileContexts[resourceId] {
|
if let current = self.fileContexts[resourceId] {
|
||||||
context = current
|
context = current
|
||||||
} else {
|
} else {
|
||||||
let paths = self.storePathsForId(resource.id)
|
let paths = self.storePathsForId(id)
|
||||||
self.timeBasedCleanup.touch(paths: [
|
self.timeBasedCleanup.touch(paths: [
|
||||||
paths.complete,
|
paths.complete,
|
||||||
paths.partial,
|
paths.partial,
|
||||||
@ -581,7 +554,7 @@ public final class MediaBox {
|
|||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
|
guard let (fileContext, releaseContext) = self.fileContext(for: resource.id) else {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -615,11 +588,15 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func resourceData(_ resource: MediaResource, size: Int, in range: Range<Int>, mode: ResourceDataRangeMode = .complete, notifyAboutIncomplete: Bool = false, attemptSynchronously: Bool = false) -> Signal<(Data, Bool), NoError> {
|
public func resourceData(_ resource: MediaResource, size: Int, in range: Range<Int>, mode: ResourceDataRangeMode = .complete, notifyAboutIncomplete: Bool = false, attemptSynchronously: Bool = false) -> Signal<(Data, Bool), NoError> {
|
||||||
|
return self.resourceData(id: resource.id, size: size, in: range, mode: mode, notifyAboutIncomplete: notifyAboutIncomplete, attemptSynchronously: attemptSynchronously)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func resourceData(id: MediaResourceId, size: Int, in range: Range<Int>, mode: ResourceDataRangeMode = .complete, notifyAboutIncomplete: Bool = false, attemptSynchronously: Bool = false) -> Signal<(Data, Bool), NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
if attemptSynchronously {
|
if attemptSynchronously {
|
||||||
let paths = self.storePathsForId(resource.id)
|
let paths = self.storePathsForId(id)
|
||||||
|
|
||||||
if let completeSize = fileSize(paths.complete) {
|
if let completeSize = fileSize(paths.complete) {
|
||||||
self.timeBasedCleanup.touch(paths: [
|
self.timeBasedCleanup.touch(paths: [
|
||||||
@ -649,7 +626,7 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
|
guard let (fileContext, releaseContext) = self.fileContext(for: id) else {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -700,7 +677,7 @@ public final class MediaBox {
|
|||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
|
guard let (fileContext, releaseContext) = self.fileContext(for: resource.id) else {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -734,7 +711,7 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
} else {
|
} else {
|
||||||
if let (fileContext, releaseContext) = self.fileContext(for: resource) {
|
if let (fileContext, releaseContext) = self.fileContext(for: resource.id) {
|
||||||
let fetchResource = self.wrappedFetchResource.get()
|
let fetchResource = self.wrappedFetchResource.get()
|
||||||
let fetchedDisposable = fileContext.fetchedFullRange(fetch: { ranges in
|
let fetchedDisposable = fileContext.fetchedFullRange(fetch: { ranges in
|
||||||
return fetchResource
|
return fetchResource
|
||||||
@ -796,7 +773,7 @@ public final class MediaBox {
|
|||||||
|
|
||||||
public func cancelInteractiveResourceFetch(_ resource: MediaResource) {
|
public func cancelInteractiveResourceFetch(_ resource: MediaResource) {
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
if let (fileContext, releaseContext) = self.fileContext(for: resource) {
|
if let (fileContext, releaseContext) = self.fileContext(for: resource.id) {
|
||||||
fileContext.cancelFullRangeFetches()
|
fileContext.cancelFullRangeFetches()
|
||||||
releaseContext()
|
releaseContext()
|
||||||
}
|
}
|
||||||
@ -805,7 +782,7 @@ public final class MediaBox {
|
|||||||
|
|
||||||
public func storeCachedResourceRepresentation(_ resource: MediaResource, representation: CachedMediaResourceRepresentation, data: Data) {
|
public func storeCachedResourceRepresentation(_ resource: MediaResource, representation: CachedMediaResourceRepresentation, data: Data) {
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
let path = self.cachedRepresentationPathsForId(resource.id, representation: representation).complete
|
let path = self.cachedRepresentationPathsForId(resource.id, representationId: representation.uniqueId, keepDuration: representation.keepDuration).complete
|
||||||
let _ = try? data.write(to: URL(fileURLWithPath: path))
|
let _ = try? data.write(to: URL(fileURLWithPath: path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -815,7 +792,7 @@ public final class MediaBox {
|
|||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
let begin: () -> Void = {
|
let begin: () -> Void = {
|
||||||
let paths = self.cachedRepresentationPathsForId(resource.id, representation: representation)
|
let paths = self.cachedRepresentationPathsForId(resource.id, representationId: representation.uniqueId, keepDuration: representation.keepDuration)
|
||||||
if let size = fileSize(paths.complete) {
|
if let size = fileSize(paths.complete) {
|
||||||
self.timeBasedCleanup.touch(paths: [
|
self.timeBasedCleanup.touch(paths: [
|
||||||
paths.complete
|
paths.complete
|
||||||
@ -837,7 +814,7 @@ public final class MediaBox {
|
|||||||
subscriber.putNext(MediaResourceData(path: paths.partial, offset: 0, size: 0, complete: false))
|
subscriber.putNext(MediaResourceData(path: paths.partial, offset: 0, size: 0, complete: false))
|
||||||
}
|
}
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
let key = CachedMediaResourceRepresentationKey(resourceId: resource.id, representation: representation)
|
let key = CachedMediaResourceRepresentationKey(resourceId: resource.id.stringRepresentation, representation: representation.uniqueId)
|
||||||
let context: CachedMediaResourceRepresentationContext
|
let context: CachedMediaResourceRepresentationContext
|
||||||
if let currentContext = self.cachedRepresentationContexts[key] {
|
if let currentContext = self.cachedRepresentationContexts[key] {
|
||||||
context = currentContext
|
context = currentContext
|
||||||
@ -977,6 +954,161 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func customResourceData(id: String, baseResourceId: String?, pathExtension: String?, complete: Bool, fetch: (() -> Signal<CachedMediaResourceRepresentationResult, NoError>)?, keepDuration: CachedMediaRepresentationKeepDuration, attemptSynchronously: Bool) -> Signal<MediaResourceData, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
let begin: () -> Void = {
|
||||||
|
let paths: ResourceStorePaths
|
||||||
|
if let baseResourceId = baseResourceId {
|
||||||
|
paths = self.cachedRepresentationPathsForId(MediaResourceId(baseResourceId), representationId: id, keepDuration: keepDuration)
|
||||||
|
} else {
|
||||||
|
paths = self.storePathsForId(MediaResourceId(id))
|
||||||
|
}
|
||||||
|
if let size = fileSize(paths.complete) {
|
||||||
|
self.timeBasedCleanup.touch(paths: [
|
||||||
|
paths.complete
|
||||||
|
])
|
||||||
|
|
||||||
|
if let pathExtension = pathExtension {
|
||||||
|
let symlinkPath = paths.complete + ".\(pathExtension)"
|
||||||
|
if fileSize(symlinkPath) == nil {
|
||||||
|
let _ = try? FileManager.default.linkItem(atPath: paths.complete, toPath: symlinkPath)
|
||||||
|
}
|
||||||
|
subscriber.putNext(MediaResourceData(path: symlinkPath, offset: 0, size: size, complete: true))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
} else {
|
||||||
|
subscriber.putNext(MediaResourceData(path: paths.complete, offset: 0, size: size, complete: true))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
} else if let fetch = fetch {
|
||||||
|
if attemptSynchronously && complete {
|
||||||
|
subscriber.putNext(MediaResourceData(path: paths.partial, offset: 0, size: 0, complete: false))
|
||||||
|
}
|
||||||
|
self.dataQueue.async {
|
||||||
|
let key = CachedMediaResourceRepresentationKey(resourceId: baseResourceId, representation: id)
|
||||||
|
let context: CachedMediaResourceRepresentationContext
|
||||||
|
if let currentContext = self.cachedRepresentationContexts[key] {
|
||||||
|
context = currentContext
|
||||||
|
} else {
|
||||||
|
context = CachedMediaResourceRepresentationContext()
|
||||||
|
self.cachedRepresentationContexts[key] = context
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = context.dataSubscribers.add(CachedMediaResourceRepresentationSubscriber(update: { data in
|
||||||
|
if !complete || data.complete {
|
||||||
|
if let pathExtension = pathExtension, data.complete {
|
||||||
|
let symlinkPath = data.path + ".\(pathExtension)"
|
||||||
|
if fileSize(symlinkPath) == nil {
|
||||||
|
let _ = try? FileManager.default.linkItem(atPath: data.path, toPath: symlinkPath)
|
||||||
|
}
|
||||||
|
subscriber.putNext(MediaResourceData(path: symlinkPath, offset: data.offset, size: data.size, complete: data.complete))
|
||||||
|
} else {
|
||||||
|
subscriber.putNext(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.complete {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}, onlyComplete: complete))
|
||||||
|
if let currentData = context.currentData {
|
||||||
|
if !complete || currentData.complete {
|
||||||
|
subscriber.putNext(currentData)
|
||||||
|
}
|
||||||
|
if currentData.complete {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
} else if !complete {
|
||||||
|
subscriber.putNext(MediaResourceData(path: paths.partial, offset: 0, size: 0, complete: false))
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set(ActionDisposable { [weak context] in
|
||||||
|
self.dataQueue.async {
|
||||||
|
if let currentContext = self.cachedRepresentationContexts[key], currentContext === context {
|
||||||
|
currentContext.dataSubscribers.remove(index)
|
||||||
|
if currentContext.dataSubscribers.isEmpty {
|
||||||
|
currentContext.disposable.dispose()
|
||||||
|
self.cachedRepresentationContexts.removeValue(forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if !context.initialized {
|
||||||
|
context.initialized = true
|
||||||
|
let signal = fetch()
|
||||||
|
|> deliverOn(self.dataQueue)
|
||||||
|
context.disposable.set(signal.start(next: { [weak self, weak context] next in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var isDone = false
|
||||||
|
switch next {
|
||||||
|
case let .temporaryPath(temporaryPath):
|
||||||
|
rename(temporaryPath, paths.complete)
|
||||||
|
isDone = true
|
||||||
|
case let .tempFile(tempFile):
|
||||||
|
rename(tempFile.path, paths.complete)
|
||||||
|
TempBox.shared.dispose(tempFile)
|
||||||
|
isDone = true
|
||||||
|
case .reset:
|
||||||
|
let file = ManagedFile(queue: strongSelf.dataQueue, path: paths.partial, mode: .readwrite)
|
||||||
|
file?.truncate(count: 0)
|
||||||
|
unlink(paths.complete)
|
||||||
|
case let .data(dataPart):
|
||||||
|
let file = ManagedFile(queue: strongSelf.dataQueue, path: paths.partial, mode: .append)
|
||||||
|
let dataCount = dataPart.count
|
||||||
|
dataPart.withUnsafeBytes { rawBytes -> Void in
|
||||||
|
let bytes = rawBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
|
||||||
|
let _ = file?.write(bytes, count: dataCount)
|
||||||
|
}
|
||||||
|
case .done:
|
||||||
|
link(paths.partial, paths.complete)
|
||||||
|
isDone = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if let strongSelf = self, let currentContext = strongSelf.cachedRepresentationContexts[key], currentContext === context {
|
||||||
|
if isDone {
|
||||||
|
currentContext.disposable.dispose()
|
||||||
|
strongSelf.cachedRepresentationContexts.removeValue(forKey: key)
|
||||||
|
}
|
||||||
|
if let size = fileSize(paths.complete) {
|
||||||
|
let data = MediaResourceData(path: paths.complete, offset: 0, size: size, complete: isDone)
|
||||||
|
currentContext.currentData = data
|
||||||
|
for subscriber in currentContext.dataSubscribers.copyItems() {
|
||||||
|
if !subscriber.onlyComplete || isDone {
|
||||||
|
subscriber.update(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let size = fileSize(paths.partial) {
|
||||||
|
let data = MediaResourceData(path: paths.partial, offset: 0, size: size, complete: isDone)
|
||||||
|
currentContext.currentData = data
|
||||||
|
for subscriber in currentContext.dataSubscribers.copyItems() {
|
||||||
|
if !subscriber.onlyComplete || isDone {
|
||||||
|
subscriber.update(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subscriber.putNext(MediaResourceData(path: paths.partial, offset: 0, size: 0, complete: false))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if attemptSynchronously {
|
||||||
|
begin()
|
||||||
|
} else {
|
||||||
|
self.concurrentQueue.async(begin)
|
||||||
|
}
|
||||||
|
return ActionDisposable {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func collectResourceCacheUsage(_ ids: [MediaResourceId]) -> Signal<[MediaResourceId: Int64], NoError> {
|
public func collectResourceCacheUsage(_ ids: [MediaResourceId]) -> Signal<[MediaResourceId: Int64], NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
self.dataQueue.async {
|
self.dataQueue.async {
|
||||||
|
@ -1222,8 +1222,7 @@ public func updateAccountNetworkUsageStats(account: Account, category: MediaReso
|
|||||||
public typealias FetchCachedResourceRepresentation = (_ account: Account, _ resource: MediaResource, _ representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError>
|
public typealias FetchCachedResourceRepresentation = (_ account: Account, _ resource: MediaResource, _ representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError>
|
||||||
public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: Network, _ media: AnyMediaReference, _ userInteractive: Bool) -> Signal<AnyMediaReference?, NoError>
|
public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: Network, _ media: AnyMediaReference, _ userInteractive: Bool) -> Signal<AnyMediaReference?, NoError>
|
||||||
|
|
||||||
public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: FetchCachedResourceRepresentation? = nil, transformOutgoingMessageMedia: TransformOutgoingMessageMedia? = nil, preFetchedResourcePath: @escaping (MediaResource) -> String? = { _ in return nil }) {
|
public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: FetchCachedResourceRepresentation? = nil, transformOutgoingMessageMedia: TransformOutgoingMessageMedia? = nil) {
|
||||||
account.postbox.mediaBox.preFetchedResourcePath = preFetchedResourcePath
|
|
||||||
account.postbox.mediaBox.fetchResource = { [weak account] resource, intervals, parameters -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in
|
account.postbox.mediaBox.fetchResource = { [weak account] resource, intervals, parameters -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in
|
||||||
if let strongAccount = account {
|
if let strongAccount = account {
|
||||||
if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, intervals, parameters) {
|
if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, intervals, parameters) {
|
||||||
|
@ -2,7 +2,15 @@ import Foundation
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Postbox
|
import Postbox
|
||||||
|
|
||||||
|
public typealias EngineTempBox = TempBox
|
||||||
|
public typealias EngineTempBoxFile = TempBoxFile
|
||||||
|
|
||||||
public final class EngineMediaResource: Equatable {
|
public final class EngineMediaResource: Equatable {
|
||||||
|
public enum CacheTimeout {
|
||||||
|
case `default`
|
||||||
|
case shortLived
|
||||||
|
}
|
||||||
|
|
||||||
public struct ByteRange {
|
public struct ByteRange {
|
||||||
public enum Priority {
|
public enum Priority {
|
||||||
case `default`
|
case `default`
|
||||||
@ -21,27 +29,16 @@ public final class EngineMediaResource: Equatable {
|
|||||||
|
|
||||||
public final class Fetch {
|
public final class Fetch {
|
||||||
public enum Result {
|
public enum Result {
|
||||||
case dataPart(resourceOffset: Int, data: Data, range: Range<Int>, complete: Bool)
|
|
||||||
case resourceSizeUpdated(Int)
|
|
||||||
case progressUpdated(Float)
|
|
||||||
case replaceHeader(data: Data, range: Range<Int>)
|
|
||||||
case moveLocalFile(path: String)
|
|
||||||
case moveTempFile(file: TempBoxFile)
|
case moveTempFile(file: TempBoxFile)
|
||||||
case copyLocalItem(MediaResourceDataFetchCopyLocalItem)
|
|
||||||
case reset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Error {
|
public enum Error {
|
||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
public let signal: (
|
public let signal: () -> Signal<Result, Error>
|
||||||
Signal<[EngineMediaResource.ByteRange], NoError>
|
|
||||||
) -> Signal<Result, Error>
|
|
||||||
|
|
||||||
public init(_ signal: @escaping (
|
public init(_ signal: @escaping () -> Signal<Result, Error>) {
|
||||||
Signal<[EngineMediaResource.ByteRange], NoError>
|
|
||||||
) -> Signal<Result, Error>) {
|
|
||||||
self.signal = signal
|
self.signal = signal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,55 +90,9 @@ public final class EngineMediaResource: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension MediaResource {
|
public extension EngineMediaResource.ResourceData {
|
||||||
func fetch(engine: TelegramEngine, parameters: MediaResourceFetchParameters?) -> EngineMediaResource.Fetch {
|
convenience init(_ data: MediaResourceData) {
|
||||||
return EngineMediaResource.Fetch { ranges in
|
self.init(path: data.path, availableSize: data.size, isComplete: data.complete)
|
||||||
return Signal { subscriber in
|
|
||||||
return engine.account.postbox.mediaBox.fetchResource!(
|
|
||||||
self,
|
|
||||||
ranges |> map { ranges -> [(Range<Int>, MediaBoxFetchPriority)] in
|
|
||||||
return ranges.map { range -> (Range<Int>, MediaBoxFetchPriority) in
|
|
||||||
let mappedPriority: MediaBoxFetchPriority
|
|
||||||
switch range.priority {
|
|
||||||
case .default:
|
|
||||||
mappedPriority = .default
|
|
||||||
case .elevated:
|
|
||||||
mappedPriority = .elevated
|
|
||||||
case .maximum:
|
|
||||||
mappedPriority = .maximum
|
|
||||||
}
|
|
||||||
return (range.range, mappedPriority)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parameters
|
|
||||||
).start(next: { result in
|
|
||||||
let mappedResult: EngineMediaResource.Fetch.Result
|
|
||||||
switch result {
|
|
||||||
case let .dataPart(resourceOffset, data, range, complete):
|
|
||||||
mappedResult = .dataPart(resourceOffset: resourceOffset, data: data, range: range, complete: complete)
|
|
||||||
case let .resourceSizeUpdated(size):
|
|
||||||
mappedResult = .resourceSizeUpdated(size)
|
|
||||||
case let .progressUpdated(progress):
|
|
||||||
mappedResult = .progressUpdated(progress)
|
|
||||||
case let .replaceHeader(data, range):
|
|
||||||
mappedResult = .replaceHeader(data: data, range: range)
|
|
||||||
case let .moveLocalFile(path):
|
|
||||||
mappedResult = .moveLocalFile(path: path)
|
|
||||||
case let .moveTempFile(file):
|
|
||||||
mappedResult = .moveTempFile(file: file)
|
|
||||||
case let .copyLocalItem(item):
|
|
||||||
mappedResult = .copyLocalItem(item)
|
|
||||||
case .reset:
|
|
||||||
mappedResult = .reset
|
|
||||||
}
|
|
||||||
subscriber.putNext(mappedResult)
|
|
||||||
}, error: { _ in
|
|
||||||
subscriber.putError(.generic)
|
|
||||||
}, completed: {
|
|
||||||
subscriber.putCompletion()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,12 +116,66 @@ public extension TelegramEngine {
|
|||||||
return _internal_clearCachedMediaResources(account: self.account, mediaResourceIds: mediaResourceIds)
|
return _internal_clearCachedMediaResources(account: self.account, mediaResourceIds: mediaResourceIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func data(id: String) -> Signal<EngineMediaResource.ResourceData, NoError> {
|
public func data(id: EngineMediaResource.Id, attemptSynchronously: Bool = false) -> Signal<EngineMediaResource.ResourceData, NoError> {
|
||||||
preconditionFailure()
|
return self.account.postbox.mediaBox.resourceData(
|
||||||
|
id: MediaResourceId(id.stringRepresentation),
|
||||||
|
pathExtension: nil,
|
||||||
|
option: .complete(waitUntilFetchStatus: false),
|
||||||
|
attemptSynchronously: attemptSynchronously
|
||||||
|
)
|
||||||
|
|> map { data in
|
||||||
|
return EngineMediaResource.ResourceData(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetch(id: String, fetch: EngineMediaResource.Fetch) -> Signal<Never, NoError> {
|
public func custom(id: String, fetch: EngineMediaResource.Fetch, cacheTimeout: EngineMediaResource.CacheTimeout = .default, attemptSynchronously: Bool = false) -> Signal<EngineMediaResource.ResourceData, NoError> {
|
||||||
preconditionFailure()
|
let mappedKeepDuration: CachedMediaRepresentationKeepDuration
|
||||||
|
switch cacheTimeout {
|
||||||
|
case .default:
|
||||||
|
mappedKeepDuration = .general
|
||||||
|
case .shortLived:
|
||||||
|
mappedKeepDuration = .shortLived
|
||||||
|
}
|
||||||
|
return self.account.postbox.mediaBox.customResourceData(
|
||||||
|
id: id,
|
||||||
|
baseResourceId: nil,
|
||||||
|
pathExtension: nil,
|
||||||
|
complete: true,
|
||||||
|
fetch: {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return fetch.signal().start(next: { result in
|
||||||
|
let mappedResult: CachedMediaResourceRepresentationResult
|
||||||
|
switch result {
|
||||||
|
case let .moveTempFile(file):
|
||||||
|
mappedResult = .tempFile(file)
|
||||||
|
}
|
||||||
|
subscriber.putNext(mappedResult)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keepDuration: mappedKeepDuration,
|
||||||
|
attemptSynchronously: attemptSynchronously
|
||||||
|
)
|
||||||
|
|> map { data in
|
||||||
|
return EngineMediaResource.ResourceData(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func httpData(url: String) -> Signal<Data, EngineMediaResource.Fetch.Error> {
|
||||||
|
return fetchHttpResource(url: url)
|
||||||
|
|> mapError { _ -> EngineMediaResource.Fetch.Error in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { value -> Signal<Data, EngineMediaResource.Fetch.Error> in
|
||||||
|
switch value {
|
||||||
|
case let .dataPart(_, data, _, _):
|
||||||
|
return .single(data)
|
||||||
|
default:
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cancelAllFetches(id: String) {
|
public func cancelAllFetches(id: String) {
|
||||||
|
@ -135,7 +135,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if updated {
|
if updated {
|
||||||
updateImageSignal = chatMapSnapshotImage(account: item.context.account, resource: MapSnapshotMediaResource(latitude: selectedMedia.latitude, longitude: selectedMedia.longitude, width: Int32(imageSize.width), height: Int32(imageSize.height)))
|
updateImageSignal = chatMapSnapshotImage(engine: item.context.engine, resource: MapSnapshotMediaResource(latitude: selectedMedia.latitude, longitude: selectedMedia.longitude, width: Int32(imageSize.width), height: Int32(imageSize.height)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
|||||||
case let .locationHeader(_, title):
|
case let .locationHeader(_, title):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||||
case let .location(theme, location):
|
case let .location(theme, location):
|
||||||
let imageSignal = chatMapSnapshotImage(account: arguments.context.account, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
let imageSignal = chatMapSnapshotImage(engine: arguments.context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
||||||
return ItemListAddressItem(theme: theme, label: "", text: location.address.replacingOccurrences(of: ", ", with: "\n"), imageSignal: imageSignal, selected: nil, sectionId: self.section, style: .blocks, action: nil)
|
return ItemListAddressItem(theme: theme, label: "", text: location.address.replacingOccurrences(of: ", ", with: "\n"), imageSignal: imageSignal, selected: nil, sectionId: self.section, style: .blocks, action: nil)
|
||||||
case let .changeLocation(_, text):
|
case let .changeLocation(_, text):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||||
@ -258,7 +258,7 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
|||||||
case let .venueHeader(_, title):
|
case let .venueHeader(_, title):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||||
case let .venue(_, _, venue):
|
case let .venue(_, _, venue):
|
||||||
return ItemListVenueItem(presentationData: presentationData, account: arguments.context.account, venue: venue, sectionId: self.section, style: .blocks, action: {
|
return ItemListVenueItem(presentationData: presentationData, engine: arguments.context.engine, venue: venue, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.updateWithVenue(venue)
|
arguments.updateWithVenue(venue)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,6 @@ public func fetchCachedResourceRepresentation(account: Account, resource: MediaR
|
|||||||
}
|
}
|
||||||
return fetchAnimatedStickerFirstFrameRepresentation(account: account, resource: resource, resourceData: data, representation: representation)
|
return fetchAnimatedStickerFirstFrameRepresentation(account: account, resource: resource, resourceData: data, representation: representation)
|
||||||
}
|
}
|
||||||
} else if let resource = resource as? MapSnapshotMediaResource, let _ = representation as? MapSnapshotMediaResourceRepresentation {
|
|
||||||
return fetchMapSnapshotResource(resource: resource)
|
|
||||||
} else if let resource = resource as? YoutubeEmbedStoryboardMediaResource, let _ = representation as? YoutubeEmbedStoryboardMediaResourceRepresentation {
|
} else if let resource = resource as? YoutubeEmbedStoryboardMediaResource, let _ = representation as? YoutubeEmbedStoryboardMediaResourceRepresentation {
|
||||||
return fetchYoutubeEmbedStoryboardResource(resource: resource)
|
return fetchYoutubeEmbedStoryboardResource(resource: resource)
|
||||||
} else if let representation = representation as? CachedPreparedPatternWallpaperRepresentation {
|
} else if let representation = representation as? CachedPreparedPatternWallpaperRepresentation {
|
||||||
|
@ -1041,7 +1041,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
if let location = (data.cachedData as? CachedChannelData)?.peerGeoLocation {
|
if let location = (data.cachedData as? CachedChannelData)?.peerGeoLocation {
|
||||||
items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased()))
|
items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased()))
|
||||||
|
|
||||||
let imageSignal = chatMapSnapshotImage(account: context.account, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
||||||
items[.groupLocation]!.append(PeerInfoScreenAddressItem(
|
items[.groupLocation]!.append(PeerInfoScreenAddressItem(
|
||||||
id: ItemLocation,
|
id: ItemLocation,
|
||||||
label: "",
|
label: "",
|
||||||
@ -1271,7 +1271,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
if isCreator, let location = cachedData.peerGeoLocation {
|
if isCreator, let location = cachedData.peerGeoLocation {
|
||||||
items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased()))
|
items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased()))
|
||||||
|
|
||||||
let imageSignal = chatMapSnapshotImage(account: context.account, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90))
|
||||||
items[.groupLocation]!.append(PeerInfoScreenAddressItem(
|
items[.groupLocation]!.append(PeerInfoScreenAddressItem(
|
||||||
id: ItemLocation,
|
id: ItemLocation,
|
||||||
label: "",
|
label: "",
|
||||||
|
@ -408,9 +408,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|> mapToSignal { result -> Signal<AddedAccountResult, NoError> in
|
|> mapToSignal { result -> Signal<AddedAccountResult, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .authorized(account):
|
case let .authorized(account):
|
||||||
setupAccount(account, fetchCachedResourceRepresentation: fetchCachedResourceRepresentation, transformOutgoingMessageMedia: transformOutgoingMessageMedia, preFetchedResourcePath: { resource in
|
setupAccount(account, fetchCachedResourceRepresentation: fetchCachedResourceRepresentation, transformOutgoingMessageMedia: transformOutgoingMessageMedia)
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return account.postbox.transaction { transaction -> AddedAccountResult in
|
return account.postbox.transaction { transaction -> AddedAccountResult in
|
||||||
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue
|
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue
|
||||||
let contentSettings = getContentSettings(transaction: transaction)
|
let contentSettings = getContentSettings(transaction: transaction)
|
||||||
|
@ -31,8 +31,6 @@ public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResour
|
|||||||
return fetchOpenInAppIconResource(resource: resource)
|
return fetchOpenInAppIconResource(resource: resource)
|
||||||
} else if let resource = resource as? EmojiSpriteResource {
|
} else if let resource = resource as? EmojiSpriteResource {
|
||||||
return fetchEmojiSpriteResource(account: account, resource: resource)
|
return fetchEmojiSpriteResource(account: account, resource: resource)
|
||||||
} else if let resource = resource as? VenueIconResource {
|
|
||||||
return fetchVenueIconResource(account: account, resource: resource)
|
|
||||||
} else if let resource = resource as? BundleResource {
|
} else if let resource = resource as? BundleResource {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
subscriber.putNext(.reset)
|
subscriber.putNext(.reset)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user