Various improvements

This commit is contained in:
Ilya Laktyushin
2023-11-22 03:24:33 +04:00
parent 31eb1081df
commit 90f09a13e4
134 changed files with 8488 additions and 3171 deletions

View File

@@ -502,5 +502,26 @@ public extension TelegramEngine.EngineData.Item {
return value
}
}
public struct AudioTranscriptionTrial: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = AudioTranscription.TrialState
public init() {
}
var key: PostboxViewKey {
return .preferences(keys: Set([PreferencesKeys.audioTranscriptionTrialState]))
}
func extract(view: PostboxView) -> Result {
guard let view = view as? PreferencesView else {
preconditionFailure()
}
guard let value = view.values[PreferencesKeys.audioTranscriptionTrialState]?.get(AudioTranscription.TrialState.self) else {
return AudioTranscription.TrialState.defaultValue
}
return value
}
}
}
}

View File

@@ -14,6 +14,7 @@ public final class EngineStoryViewListContext {
}
public enum SortMode {
case repostsFirst
case reactionsFirst
case recentFirst
}
@@ -176,6 +177,16 @@ public final class EngineStoryViewListContext {
}
}
switch sortMode {
case .repostsFirst:
items.sort(by: { lhs, rhs in
if (lhs.reaction == nil) != (rhs.reaction == nil) {
return lhs.reaction != nil
}
if lhs.timestamp != rhs.timestamp {
return lhs.timestamp > rhs.timestamp
}
return lhs.peer.id < rhs.peer.id
})
case .reactionsFirst:
items.sort(by: { lhs, rhs in
if (lhs.reaction == nil) != (rhs.reaction == nil) {
@@ -287,7 +298,7 @@ public final class EngineStoryViewListContext {
switch sortMode {
case .reactionsFirst:
flags |= (1 << 2)
case .recentFirst:
case .recentFirst, .repostsFirst:
break
}
if searchQuery != nil {
@@ -376,7 +387,8 @@ public final class EngineStoryViewListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
))
if let entry = CodableEntry(updatedItem) {
transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry)
@@ -413,7 +425,8 @@ public final class EngineStoryViewListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
))
if let entry = CodableEntry(updatedItem) {
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)

View File

@@ -39,6 +39,40 @@ public extension Stories {
}
}
struct PendingForwardInfo: Codable, Equatable {
private enum CodingKeys: String, CodingKey {
case peerId = "peerId"
case storyId = "storyId"
case isForwardingDisabled = "isForwardingDisabled"
}
public let peerId: EnginePeer.Id
public let storyId: Int32
public let isForwardingDisabled: Bool
public init(peerId: EnginePeer.Id, storyId: Int32, isForwardingDisabled: Bool) {
self.peerId = peerId
self.storyId = storyId
self.isForwardingDisabled = isForwardingDisabled
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.peerId = EnginePeer.Id(try container.decode(Int64.self, forKey: .peerId))
self.storyId = try container.decode(Int32.self, forKey: .storyId)
self.isForwardingDisabled = try container.decode(Bool.self, forKey: .isForwardingDisabled)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.peerId.toInt64(), forKey: .peerId)
try container.encode(self.storyId, forKey: .storyId)
try container.encode(self.isForwardingDisabled, forKey: .isForwardingDisabled)
}
}
final class PendingItem: Equatable, Codable {
private enum CodingKeys: CodingKey {
case target
@@ -54,6 +88,7 @@ public extension Stories {
case isForwardingDisabled
case period
case randomId
case forwardInfo
}
public let target: PendingTarget
@@ -69,6 +104,7 @@ public extension Stories {
public let isForwardingDisabled: Bool
public let period: Int32
public let randomId: Int64
public let forwardInfo: PendingForwardInfo?
public init(
target: PendingTarget,
@@ -83,7 +119,8 @@ public extension Stories {
privacy: EngineStoryPrivacy,
isForwardingDisabled: Bool,
period: Int32,
randomId: Int64
randomId: Int64,
forwardInfo: PendingForwardInfo?
) {
self.target = target
self.stableId = stableId
@@ -98,6 +135,7 @@ public extension Stories {
self.isForwardingDisabled = isForwardingDisabled
self.period = period
self.randomId = randomId
self.forwardInfo = forwardInfo
}
public init(from decoder: Decoder) throws {
@@ -123,6 +161,8 @@ public extension Stories {
self.isForwardingDisabled = try container.decodeIfPresent(Bool.self, forKey: .isForwardingDisabled) ?? false
self.period = try container.decode(Int32.self, forKey: .period)
self.randomId = try container.decode(Int64.self, forKey: .randomId)
self.forwardInfo = try container.decodeIfPresent(PendingForwardInfo.self, forKey: .forwardInfo)
}
public func encode(to encoder: Encoder) throws {
@@ -150,6 +190,7 @@ public extension Stories {
try container.encode(self.isForwardingDisabled, forKey: .isForwardingDisabled)
try container.encode(self.period, forKey: .period)
try container.encode(self.randomId, forKey: .randomId)
try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo)
}
public static func ==(lhs: PendingItem, rhs: PendingItem) -> Bool {
@@ -186,6 +227,9 @@ public extension Stories {
if lhs.randomId != rhs.randomId {
return false
}
if lhs.forwardInfo != rhs.forwardInfo {
return false
}
return true
}
}
@@ -359,8 +403,9 @@ final class PendingStoryManager {
case let .peer(peerId):
toPeerId = peerId
}
let stableId = firstItem.stableId
pendingItemContext.disposable = (_internal_uploadStoryImpl(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.revalidationContext, auxiliaryMethods: self.auxiliaryMethods, toPeerId: toPeerId, stableId: stableId, media: firstItem.media, mediaAreas: firstItem.mediaAreas, text: firstItem.text, entities: firstItem.entities, embeddedStickers: firstItem.embeddedStickers, pin: firstItem.pin, privacy: firstItem.privacy, isForwardingDisabled: firstItem.isForwardingDisabled, period: Int(firstItem.period), randomId: firstItem.randomId)
pendingItemContext.disposable = (_internal_uploadStoryImpl(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.revalidationContext, auxiliaryMethods: self.auxiliaryMethods, toPeerId: toPeerId, stableId: stableId, media: firstItem.media, mediaAreas: firstItem.mediaAreas, text: firstItem.text, entities: firstItem.entities, embeddedStickers: firstItem.embeddedStickers, pin: firstItem.pin, privacy: firstItem.privacy, isForwardingDisabled: firstItem.isForwardingDisabled, period: Int(firstItem.period), randomId: firstItem.randomId, forwardInfo: firstItem.forwardInfo)
|> deliverOn(self.queue)).start(next: { [weak self] event in
guard let `self` = self else {
return

View File

@@ -6,11 +6,14 @@ import TelegramApi
public enum EngineStoryInputMedia {
case image(dimensions: PixelDimensions, data: Data, stickers: [TelegramMediaFile])
case video(dimensions: PixelDimensions, duration: Double, resource: TelegramMediaResource, firstFrameFile: TempBoxFile?, stickers: [TelegramMediaFile])
case existing(media: Media)
var embeddedStickers: [TelegramMediaFile] {
switch self {
case let .image(_, _, stickers), let .video(_, _, _, _, stickers):
return stickers
case .existing:
return []
}
}
}
@@ -36,6 +39,34 @@ public extension EngineStoryPrivacy {
}
}
public extension EngineStoryItem.ForwardInfo {
init?(_ forwardInfo: Stories.Item.ForwardInfo, transaction: Transaction) {
switch forwardInfo {
case let .known(peerId, storyId):
if let peer = transaction.getPeer(peerId) {
self = .known(peer: EnginePeer(peer), storyId: storyId)
} else {
return nil
}
case let .unknown(name):
self = .unknown(name: name)
}
}
init?(_ forwardInfo: Stories.Item.ForwardInfo, peers: [PeerId: Peer]) {
switch forwardInfo {
case let .known(peerId, storyId):
if let peer = peers[peerId] {
self = .known(peer: EnginePeer(peer), storyId: storyId)
} else {
return nil
}
case let .unknown(name):
self = .unknown(name: name)
}
}
}
public enum Stories {
public final class Item: Codable, Equatable {
public struct Views: Codable, Equatable {
@@ -162,6 +193,49 @@ public enum Stories {
}
}
public enum ForwardInfo: Codable, Equatable {
public enum DecodingError: Error {
case generic
}
private enum CodingKeys: CodingKey {
case discriminator
case authorPeerId
case storyId
case authorName
}
case known(peerId: EnginePeer.Id, storyId: Int32)
case unknown(name: String)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch try container.decode(Int32.self, forKey: .discriminator) {
case 0:
self = .known(peerId: EnginePeer.Id(try container.decode(Int64.self, forKey: .authorPeerId)), storyId: try container.decode(Int32.self, forKey: .storyId))
case 1:
self = .unknown(name: try container.decode(String.self, forKey: .authorName))
default:
throw DecodingError.generic
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .known(peerId, storyId):
try container.encode(0 as Int32, forKey: .discriminator)
try container.encode(peerId.toInt64(), forKey: .authorPeerId)
try container.encode(storyId, forKey: .storyId)
case let .unknown(name):
try container.encode(1 as Int32, forKey: .discriminator)
try container.encode(name, forKey: .authorName)
}
}
}
private enum CodingKeys: String, CodingKey {
case id
case timestamp
@@ -182,6 +256,7 @@ public enum Stories {
case isEdited
case isMy
case myReaction
case forwardInfo
}
public let id: Int32
@@ -203,6 +278,7 @@ public enum Stories {
public let isEdited: Bool
public let isMy: Bool
public let myReaction: MessageReaction.Reaction?
public let forwardInfo: ForwardInfo?
public init(
id: Int32,
@@ -223,7 +299,8 @@ public enum Stories {
isForwardingDisabled: Bool,
isEdited: Bool,
isMy: Bool,
myReaction: MessageReaction.Reaction?
myReaction: MessageReaction.Reaction?,
forwardInfo: ForwardInfo?
) {
self.id = id
self.timestamp = timestamp
@@ -244,6 +321,7 @@ public enum Stories {
self.isEdited = isEdited
self.isMy = isMy
self.myReaction = myReaction
self.forwardInfo = forwardInfo
}
public init(from decoder: Decoder) throws {
@@ -274,6 +352,7 @@ public enum Stories {
self.isEdited = try container.decodeIfPresent(Bool.self, forKey: .isEdited) ?? false
self.isMy = try container.decodeIfPresent(Bool.self, forKey: .isMy) ?? false
self.myReaction = try container.decodeIfPresent(MessageReaction.Reaction.self, forKey: .myReaction)
self.forwardInfo = try container.decodeIfPresent(ForwardInfo.self, forKey: .forwardInfo)
}
public func encode(to encoder: Encoder) throws {
@@ -305,6 +384,7 @@ public enum Stories {
try container.encode(self.isEdited, forKey: .isEdited)
try container.encode(self.isMy, forKey: .isMy)
try container.encodeIfPresent(self.myReaction, forKey: .myReaction)
try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo)
}
public static func ==(lhs: Item, rhs: Item) -> Bool {
@@ -369,7 +449,9 @@ public enum Stories {
if lhs.myReaction != rhs.myReaction {
return false
}
if lhs.forwardInfo != rhs.forwardInfo {
return false
}
return true
}
}
@@ -752,6 +834,8 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput
)
return fileMedia
case let .existing(media):
return media
}
}
@@ -845,7 +929,7 @@ private func apiInputPrivacyRules(privacy: EngineStoryPrivacy, transaction: Tran
return privacyRules
}
func _internal_uploadStory(account: Account, target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal<Int32, NoError> {
func _internal_uploadStory(account: Account, target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?) -> Signal<Int32, NoError> {
let inputMedia = prepareUploadStoryContent(account: account, media: media)
return (account.postbox.transaction { transaction in
@@ -872,7 +956,8 @@ func _internal_uploadStory(account: Account, target: Stories.PendingTarget, medi
privacy: privacy,
isForwardingDisabled: isForwardingDisabled,
period: Int32(period),
randomId: randomId
randomId: randomId,
forwardInfo: forwardInfo
))
transaction.setLocalStoryState(state: CodableEntry(currentState))
return stableId
@@ -917,7 +1002,28 @@ private func _internal_putPendingStoryIdMapping(peerId: PeerId, stableId: Int32,
}
}
func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId: PeerId, stateManager: AccountStateManager, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, toPeerId: PeerId, stableId: Int32, media: Media, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], embeddedStickers: [TelegramMediaFile], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal<StoryUploadResult, NoError> {
func _internal_uploadStoryImpl(
postbox: Postbox,
network: Network,
accountPeerId: PeerId,
stateManager: AccountStateManager,
messageMediaPreuploadManager: MessageMediaPreuploadManager,
revalidationContext: MediaReferenceRevalidationContext,
auxiliaryMethods: AccountAuxiliaryMethods,
toPeerId: PeerId,
stableId: Int32,
media: Media,
mediaAreas: [MediaArea],
text: String,
entities: [MessageTextEntity],
embeddedStickers: [TelegramMediaFile],
pin: Bool,
privacy: EngineStoryPrivacy,
isForwardingDisabled: Bool,
period: Int,
randomId: Int64,
forwardInfo: Stories.PendingForwardInfo?
) -> Signal<StoryUploadResult, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(toPeerId).flatMap(apiInputPeer)
}
@@ -975,6 +1081,14 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
flags |= 1 << 5
}
var fwdFromId: Api.InputPeer?
var fwdFromStory: Int32?
if let forwardInfo = forwardInfo, let inputPeer = transaction.getPeer(forwardInfo.peerId).flatMap({ apiInputPeer($0) }) {
flags |= 1 << 6
fwdFromId = inputPeer
fwdFromStory = forwardInfo.storyId
}
return network.request(Api.functions.stories.sendStory(
flags: flags,
peer: inputPeer,
@@ -985,8 +1099,8 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
privacyRules: privacyRules,
randomId: randomId,
period: Int32(period),
fwdFromId: nil,
fwdFromStory: nil
fwdFromId: fwdFromId,
fwdFromStory: fwdFromStory
))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
@@ -1034,7 +1148,8 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
)
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends))
@@ -1214,7 +1329,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
)
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
transaction.setStory(id: storyId, value: entry)
@@ -1243,7 +1359,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
)
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
@@ -1435,7 +1552,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
)
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
@@ -1463,7 +1581,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
)
updatedItems.append(updatedItem)
}
@@ -1538,11 +1657,26 @@ extension Stories.Item.Views {
}
}
extension Stories.Item.ForwardInfo {
init?(apiForwardInfo: Api.StoryFwdHeader) {
switch apiForwardInfo {
case let .storyFwdHeader(_, from, fromName, storyId):
if let from = from, let storyId = storyId {
self = .known(peerId: from.peerId, storyId: storyId)
return
} else if let fromName = fromName {
self = .unknown(name: fromName)
return
}
}
return nil
}
}
extension Stories.StoredItem {
init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) {
switch apiStoryItem {
case let .storyItem(flags, id, date, fwdFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction):
let _ = fwdFrom
case let .storyItem(flags, id, date, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction):
let (parsedMedia, _, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let parsedMedia = parsedMedia {
var parsedPrivacy: Stories.Item.Privacy?
@@ -1613,6 +1747,13 @@ extension Stories.StoredItem {
mergedIsMy = (flags & (1 << 16)) != 0
}
var mergedForwardInfo: Stories.Item.ForwardInfo?
if isMin, let existingItem = existingItem {
mergedForwardInfo = existingItem.forwardInfo
} else {
mergedForwardInfo = forwardFrom.flatMap(Stories.Item.ForwardInfo.init(apiForwardInfo:))
}
let item = Stories.Item(
id: id,
timestamp: date,
@@ -1632,7 +1773,8 @@ extension Stories.StoredItem {
isForwardingDisabled: isForwardingDisabled,
isEdited: isEdited,
isMy: mergedIsMy,
myReaction: mergedMyReaction
myReaction: mergedMyReaction,
forwardInfo: mergedForwardInfo
)
self = .item(item)
} else {
@@ -2104,7 +2246,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: reaction
myReaction: reaction,
forwardInfo: item.forwardInfo
))
updatedItemValue = updatedItem
if let entry = CodableEntry(updatedItem) {
@@ -2135,7 +2278,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: reaction
myReaction: reaction,
forwardInfo: item.forwardInfo
))
updatedItemValue = updatedItem
if let entry = CodableEntry(updatedItem) {

View File

@@ -51,6 +51,11 @@ public final class EngineStoryItem: Equatable {
}
}
public enum ForwardInfo: Equatable {
case known(peer: EnginePeer, storyId: Int32)
case unknown(name: String)
}
public let id: Int32
public let timestamp: Int32
public let expirationTimestamp: Int32
@@ -71,8 +76,9 @@ public final class EngineStoryItem: Equatable {
public let isEdited: Bool
public let isMy: Bool
public let myReaction: MessageReaction.Reaction?
public let forwardInfo: ForwardInfo?
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?) {
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?, forwardInfo: ForwardInfo?) {
self.id = id
self.timestamp = timestamp
self.expirationTimestamp = expirationTimestamp
@@ -93,6 +99,7 @@ public final class EngineStoryItem: Equatable {
self.isEdited = isEdited
self.isMy = isMy
self.myReaction = myReaction
self.forwardInfo = forwardInfo
}
public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool {
@@ -156,11 +163,25 @@ public final class EngineStoryItem: Equatable {
if lhs.myReaction != rhs.myReaction {
return false
}
if lhs.forwardInfo != rhs.forwardInfo {
return false
}
return true
}
}
extension EngineStoryItem {
extension EngineStoryItem.ForwardInfo {
var storedForwardInfo: Stories.Item.ForwardInfo {
switch self {
case let .known(peer, storyId):
return .known(peerId: peer.id, storyId: storyId)
case let .unknown(name):
return .unknown(name: name)
}
}
}
public extension EngineStoryItem {
func asStoryItem() -> Stories.Item {
return Stories.Item(
id: self.id,
@@ -195,7 +216,8 @@ extension EngineStoryItem {
isForwardingDisabled: self.isForwardingDisabled,
isEdited: self.isEdited,
isMy: self.isMy,
myReaction: self.myReaction
myReaction: self.myReaction,
forwardInfo: self.forwardInfo?.storedForwardInfo
)
}
}
@@ -570,7 +592,8 @@ public final class PeerStoryListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
)
items.append(mappedItem)
@@ -713,7 +736,8 @@ public final class PeerStoryListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
)
storyItems.append(mappedItem)
}
@@ -802,6 +826,11 @@ public final class PeerStoryListContext {
}
}
}
if let forwardInfo = item.forwardInfo, case let .known(peerId, _) = forwardInfo {
if let peer = transaction.getPeer(peerId) {
peers[peer.id] = peer
}
}
}
}
default:
@@ -868,7 +897,8 @@ public final class PeerStoryListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
)
finalUpdatedState = updatedState
}
@@ -914,7 +944,8 @@ public final class PeerStoryListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
)
finalUpdatedState = updatedState
} else {
@@ -962,7 +993,8 @@ public final class PeerStoryListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
))
updatedState.items.sort(by: { lhs, rhs in
return lhs.timestamp > rhs.timestamp
@@ -1006,7 +1038,8 @@ public final class PeerStoryListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }
))
updatedState.items.sort(by: { lhs, rhs in
return lhs.timestamp > rhs.timestamp
@@ -1174,7 +1207,8 @@ public final class PeerExpiringStoryListContext {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }
)
items.append(.item(mappedItem))
}

View File

@@ -476,7 +476,7 @@ public extension TelegramEngine {
public func transcribeAudio(messageId: MessageId) -> Signal<EngineAudioTranscriptionResult, NoError> {
return _internal_transcribeAudio(postbox: self.account.postbox, network: self.account.network, messageId: messageId)
}
public func storeLocallyTranscribedAudio(messageId: MessageId, text: String, isFinal: Bool, error: AudioTranscriptionMessageAttribute.TranscriptionError?) -> Signal<Never, NoError> {
return self.account.postbox.transaction { transaction -> Void in
transaction.updateMessage(messageId, update: { currentMessage in
@@ -1178,7 +1178,8 @@ public extension TelegramEngine {
isForwardingDisabled: item.isForwardingDisabled,
isEdited: item.isEdited,
isMy: item.isMy,
myReaction: item.myReaction
myReaction: item.myReaction,
forwardInfo: item.forwardInfo
))
if let entry = CodableEntry(updatedItem) {
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
@@ -1192,8 +1193,8 @@ public extension TelegramEngine {
}
}
public func uploadStory(target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal<Int32, NoError> {
return _internal_uploadStory(account: self.account, target: target, media: media, mediaAreas: mediaAreas, text: text, entities: entities, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: period, randomId: randomId)
public func uploadStory(target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?) -> Signal<Int32, NoError> {
return _internal_uploadStory(account: self.account, target: target, media: media, mediaAreas: mediaAreas, text: text, entities: entities, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: period, randomId: randomId, forwardInfo: forwardInfo)
}
public func allStoriesUploadEvents() -> Signal<(Int32, Int32), NoError> {

View File

@@ -0,0 +1,153 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
public enum EngineAudioTranscriptionResult {
case success
case error
}
func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<EngineAudioTranscriptionResult, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<EngineAudioTranscriptionResult, NoError> in
guard let inputPeer = inputPeer else {
return .single(.error)
}
return network.request(Api.functions.messages.transcribeAudio(peer: inputPeer, msgId: messageId.id))
|> map { result -> Result<Api.messages.TranscribedAudio, AudioTranscriptionMessageAttribute.TranscriptionError> in
return .success(result)
}
|> `catch` { error -> Signal<Result<Api.messages.TranscribedAudio, AudioTranscriptionMessageAttribute.TranscriptionError>, NoError> in
let mappedError: AudioTranscriptionMessageAttribute.TranscriptionError
if error.errorDescription == "MSG_VOICE_TOO_LONG" {
mappedError = .tooLong
} else {
mappedError = .generic
}
return .single(.failure(mappedError))
}
|> mapToSignal { result -> Signal<EngineAudioTranscriptionResult, NoError> in
return postbox.transaction { transaction -> EngineAudioTranscriptionResult in
let updatedAttribute: AudioTranscriptionMessageAttribute
switch result {
case let .success(transcribedAudio):
switch transcribedAudio {
case let .transcribedAudio(flags, transcriptionId, text, trialRemainingCount, trialUntilDate):
let isPending = (flags & (1 << 0)) != 0
_internal_updateAudioTranscriptionTrialState(transaction: transaction) { current in
var updated = current
if let trialRemainingCount = trialRemainingCount, trialRemainingCount > 0 {
updated = updated.withUpdatedRemainingCount(trialRemainingCount)
} else if let trialUntilDate = trialUntilDate {
updated = updated.withUpdatedCooldownUntilTime(trialUntilDate)
} else {
updated = updated.withUpdatedCooldownUntilTime(nil)
}
return updated
}
updatedAttribute = AudioTranscriptionMessageAttribute(id: transcriptionId, text: text, isPending: isPending, didRate: false, error: nil)
}
case let .failure(error):
updatedAttribute = AudioTranscriptionMessageAttribute(id: 0, text: "", isPending: false, didRate: false, error: error)
}
transaction.updateMessage(messageId, update: { currentMessage in
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
var attributes = currentMessage.attributes.filter { !($0 is AudioTranscriptionMessageAttribute) }
attributes.append(updatedAttribute)
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
if updatedAttribute.error == nil {
return .success
} else {
return .error
}
}
}
}
}
func _internal_rateAudioTranscription(postbox: Postbox, network: Network, messageId: MessageId, id: Int64, isGood: Bool) -> Signal<Never, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
transaction.updateMessage(messageId, update: { currentMessage in
var storeForwardInfo: StoreMessageForwardInfo?
if let forwardInfo = currentMessage.forwardInfo {
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
}
var attributes = currentMessage.attributes
for i in 0 ..< attributes.count {
if let attribute = attributes[i] as? AudioTranscriptionMessageAttribute {
attributes[i] = attribute.withDidRate()
}
}
return .update(StoreMessage(
id: currentMessage.id,
globallyUniqueId: currentMessage.globallyUniqueId,
groupingKey: currentMessage.groupingKey,
threadId: currentMessage.threadId,
timestamp: currentMessage.timestamp,
flags: StoreMessageFlags(currentMessage.flags),
tags: currentMessage.tags,
globalTags: currentMessage.globalTags,
localTags: currentMessage.localTags,
forwardInfo: storeForwardInfo,
authorId: currentMessage.author?.id,
text: currentMessage.text,
attributes: attributes,
media: currentMessage.media
))
})
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
guard let inputPeer = inputPeer else {
return .complete()
}
return network.request(Api.functions.messages.rateTranscribedAudio(peer: inputPeer, msgId: messageId.id, transcriptionId: id, good: isGood ? .boolTrue : .boolFalse))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)
}
|> ignoreValues
}
}
public enum AudioTranscription {
public struct TrialState: Equatable, Codable {
public let cooldownUntilTime: Int32?
public let remainingCount: Int32
func withUpdatedCooldownUntilTime(_ time: Int32?) -> AudioTranscription.TrialState {
return AudioTranscription.TrialState(cooldownUntilTime: time, remainingCount: time != nil ? 0 : max(1, self.remainingCount))
}
func withUpdatedRemainingCount(_ remainingCount: Int32) -> AudioTranscription.TrialState {
return AudioTranscription.TrialState(remainingCount: remainingCount)
}
public init(cooldownUntilTime: Int32? = nil, remainingCount: Int32) {
self.cooldownUntilTime = cooldownUntilTime
self.remainingCount = remainingCount
}
public static var defaultValue: AudioTranscription.TrialState {
return AudioTranscription.TrialState(
cooldownUntilTime: nil,
remainingCount: 1
)
}
}
}
func _internal_updateAudioTranscriptionTrialState(transaction: Transaction, _ f: (AudioTranscription.TrialState) -> AudioTranscription.TrialState) {
let current = transaction.getPreferencesEntry(key: PreferencesKeys.audioTranscriptionTrialState)?.get(AudioTranscription.TrialState.self) ?? .defaultValue
transaction.setPreferencesEntry(key: PreferencesKeys.audioTranscriptionTrialState, value: PreferencesEntry(f(current)))
}

View File

@@ -155,108 +155,3 @@ func _internal_togglePeerMessagesTranslationHidden(account: Account, peerId: Eng
|> ignoreValues
}
}
public enum EngineAudioTranscriptionResult {
case success
case error
}
func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<EngineAudioTranscriptionResult, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<EngineAudioTranscriptionResult, NoError> in
guard let inputPeer = inputPeer else {
return .single(.error)
}
return network.request(Api.functions.messages.transcribeAudio(peer: inputPeer, msgId: messageId.id))
|> map { result -> Result<Api.messages.TranscribedAudio, AudioTranscriptionMessageAttribute.TranscriptionError> in
return .success(result)
}
|> `catch` { error -> Signal<Result<Api.messages.TranscribedAudio, AudioTranscriptionMessageAttribute.TranscriptionError>, NoError> in
let mappedError: AudioTranscriptionMessageAttribute.TranscriptionError
if error.errorDescription == "MSG_VOICE_TOO_LONG" {
mappedError = .tooLong
} else {
mappedError = .generic
}
return .single(.failure(mappedError))
}
|> mapToSignal { result -> Signal<EngineAudioTranscriptionResult, NoError> in
return postbox.transaction { transaction -> EngineAudioTranscriptionResult in
let updatedAttribute: AudioTranscriptionMessageAttribute
switch result {
case let .success(transcribedAudio):
switch transcribedAudio {
case let .transcribedAudio(flags, transcriptionId, text):
let isPending = (flags & (1 << 0)) != 0
updatedAttribute = AudioTranscriptionMessageAttribute(id: transcriptionId, text: text, isPending: isPending, didRate: false, error: nil)
}
case let .failure(error):
updatedAttribute = AudioTranscriptionMessageAttribute(id: 0, text: "", isPending: false, didRate: false, error: error)
}
transaction.updateMessage(messageId, update: { currentMessage in
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
var attributes = currentMessage.attributes.filter { !($0 is AudioTranscriptionMessageAttribute) }
attributes.append(updatedAttribute)
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
if updatedAttribute.error == nil {
return .success
} else {
return .error
}
}
}
}
}
func _internal_rateAudioTranscription(postbox: Postbox, network: Network, messageId: MessageId, id: Int64, isGood: Bool) -> Signal<Never, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
transaction.updateMessage(messageId, update: { currentMessage in
var storeForwardInfo: StoreMessageForwardInfo?
if let forwardInfo = currentMessage.forwardInfo {
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
}
var attributes = currentMessage.attributes
for i in 0 ..< attributes.count {
if let attribute = attributes[i] as? AudioTranscriptionMessageAttribute {
attributes[i] = attribute.withDidRate()
}
}
return .update(StoreMessage(
id: currentMessage.id,
globallyUniqueId: currentMessage.globallyUniqueId,
groupingKey: currentMessage.groupingKey,
threadId: currentMessage.threadId,
timestamp: currentMessage.timestamp,
flags: StoreMessageFlags(currentMessage.flags),
tags: currentMessage.tags,
globalTags: currentMessage.globalTags,
localTags: currentMessage.localTags,
forwardInfo: storeForwardInfo,
authorId: currentMessage.author?.id,
text: currentMessage.text,
attributes: attributes,
media: currentMessage.media
))
})
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
guard let inputPeer = inputPeer else {
return .complete()
}
return network.request(Api.functions.messages.rateTranscribedAudio(peer: inputPeer, msgId: messageId.id, transcriptionId: id, good: isGood ? .boolTrue : .boolFalse))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)
}
|> ignoreValues
}
}

View File

@@ -104,17 +104,23 @@ func _internal_recommendedChannels(account: Account, peerId: EnginePeer.Id) -> S
let key = PostboxViewKey.cachedItem(entryId(peerId: peerId))
return account.postbox.combinedView(keys: [key])
|> mapToSignal { views -> Signal<RecommendedChannels?, NoError> in
guard let cachedChannels = (views.views[key] as? CachedItemView)?.value?.get(CachedRecommendedChannels.self) else {
guard let cachedChannels = (views.views[key] as? CachedItemView)?.value?.get(CachedRecommendedChannels.self), !cachedChannels.peerIds.isEmpty else {
return .single(nil)
}
return account.postbox.transaction { transaction -> RecommendedChannels? in
var channels: [RecommendedChannels.Channel] = []
for peerId in cachedChannels.peerIds {
if let peer = transaction.getPeer(peerId), let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
channels.append(RecommendedChannels.Channel(peer: EnginePeer(peer), subscribers: cachedData.participantsSummary.memberCount ?? 0))
return account.postbox.multiplePeersView(cachedChannels.peerIds)
|> mapToSignal { view in
return account.postbox.transaction { transaction -> RecommendedChannels? in
var channels: [RecommendedChannels.Channel] = []
for peerId in cachedChannels.peerIds {
if let peer = view.peers[peerId] as? TelegramChannel, let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
if case .member = peer.participationStatus {
} else {
channels.append(RecommendedChannels.Channel(peer: EnginePeer(peer), subscribers: cachedData.participantsSummary.memberCount ?? 0))
}
}
}
return RecommendedChannels(channels: channels, isHidden: cachedChannels.isHidden)
}
return RecommendedChannels(channels: channels, isHidden: cachedChannels.isHidden)
}
}
}

View File

@@ -186,7 +186,7 @@ func _internal_revertChatWallpaper(account: Account, peerId: EnginePeer.Id) -> S
return account.network.request(Api.functions.messages.setChatWallPaper(flags: flags, peer: inputPeer, wallpaper: nil, settings: nil, id: nil), automaticFloodWait: false)
|> map(Optional.init)
|> `catch` { error -> Signal<Api.Updates?, RevertChatWallpaperError> in
if error.description == "WALLPAPER_NOT_FOUND" {
if error.errorDescription == "WALLPAPER_NOT_FOUND" {
return .single(nil)
}
return .fail(.generic)
@@ -215,7 +215,7 @@ public enum SetExistingChatWallpaperError {
case generic
}
func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId, settings: WallpaperSettings?) -> Signal<Void, SetExistingChatWallpaperError> {
func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId, settings: WallpaperSettings?, forBoth: Bool) -> Signal<Void, SetExistingChatWallpaperError> {
return account.postbox.transaction { transaction -> Peer? in
if let peer = transaction.getPeer(messageId.peerId), let message = transaction.getMessage(messageId) {
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .setChatWallpaper(wallpaper, _) = action.action {
@@ -248,6 +248,9 @@ func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId,
flags |= 1 << 2
inputSettings = apiWallpaperSettings(settings)
}
if forBoth {
flags |= 1 << 3
}
return account.network.request(Api.functions.messages.setChatWallPaper(flags: flags, peer: inputPeer, wallpaper: nil, settings: inputSettings, id: messageId.id), automaticFloodWait: false)
|> `catch` { _ -> Signal<Api.Updates, SetExistingChatWallpaperError> in
return .fail(.generic)

View File

@@ -22,8 +22,8 @@ public extension TelegramEngine {
|> ignoreValues
}
public func setExistingChatWallpaper(messageId: MessageId, settings: WallpaperSettings?) -> Signal<Void, SetExistingChatWallpaperError> {
return _internal_setExistingChatWallpaper(account: self.account, messageId: messageId, settings: settings)
public func setExistingChatWallpaper(messageId: MessageId, settings: WallpaperSettings?, forBoth: Bool) -> Signal<Void, SetExistingChatWallpaperError> {
return _internal_setExistingChatWallpaper(account: self.account, messageId: messageId, settings: settings, forBoth: forBoth)
}
public func revertChatWallpaper(peerId: EnginePeer.Id) -> Signal<Void, RevertChatWallpaperError> {