Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2022-08-02 22:04:09 +03:00
commit 31ba2e963f
19 changed files with 336 additions and 49 deletions

View File

@ -7955,3 +7955,5 @@ Sorry for the inconvenience.";
"KeyCommand.SwitchToPIP" = "Switch to Picture-in-Picture"; "KeyCommand.SwitchToPIP" = "Switch to Picture-in-Picture";
"KeyCommand.EnterFullscreen" = "Enter Fullscreen"; "KeyCommand.EnterFullscreen" = "Enter Fullscreen";
"KeyCommand.ExitFullscreen" = "Exit Fullscreen"; "KeyCommand.ExitFullscreen" = "Exit Fullscreen";
"StickerPacksSettings.SuggestAnimatedEmoji" = "Suggest Animated Emoji";

View File

@ -91,7 +91,7 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem {
if file.fileName?.lowercased().hasSuffix(".ogg") == true { if file.fileName?.lowercased().hasSuffix(".ogg") == true {
albumArt = nil albumArt = nil
} else { } else {
albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .standalone(media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .standalone(media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false))
} }
return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: false) return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: false)

View File

@ -473,9 +473,9 @@ public final class ListMessageFileItemNode: ListMessageNode {
if !voice { if !voice {
if file.fileName?.lowercased().hasSuffix(".ogg") == true { if file.fileName?.lowercased().hasSuffix(".ogg") == true {
iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: "", performer: "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: "", performer: "", isThumbnail: false))) iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: "", performer: "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: "", performer: "", isThumbnail: false)))
} else { } else {
iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false))) iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: false)))
} }
} else { } else {
titleText = NSAttributedString(string: " ", font: audioTitleFont, textColor: item.presentationData.theme.theme.list.itemPrimaryTextColor) titleText = NSAttributedString(string: " ", font: audioTitleFont, textColor: item.presentationData.theme.theme.list.itemPrimaryTextColor)

View File

@ -25,11 +25,13 @@ public struct ExternalMusicAlbumArtResourceId {
} }
public class ExternalMusicAlbumArtResource: Equatable { public class ExternalMusicAlbumArtResource: Equatable {
public let file: FileMediaReference?
public let title: String public let title: String
public let performer: String public let performer: String
public let isThumbnail: Bool public let isThumbnail: Bool
public init(title: String, performer: String, isThumbnail: Bool) { public init(file: FileMediaReference?, title: String, performer: String, isThumbnail: Bool) {
self.file = file
self.title = title self.title = title
self.performer = performer self.performer = performer
self.isThumbnail = isThumbnail self.isThumbnail = isThumbnail
@ -40,6 +42,9 @@ public class ExternalMusicAlbumArtResource: Equatable {
} }
public static func ==(lhs: ExternalMusicAlbumArtResource, rhs: ExternalMusicAlbumArtResource) -> Bool { public static func ==(lhs: ExternalMusicAlbumArtResource, rhs: ExternalMusicAlbumArtResource) -> Bool {
if lhs.file?.media.fileId != rhs.file?.media.fileId {
return false
}
if lhs.title != rhs.title { if lhs.title != rhs.title {
return false return false
} }
@ -53,8 +58,10 @@ public class ExternalMusicAlbumArtResource: Equatable {
} }
} }
public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, resource: ExternalMusicAlbumArtResource) -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> { public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, file: FileMediaReference?, resource: ExternalMusicAlbumArtResource) -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> {
return Signal { subscriber in return engine.resources.fetchAlbumCover(file: file, title: resource.title, performer: resource.performer)
/*return Signal { subscriber in
if resource.performer.isEmpty || resource.performer.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "unknown artist" || resource.title.isEmpty { if resource.performer.isEmpty || resource.performer.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "unknown artist" || resource.title.isEmpty {
subscriber.putError(.generic) subscriber.putError(.generic)
return EmptyDisposable return EmptyDisposable
@ -151,5 +158,5 @@ public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, resource:
fetchDisposable.dispose() fetchDisposable.dispose()
} }
} }
} }*/
} }

View File

@ -2592,7 +2592,7 @@ public func albumArtThumbnailData(engine: TelegramEngine, thumbnail: ExternalMus
return engine.resources.custom( return engine.resources.custom(
id: thumbnail.id.stringRepresentation, id: thumbnail.id.stringRepresentation,
fetch: EngineMediaResource.Fetch { fetch: EngineMediaResource.Fetch {
return fetchExternalMusicAlbumArtResource(engine: engine, resource: thumbnail) return fetchExternalMusicAlbumArtResource(engine: engine, file: thumbnail.file, resource: thumbnail)
}, },
attemptSynchronously: attemptSynchronously attemptSynchronously: attemptSynchronously
) )
@ -2613,7 +2613,7 @@ public func albumArtThumbnailData(engine: TelegramEngine, thumbnail: ExternalMus
}) })
} }
private func albumArtFullSizeDatas(engine: TelegramEngine, thumbnail: ExternalMusicAlbumArtResource, fullSize: ExternalMusicAlbumArtResource, autoFetchFullSize: Bool = true) -> Signal<Tuple3<Data?, Data?, Bool>, NoError> { private func albumArtFullSizeDatas(engine: TelegramEngine, file: FileMediaReference?, thumbnail: ExternalMusicAlbumArtResource, fullSize: ExternalMusicAlbumArtResource, autoFetchFullSize: Bool = true) -> Signal<Tuple3<Data?, Data?, Bool>, NoError> {
return engine.resources.custom( return engine.resources.custom(
id: fullSize.id.stringRepresentation, id: fullSize.id.stringRepresentation,
fetch: nil, fetch: nil,
@ -2629,14 +2629,14 @@ private func albumArtFullSizeDatas(engine: TelegramEngine, thumbnail: ExternalMu
engine.resources.custom( engine.resources.custom(
id: thumbnail.id.stringRepresentation, id: thumbnail.id.stringRepresentation,
fetch: EngineMediaResource.Fetch { fetch: EngineMediaResource.Fetch {
return fetchExternalMusicAlbumArtResource(engine: engine, resource: thumbnail) return fetchExternalMusicAlbumArtResource(engine: engine, file: file, resource: thumbnail)
}, },
attemptSynchronously: false attemptSynchronously: false
), ),
engine.resources.custom( engine.resources.custom(
id: fullSize.id.stringRepresentation, id: fullSize.id.stringRepresentation,
fetch: autoFetchFullSize ? EngineMediaResource.Fetch { fetch: autoFetchFullSize ? EngineMediaResource.Fetch {
return fetchExternalMusicAlbumArtResource(engine: engine, resource: fullSize) return fetchExternalMusicAlbumArtResource(engine: engine, file: file, resource: fullSize)
} : nil, } : nil,
attemptSynchronously: false attemptSynchronously: false
) )
@ -2749,7 +2749,7 @@ public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReferen
return Tuple(thumbnailData, nil, false) return Tuple(thumbnailData, nil, false)
} }
} else { } else {
immediateArtworkData = albumArtFullSizeDatas(engine: engine, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource) immediateArtworkData = albumArtFullSizeDatas(engine: engine, file: fileReference, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource)
} }
} }

View File

@ -8,7 +8,7 @@ public struct MediaResourceId: Equatable, Hashable {
} }
} }
public protocol MediaResource { public protocol MediaResource: AnyObject {
var id: MediaResourceId { get } var id: MediaResourceId { get }
var size: Int64? { get } var size: Int64? { get }
var streamable: Bool { get } var streamable: Bool { get }

View File

@ -32,11 +32,12 @@ private final class InstalledStickerPacksControllerArguments {
let openArchived: ([ArchivedStickerPackItem]?) -> Void let openArchived: ([ArchivedStickerPackItem]?) -> Void
let openSuggestOptions: () -> Void let openSuggestOptions: () -> Void
let toggleAnimatedStickers: (Bool) -> Void let toggleAnimatedStickers: (Bool) -> Void
let toggleSuggestAnimatedEmoji: (Bool) -> Void
let togglePackSelected: (ItemCollectionId) -> Void let togglePackSelected: (ItemCollectionId) -> Void
let expandTrendingPacks: () -> Void let expandTrendingPacks: () -> Void
let addPack: (StickerPackCollectionInfo) -> Void let addPack: (StickerPackCollectionInfo) -> Void
init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openEmoji: @escaping () -> Void, openQuickReaction: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void, togglePackSelected: @escaping (ItemCollectionId) -> Void, expandTrendingPacks: @escaping () -> Void, addPack: @escaping (StickerPackCollectionInfo) -> Void) { init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openEmoji: @escaping () -> Void, openQuickReaction: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void, toggleSuggestAnimatedEmoji: @escaping (Bool) -> Void, togglePackSelected: @escaping (ItemCollectionId) -> Void, expandTrendingPacks: @escaping () -> Void, addPack: @escaping (StickerPackCollectionInfo) -> Void) {
self.account = account self.account = account
self.openStickerPack = openStickerPack self.openStickerPack = openStickerPack
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
@ -49,6 +50,7 @@ private final class InstalledStickerPacksControllerArguments {
self.openArchived = openArchived self.openArchived = openArchived
self.openSuggestOptions = openSuggestOptions self.openSuggestOptions = openSuggestOptions
self.toggleAnimatedStickers = toggleAnimatedStickers self.toggleAnimatedStickers = toggleAnimatedStickers
self.toggleSuggestAnimatedEmoji = toggleSuggestAnimatedEmoji
self.togglePackSelected = togglePackSelected self.togglePackSelected = togglePackSelected
self.expandTrendingPacks = expandTrendingPacks self.expandTrendingPacks = expandTrendingPacks
self.addPack = addPack self.addPack = addPack
@ -89,6 +91,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
case quickReaction(String, UIImage?) case quickReaction(String, UIImage?)
case animatedStickers(PresentationTheme, String, Bool) case animatedStickers(PresentationTheme, String, Bool)
case animatedStickersInfo(PresentationTheme, String) case animatedStickersInfo(PresentationTheme, String)
case suggestAnimatedEmoji(String, Bool)
case trendingPacksTitle(PresentationTheme, String) case trendingPacksTitle(PresentationTheme, String)
case trendingPack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, Bool) case trendingPack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, Bool)
case trendingExpand(PresentationTheme, String) case trendingExpand(PresentationTheme, String)
@ -98,7 +101,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo: case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji:
return InstalledStickerPacksSection.service.rawValue return InstalledStickerPacksSection.service.rawValue
case .trendingPacksTitle, .trendingPack, .trendingExpand: case .trendingPacksTitle, .trendingPack, .trendingExpand:
return InstalledStickerPacksSection.trending.rawValue return InstalledStickerPacksSection.trending.rawValue
@ -125,18 +128,20 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
return .index(6) return .index(6)
case .animatedStickersInfo: case .animatedStickersInfo:
return .index(7) return .index(7)
case .trendingPacksTitle: case .suggestAnimatedEmoji:
return .index(8) return .index(8)
case .trendingPacksTitle:
return .index(9)
case let .trendingPack(_, _, _, info, _, _, _, _, _): case let .trendingPack(_, _, _, info, _, _, _, _, _):
return .trendingPack(info.id) return .trendingPack(info.id)
case .trendingExpand: case .trendingExpand:
return .index(9)
case .packsTitle:
return .index(10) return .index(10)
case .packsTitle:
return .index(11)
case let .pack(_, _, _, info, _, _, _, _, _, _): case let .pack(_, _, _, info, _, _, _, _, _, _):
return .pack(info.id) return .pack(info.id)
case .packsInfo: case .packsInfo:
return .index(11) return .index(12)
} }
} }
@ -190,6 +195,12 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .suggestAnimatedEmoji(lhsText, lhsValue):
if case let .suggestAnimatedEmoji(rhsText, rhsValue) = rhs, lhsValue == rhsValue, lhsText == rhsText {
return true
} else {
return false
}
case let .trendingPacksTitle(lhsTheme, lhsText): case let .trendingPacksTitle(lhsTheme, lhsText):
if case let .trendingPacksTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { if case let .trendingPacksTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true return true
@ -344,9 +355,16 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
default: default:
return true return true
} }
case .suggestAnimatedEmoji:
switch rhs {
case .suggestOptions, .trending, .archived, .masks, .emoji, .quickReaction, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji:
return false
default:
return true
}
case .trendingPacksTitle: case .trendingPacksTitle:
switch rhs { switch rhs {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .trendingPacksTitle: case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji, .trendingPacksTitle:
return false return false
default: default:
return true return true
@ -362,14 +380,14 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
} }
case .trendingExpand: case .trendingExpand:
switch rhs { switch rhs {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .trendingPacksTitle, .trendingPack, .trendingExpand: case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji, .trendingPacksTitle, .trendingPack, .trendingExpand:
return false return false
default: default:
return true return true
} }
case .packsTitle: case .packsTitle:
switch rhs { switch rhs {
case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .trendingPacksTitle, .trendingPack, .trendingExpand, .packsTitle: case .suggestOptions, .trending, .masks, .emoji, .quickReaction, .archived, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji, .trendingPacksTitle, .trendingPack, .trendingExpand, .packsTitle:
return false return false
default: default:
return true return true
@ -432,6 +450,10 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
}) })
case let .animatedStickersInfo(_, text): case let .animatedStickersInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .suggestAnimatedEmoji(text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleSuggestAnimatedEmoji(value)
})
case let .trendingPacksTitle(_, text): case let .trendingPacksTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .trendingPack(_, _, _, info, topItem, count, animatedStickers, unread, installed): case let .trendingPack(_, _, _, info, topItem, count, animatedStickers, unread, installed):
@ -615,6 +637,8 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati
if let archived = archived, !archived.isEmpty { if let archived = archived, !archived.isEmpty {
entries.append(.archived(presentationData.theme, presentationData.strings.StickersList_ArchivedEmojiItem, Int32(archived.count), archived)) entries.append(.archived(presentationData.theme, presentationData.strings.StickersList_ArchivedEmojiItem, Int32(archived.count), archived))
} }
entries.append(.suggestAnimatedEmoji(presentationData.strings.StickerPacksSettings_SuggestAnimatedEmoji, stickerSettings.suggestAnimatedEmoji))
} }
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespaceForMode(mode)])] as? ItemCollectionInfosView { if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespaceForMode(mode)])] as? ItemCollectionInfosView {
@ -857,6 +881,10 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return current.withUpdatedLoopAnimatedStickers(value) return current.withUpdatedLoopAnimatedStickers(value)
}).start() }).start()
}, toggleSuggestAnimatedEmoji: { value in
let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return current.withUpdatedSuggestAnimatedEmoji(value)
}).start()
}, togglePackSelected: { packId in }, togglePackSelected: { packId in
updateState { state in updateState { state in
if var selectedPackIds = state.selectedPackIds { if var selectedPackIds = state.selectedPackIds {

View File

@ -363,6 +363,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1770371538] = { return Api.InputWallPaper.parse_inputWallPaperNoFile($0) } dict[-1770371538] = { return Api.InputWallPaper.parse_inputWallPaperNoFile($0) }
dict[1913199744] = { return Api.InputWallPaper.parse_inputWallPaperSlug($0) } dict[1913199744] = { return Api.InputWallPaper.parse_inputWallPaperSlug($0) }
dict[-1678949555] = { return Api.InputWebDocument.parse_inputWebDocument($0) } dict[-1678949555] = { return Api.InputWebDocument.parse_inputWebDocument($0) }
dict[-193992412] = { return Api.InputWebFileLocation.parse_inputWebFileAudioAlbumThumbLocation($0) }
dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) } dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) }
dict[-1036396922] = { return Api.InputWebFileLocation.parse_inputWebFileLocation($0) } dict[-1036396922] = { return Api.InputWebFileLocation.parse_inputWebFileLocation($0) }
dict[1048946971] = { return Api.Invoice.parse_invoice($0) } dict[1048946971] = { return Api.Invoice.parse_invoice($0) }

View File

@ -54,11 +54,21 @@ public extension Api {
} }
public extension Api { public extension Api {
enum InputWebFileLocation: TypeConstructorDescription { enum InputWebFileLocation: TypeConstructorDescription {
case inputWebFileAudioAlbumThumbLocation(flags: Int32, document: Api.InputDocument?, title: String?, performer: String?)
case inputWebFileGeoPointLocation(geoPoint: Api.InputGeoPoint, accessHash: Int64, w: Int32, h: Int32, zoom: Int32, scale: Int32) case inputWebFileGeoPointLocation(geoPoint: Api.InputGeoPoint, accessHash: Int64, w: Int32, h: Int32, zoom: Int32, scale: Int32)
case inputWebFileLocation(url: String, accessHash: Int64) case inputWebFileLocation(url: String, accessHash: Int64)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer):
if boxed {
buffer.appendInt32(-193992412)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)}
break
case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale): case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale):
if boxed { if boxed {
buffer.appendInt32(-1625153079) buffer.appendInt32(-1625153079)
@ -82,6 +92,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer):
return ("inputWebFileAudioAlbumThumbLocation", [("flags", String(describing: flags)), ("document", String(describing: document)), ("title", String(describing: title)), ("performer", String(describing: performer))])
case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale): case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale):
return ("inputWebFileGeoPointLocation", [("geoPoint", String(describing: geoPoint)), ("accessHash", String(describing: accessHash)), ("w", String(describing: w)), ("h", String(describing: h)), ("zoom", String(describing: zoom)), ("scale", String(describing: scale))]) return ("inputWebFileGeoPointLocation", [("geoPoint", String(describing: geoPoint)), ("accessHash", String(describing: accessHash)), ("w", String(describing: w)), ("h", String(describing: h)), ("zoom", String(describing: zoom)), ("scale", String(describing: scale))])
case .inputWebFileLocation(let url, let accessHash): case .inputWebFileLocation(let url, let accessHash):
@ -89,6 +101,28 @@ public extension Api {
} }
} }
public static func parse_inputWebFileAudioAlbumThumbLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputDocument?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputDocument
} }
var _3: String?
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
var _4: String?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.InputWebFileLocation.inputWebFileAudioAlbumThumbLocation(flags: _1!, document: _2, title: _3, performer: _4)
}
else {
return nil
}
}
public static func parse_inputWebFileGeoPointLocation(_ reader: BufferReader) -> InputWebFileLocation? { public static func parse_inputWebFileGeoPointLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: Api.InputGeoPoint? var _1: Api.InputGeoPoint?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -82,9 +82,3 @@ extension SecretFileMediaResource: TelegramCloudMediaResource, TelegramMultipart
return .inputEncryptedFileLocation(id: self.fileId, accessHash: self.accessHash) return .inputEncryptedFileLocation(id: self.fileId, accessHash: self.accessHash)
} }
} }
extension WebFileReferenceMediaResource {
var apiInputLocation: Api.InputWebFileLocation {
return .inputWebFileLocation(url: self.url, accessHash: self.accessHash)
}
}

View File

@ -56,6 +56,7 @@ private enum MultipartFetchDownloadError {
case reuploadToCdn(masterDatacenterId: Int32, token: Data) case reuploadToCdn(masterDatacenterId: Int32, token: Data)
case revalidateMediaReference case revalidateMediaReference
case hashesMissing case hashesMissing
case fatal
} }
private enum MultipartFetchGenericLocationResult { private enum MultipartFetchGenericLocationResult {
@ -347,7 +348,7 @@ private enum MultipartFetchSource {
case let .web(_, location): case let .web(_, location):
return download.request(Api.functions.upload.getWebFile(location: location, offset: Int32(offset), limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground) return download.request(Api.functions.upload.getWebFile(location: location, offset: Int32(offset), limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground)
|> mapError { error -> MultipartFetchDownloadError in |> mapError { error -> MultipartFetchDownloadError in
return .generic return .fatal
} }
|> mapToSignal { result -> Signal<Data, MultipartFetchDownloadError> in |> mapToSignal { result -> Signal<Data, MultipartFetchDownloadError> in
switch result { switch result {
@ -448,6 +449,7 @@ private final class MultipartFetchManager {
let continueInBackground: Bool let continueInBackground: Bool
let partReady: (Int64, Data) -> Void let partReady: (Int64, Data) -> Void
let reportCompleteSize: (Int64) -> Void let reportCompleteSize: (Int64) -> Void
let finishWithError: (MediaResourceDataFetchError) -> Void
private let useMainConnection: Bool private let useMainConnection: Bool
private var source: MultipartFetchSource private var source: MultipartFetchSource
@ -472,7 +474,7 @@ private final class MultipartFetchManager {
private var fetchSpeedRecords: [FetchSpeedRecord] = [] private var fetchSpeedRecords: [FetchSpeedRecord] = []
private var totalFetchedByteCount: Int = 0 private var totalFetchedByteCount: Int = 0
init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int64?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext?, partReady: @escaping (Int64, Data) -> Void, reportCompleteSize: @escaping (Int64) -> Void, useMainConnection: Bool) { init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int64?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext?, partReady: @escaping (Int64, Data) -> Void, reportCompleteSize: @escaping (Int64) -> Void, finishWithError: @escaping (MediaResourceDataFetchError) -> Void, useMainConnection: Bool) {
self.resource = resource self.resource = resource
self.parameters = parameters self.parameters = parameters
self.consumerId = Int64.random(in: Int64.min ... Int64.max) self.consumerId = Int64.random(in: Int64.min ... Int64.max)
@ -528,6 +530,7 @@ private final class MultipartFetchManager {
self.source = .master(location: location, download: DownloadWrapper(consumerId: self.consumerId, datacenterId: location.datacenterId, isCdn: false, network: network, useMainConnection: self.useMainConnection)) self.source = .master(location: location, download: DownloadWrapper(consumerId: self.consumerId, datacenterId: location.datacenterId, isCdn: false, network: network, useMainConnection: self.useMainConnection))
self.partReady = partReady self.partReady = partReady
self.reportCompleteSize = reportCompleteSize self.reportCompleteSize = reportCompleteSize
self.finishWithError = finishWithError
self.rangesDisposable = (intervals self.rangesDisposable = (intervals
|> deliverOn(self.queue)).start(next: { [weak self] intervals in |> deliverOn(self.queue)).start(next: { [weak self] intervals in
@ -734,6 +737,8 @@ private final class MultipartFetchManager {
switch error { switch error {
case .generic: case .generic:
break break
case .fatal:
strongSelf.finishWithError(.generic)
case .revalidateMediaReference: case .revalidateMediaReference:
if !strongSelf.revalidatingMediaReference && !strongSelf.revalidatedMediaReference { if !strongSelf.revalidatingMediaReference && !strongSelf.revalidatedMediaReference {
strongSelf.revalidatingMediaReference = true strongSelf.revalidatingMediaReference = true
@ -821,7 +826,7 @@ public func resourceFetchInfo(resource: TelegramMediaResource) -> MediaResourceF
func multipartFetch(postbox: Postbox, network: Network, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int64? = nil, continueInBackground: Bool = false, useMainConnection: Bool = false) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> { func multipartFetch(postbox: Postbox, network: Network, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int64? = nil, continueInBackground: Bool = false, useMainConnection: Bool = false) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return Signal { subscriber in return Signal { subscriber in
let location: MultipartFetchMasterLocation let location: MultipartFetchMasterLocation
if let resource = resource as? WebFileReferenceMediaResource { if let resource = resource as? MediaResourceWithWebFileReference {
location = .web(Int32(datacenterId), resource.apiInputLocation) location = .web(Int32(datacenterId), resource.apiInputLocation)
} else { } else {
location = .generic(Int32(datacenterId), { resource, resourceReference, fileReference in location = .generic(Int32(datacenterId), { resource, resourceReference, fileReference in
@ -876,6 +881,8 @@ func multipartFetch(postbox: Postbox, network: Network, mediaReferenceRevalidati
}, reportCompleteSize: { size in }, reportCompleteSize: { size in
subscriber.putNext(.resourceSizeUpdated(size)) subscriber.putNext(.resourceSizeUpdated(size))
subscriber.putCompletion() subscriber.putCompletion()
}, finishWithError: { error in
subscriber.putError(error)
}, useMainConnection: useMainConnection) }, useMainConnection: useMainConnection)
manager.start() manager.start()

View File

@ -49,7 +49,7 @@ func fetchResource(account: Account, resource: MediaResource, intervals: Signal<
} else if let cloudResource = resource as? TelegramMultipartFetchableResource { } else if let cloudResource = resource as? TelegramMultipartFetchableResource {
return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false))
|> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: parameters)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: parameters))
} else if let webFileResource = resource as? WebFileReferenceMediaResource { } else if let webFileResource = resource as? MediaResourceWithWebFileReference {
return currentWebDocumentsHostDatacenterId(postbox: account.postbox, isTestingEnvironment: account.testingEnvironment) return currentWebDocumentsHostDatacenterId(postbox: account.postbox, isTestingEnvironment: account.testingEnvironment)
|> castError(MediaResourceDataFetchError.self) |> castError(MediaResourceDataFetchError.self)
|> mapToSignal { datacenterId -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in |> mapToSignal { datacenterId -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in

View File

@ -1,5 +1,6 @@
import Foundation import Foundation
import Postbox import Postbox
import TelegramApi
public struct CloudFileMediaResourceId: Hashable, Equatable { public struct CloudFileMediaResourceId: Hashable, Equatable {
let datacenterId: Int let datacenterId: Int
@ -664,7 +665,11 @@ public struct WebFileReferenceMediaResourceId: Hashable, Equatable {
} }
} }
public final class WebFileReferenceMediaResource: TelegramMediaResource { protocol MediaResourceWithWebFileReference: TelegramMediaResource {
var apiInputLocation: Api.InputWebFileLocation { get }
}
public final class WebFileReferenceMediaResource: TelegramMediaResource, MediaResourceWithWebFileReference {
public let url: String public let url: String
public let actualSize: Int64 public let actualSize: Int64
public var size: Int64? { public var size: Int64? {
@ -705,8 +710,71 @@ public final class WebFileReferenceMediaResource: TelegramMediaResource {
return false return false
} }
} }
var apiInputLocation: Api.InputWebFileLocation {
return .inputWebFileLocation(url: self.url, accessHash: self.accessHash)
}
} }
final class AlbumCoverResource: TelegramMediaResource, MediaResourceWithWebFileReference {
var id: MediaResourceId {
return MediaResourceId("AlbumCoverResource-\(self.datacenterId)-\(self.title)-\(self.performer)")
}
let datacenterId: Int
let size: Int64? = nil
func isEqual(to: MediaResource) -> Bool {
return self === to
}
var fileReference: Data? {
if let file = self.file, let resource = file.media.resource as? CloudDocumentMediaResource {
return resource.fileReference
} else {
return nil
}
}
let file: FileMediaReference?
let title: String
let performer: String
init(datacenterId: Int, file: FileMediaReference?, title: String, performer: String) {
self.datacenterId = datacenterId
self.file = file
self.title = title
self.performer = performer
}
init(decoder: PostboxDecoder) {
preconditionFailure()
}
func encode(_ encoder: PostboxEncoder) {
}
var apiInputLocation: Api.InputWebFileLocation {
var flags: Int32 = 0
var document: Api.InputDocument?
if let file = self.file, let resource = file.media.resource as? CloudDocumentMediaResource {
document = .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data()))
flags |= 1 << 0
}
if !self.title.isEmpty {
flags |= 1 << 1
}
if !self.performer.isEmpty {
flags |= 1 << 1
}
return .inputWebFileAudioAlbumThumbLocation(
flags: flags,
document: document,
title: self.title.isEmpty ? nil : self.title,
performer: self.performer.isEmpty ? nil : self.performer
)
}
}
public struct SecretFileMediaResourceId: Hashable, Equatable { public struct SecretFileMediaResourceId: Hashable, Equatable {
public let fileId: Int64 public let fileId: Int64

View File

@ -1,10 +1,59 @@
import Foundation import Foundation
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
import TelegramApi
public typealias EngineTempBox = TempBox public typealias EngineTempBox = TempBox
public typealias EngineTempBoxFile = TempBoxFile public typealias EngineTempBoxFile = TempBoxFile
func bufferedFetch(_ signal: Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error>) -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> {
return Signal { subscriber in
final class State {
var data = Data()
var isCompleted: Bool = false
init() {
}
}
let state = Atomic<State>(value: State())
return signal.start(next: { value in
switch value {
case let .dataPart(_, data, _, _):
let _ = state.with { state in
state.data.append(data)
}
case let .moveTempFile(file):
let _ = state.with { state in
state.isCompleted = true
}
subscriber.putNext(.moveTempFile(file: file))
case .resourceSizeUpdated:
break
default:
assert(false)
break
}
}, error: { error in
subscriber.putError(error)
}, completed: {
let tempFile = state.with { state -> TempBoxFile? in
if state.isCompleted {
return nil
} else {
let tempFile = TempBox.shared.tempFile(fileName: "data")
let _ = try? state.data.write(to: URL(fileURLWithPath: tempFile.path), options: .atomic)
return tempFile
}
}
if let tempFile = tempFile {
subscriber.putNext(.moveTempFile(file: tempFile))
}
subscriber.putCompletion()
})
}
}
public final class EngineMediaResource: Equatable { public final class EngineMediaResource: Equatable {
public enum CacheTimeout { public enum CacheTimeout {
case `default` case `default`
@ -29,7 +78,14 @@ public final class EngineMediaResource: Equatable {
public final class Fetch { public final class Fetch {
public enum Result { public enum Result {
case dataPart(resourceOffset: Int64, data: Data, range: Range<Int64>, complete: Bool)
case resourceSizeUpdated(Int64)
case progressUpdated(Float)
case replaceHeader(data: Data, range: Range<Int64>)
case moveLocalFile(path: String)
case moveTempFile(file: TempBoxFile) case moveTempFile(file: TempBoxFile)
case copyLocalItem(MediaResourceDataFetchCopyLocalItem)
case reset
} }
public enum Error { public enum Error {
@ -189,6 +245,9 @@ public extension TelegramEngine {
switch result { switch result {
case let .moveTempFile(file): case let .moveTempFile(file):
mappedResult = .tempFile(file) mappedResult = .tempFile(file)
default:
assert(false)
return
} }
subscriber.putNext(mappedResult) subscriber.putNext(mappedResult)
}, completed: { }, completed: {
@ -219,6 +278,53 @@ public extension TelegramEngine {
} }
} }
} }
public func fetchAlbumCover(file: FileMediaReference?, title: String, performer: String) -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> {
let datacenterId: Int
if let resource = file?.media.resource as? CloudDocumentMediaResource {
datacenterId = resource.datacenterId
} else {
datacenterId = self.account.network.datacenterId
}
let resource = AlbumCoverResource(datacenterId: datacenterId, file: file, title: title, performer: performer)
return bufferedFetch(multipartFetch(postbox: self.account.postbox, network: self.account.network, mediaReferenceRevalidationContext: self.account.mediaReferenceRevalidationContext, resource: resource, datacenterId: datacenterId, size: nil, intervals: .single([(0 ..< Int64.max, .default)]), parameters: MediaResourceFetchParameters(
tag: nil,
info: TelegramCloudMediaResourceFetchInfo(
reference: MediaResourceReference.standalone(resource: resource),
preferBackgroundReferenceRevalidation: false,
continueInBackground: false
),
isRandomAccessAllowed: true
))
|> map { result -> EngineMediaResource.Fetch.Result in
switch result {
case let .dataPart(resourceOffset, data, range, complete):
return .dataPart(resourceOffset: resourceOffset, data: data, range: range, complete: complete)
case let .resourceSizeUpdated(size):
return .resourceSizeUpdated(size)
case let .progressUpdated(value):
return .progressUpdated(value)
case let .replaceHeader(data, range):
return .replaceHeader(data: data, range: range)
case let .moveLocalFile(path):
return .moveLocalFile(path: path)
case let .moveTempFile(file):
return .moveTempFile(file: file)
case let .copyLocalItem(item):
return .copyLocalItem(item)
case .reset:
return .reset
}
}
|> mapError { error -> EngineMediaResource.Fetch.Error in
switch error {
case .generic:
return .generic
}
})
}
public func cancelAllFetches(id: String) { public func cancelAllFetches(id: String) {
preconditionFailure() preconditionFailure()

View File

@ -21,6 +21,7 @@ swift_library(
"//submodules/TelegramCore:TelegramCore", "//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TextFormat:TextFormat", "//submodules/TextFormat:TextFormat",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -11,6 +11,7 @@ import Postbox
import TelegramPresentationData import TelegramPresentationData
import EmojiTextAttachmentView import EmojiTextAttachmentView
import TextFormat import TextFormat
import TelegramUIPreferences
public final class EmojiSuggestionsComponent: Component { public final class EmojiSuggestionsComponent: Component {
public typealias EnvironmentType = Empty public typealias EnvironmentType = Empty
@ -33,10 +34,20 @@ public final class EmojiSuggestionsComponent: Component {
return combineLatest( return combineLatest(
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
context.account.viewTracker.featuredEmojiPacks(), context.account.viewTracker.featuredEmojiPacks(),
hasPremium hasPremium,
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings])
) )
|> take(1) |> take(1)
|> map { view, featuredEmojiPacks, hasPremium -> [TelegramMediaFile] in |> map { view, featuredEmojiPacks, hasPremium, sharedData -> [TelegramMediaFile] in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
stickerSettings = value
}
if !stickerSettings.suggestAnimatedEmoji {
return []
}
var result: [TelegramMediaFile] = [] var result: [TelegramMediaFile] = []
let normalizedQuery = query.basicEmoji.0 let normalizedQuery = query.basicEmoji.0
@ -158,7 +169,9 @@ public final class EmojiSuggestionsComponent: Component {
} }
} }
private let blurView: BlurredBackgroundView
private let backgroundLayer: SimpleShapeLayer private let backgroundLayer: SimpleShapeLayer
private let shadowLayer: SimpleLayer
private let scrollView: UIScrollView private let scrollView: UIScrollView
private var component: EmojiSuggestionsComponent? private var component: EmojiSuggestionsComponent?
@ -168,11 +181,21 @@ public final class EmojiSuggestionsComponent: Component {
private var visibleLayers: [MediaId: InlineStickerItemLayer] = [:] private var visibleLayers: [MediaId: InlineStickerItemLayer] = [:]
override init(frame: CGRect) { override init(frame: CGRect) {
self.blurView = BlurredBackgroundView(color: .clear, enableBlur: true)
/*self.blurView.layer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor
self.blurView.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
self.blurView.layer.shadowRadius = 15.0
self.blurView.layer.shadowOpacity = 0.15*/
self.shadowLayer = SimpleLayer()
self.shadowLayer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor
self.shadowLayer.shadowOffset = CGSize(width: 0.0, height: 2.0)
self.shadowLayer.shadowRadius = 15.0
self.shadowLayer.shadowOpacity = 0.15
self.backgroundLayer = SimpleShapeLayer() self.backgroundLayer = SimpleShapeLayer()
self.backgroundLayer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor
self.backgroundLayer.shadowOffset = CGSize(width: 0.0, height: 2.0) self.blurView.layer.mask = self.backgroundLayer
self.backgroundLayer.shadowRadius = 15.0
self.backgroundLayer.shadowOpacity = 0.15
self.scrollView = UIScrollView() self.scrollView = UIScrollView()
@ -197,7 +220,9 @@ public final class EmojiSuggestionsComponent: Component {
self.scrollView.delegate = self self.scrollView.delegate = self
self.scrollView.clipsToBounds = true self.scrollView.clipsToBounds = true
self.layer.addSublayer(self.backgroundLayer) self.layer.addSublayer(self.shadowLayer)
self.addSubview(self.blurView)
//self.layer.addSublayer(self.backgroundLayer)
self.addSubview(self.scrollView) self.addSubview(self.scrollView)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
@ -317,16 +342,22 @@ public final class EmojiSuggestionsComponent: Component {
path.addArc(tangent1End: CGPoint(x: size.width, y: 0.0), tangent2End: CGPoint(x: size.width - radius, y: 0.0), radius: radius) path.addArc(tangent1End: CGPoint(x: size.width, y: 0.0), tangent2End: CGPoint(x: size.width - radius, y: 0.0), radius: radius)
path.addLine(to: CGPoint(x: radius, y: 0.0)) path.addLine(to: CGPoint(x: radius, y: 0.0))
self.shadowLayer.shadowPath = path
self.shadowLayer.frame = CGRect(origin: CGPoint(), size: size)
self.blurView.frame = CGRect(origin: CGPoint(), size: size)
self.blurView.update(size: size, transition: .immediate)
self.backgroundLayer.frame = CGRect(origin: CGPoint(), size: size) self.backgroundLayer.frame = CGRect(origin: CGPoint(), size: size)
self.backgroundLayer.path = path self.backgroundLayer.path = path
self.backgroundLayer.shadowPath = path //self.blurView.shadowPath = path
} }
func update(component: EmojiSuggestionsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize { func update(component: EmojiSuggestionsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let height: CGFloat = 54.0 let height: CGFloat = 54.0
if self.component?.theme !== component.theme { if self.component?.theme !== component.theme {
self.backgroundLayer.fillColor = component.theme.list.plainBackgroundColor.cgColor //self.backgroundLayer.fillColor = component.theme.list.plainBackgroundColor.cgColor
self.backgroundLayer.fillColor = UIColor.black.cgColor
self.blurView.updateColor(color: component.theme.list.plainBackgroundColor.withMultipliedAlpha(0.88), transition: .immediate)
} }
var resetScrollingPosition = false var resetScrollingPosition = false
if self.component?.files != component.files { if self.component?.files != component.files {

View File

@ -1496,7 +1496,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
} }
} }
image = playerAlbumArt(postbox: context.account.postbox, engine: context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated) image = playerAlbumArt(postbox: context.account.postbox, engine: context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated)
} }
} }
let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor) let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor)

View File

@ -111,7 +111,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem {
if file.fileName?.lowercased().hasSuffix(".ogg") == true { if file.fileName?.lowercased().hasSuffix(".ogg") == true {
albumArt = nil albumArt = nil
} else { } else {
albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false))
} }
return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0) return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0)

View File

@ -12,14 +12,16 @@ public enum EmojiStickerSuggestionMode: Int32 {
public struct StickerSettings: Codable, Equatable { public struct StickerSettings: Codable, Equatable {
public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode
public var loopAnimatedStickers: Bool public var loopAnimatedStickers: Bool
public var suggestAnimatedEmoji: Bool
public static var defaultSettings: StickerSettings { public static var defaultSettings: StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: .all, loopAnimatedStickers: true) return StickerSettings(emojiStickerSuggestionMode: .all, loopAnimatedStickers: true, suggestAnimatedEmoji: true)
} }
init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode, loopAnimatedStickers: Bool) { init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode, loopAnimatedStickers: Bool, suggestAnimatedEmoji: Bool) {
self.emojiStickerSuggestionMode = emojiStickerSuggestionMode self.emojiStickerSuggestionMode = emojiStickerSuggestionMode
self.loopAnimatedStickers = loopAnimatedStickers self.loopAnimatedStickers = loopAnimatedStickers
self.suggestAnimatedEmoji = suggestAnimatedEmoji
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
@ -27,6 +29,7 @@ public struct StickerSettings: Codable, Equatable {
self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: try container.decode(Int32.self, forKey: "emojiStickerSuggestionMode"))! self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: try container.decode(Int32.self, forKey: "emojiStickerSuggestionMode"))!
self.loopAnimatedStickers = try container.decodeIfPresent(Bool.self, forKey: "loopAnimatedStickers") ?? true self.loopAnimatedStickers = try container.decodeIfPresent(Bool.self, forKey: "loopAnimatedStickers") ?? true
self.suggestAnimatedEmoji = try container.decodeIfPresent(Bool.self, forKey: "suggestAnimatedEmoji") ?? true
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -34,18 +37,23 @@ public struct StickerSettings: Codable, Equatable {
try container.encode(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode") try container.encode(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode")
try container.encode(self.loopAnimatedStickers, forKey: "loopAnimatedStickers") try container.encode(self.loopAnimatedStickers, forKey: "loopAnimatedStickers")
try container.encode(self.suggestAnimatedEmoji, forKey: "suggestAnimatedEmoji")
} }
public static func ==(lhs: StickerSettings, rhs: StickerSettings) -> Bool { public static func ==(lhs: StickerSettings, rhs: StickerSettings) -> Bool {
return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode && lhs.loopAnimatedStickers == rhs.loopAnimatedStickers return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode && lhs.loopAnimatedStickers == rhs.loopAnimatedStickers && lhs.suggestAnimatedEmoji == rhs.suggestAnimatedEmoji
} }
public func withUpdatedEmojiStickerSuggestionMode(_ emojiStickerSuggestionMode: EmojiStickerSuggestionMode) -> StickerSettings { public func withUpdatedEmojiStickerSuggestionMode(_ emojiStickerSuggestionMode: EmojiStickerSuggestionMode) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers) return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers, suggestAnimatedEmoji: self.suggestAnimatedEmoji)
} }
public func withUpdatedLoopAnimatedStickers(_ loopAnimatedStickers: Bool) -> StickerSettings { public func withUpdatedLoopAnimatedStickers(_ loopAnimatedStickers: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: loopAnimatedStickers) return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: loopAnimatedStickers, suggestAnimatedEmoji: self.suggestAnimatedEmoji)
}
public func withUpdatedSuggestAnimatedEmoji(_ suggestAnimatedEmoji: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers, suggestAnimatedEmoji: suggestAnimatedEmoji)
} }
} }