Signal-iOS/Signal/src/ViewControllers/HomeView/Stories/IncomingStoryViewModel.swift

98 lines
3.9 KiB
Swift

//
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalServiceKit
struct IncomingStoryViewModel: Dependencies {
let identifier: UUID
let records: [StoryMessageRecord]
let recordIds: [Int64]
let hasUnviewedRecords: Bool
enum Attachment {
case file(TSAttachment)
case text(TextAttachment)
case missing
}
let latestRecordAttachment: Attachment
let latestRecordHasReplies: Bool
let latestRecordName: String
let latestRecordTimestamp: UInt64
let latestRecordAvatarDataSource: ConversationAvatarDataSource
init(records: [StoryMessageRecord], identifier: UUID = UUID(), transaction: SDSAnyReadTransaction) throws {
self.identifier = identifier
let sortedFilteredRecords = records.lazy.filter { $0.direction == .incoming }.sorted { $0.timestamp > $1.timestamp }
self.records = sortedFilteredRecords
self.recordIds = sortedFilteredRecords.compactMap { $0.id }
self.hasUnviewedRecords = sortedFilteredRecords.reduce(false, { partialResult, record in
switch record.manifest {
case .incoming(_, let viewed):
return partialResult || !viewed
case .outgoing:
owsFailDebug("Unexpected record type")
return partialResult
}
})
guard let latestRecord = sortedFilteredRecords.first else {
throw OWSAssertionError("At least one record is required.")
}
if let groupId = latestRecord.groupId {
guard let groupThread = TSGroupThread.fetch(groupId: groupId, transaction: transaction) else {
throw OWSAssertionError("Missing group thread for group story")
}
let authorShortName = Self.contactsManager.shortDisplayName(
for: latestRecord.authorAddress,
transaction: transaction
)
let nameFormat = NSLocalizedString(
"GROUP_STORY_NAME_FORMAT",
comment: "Name for a group story on the stories list. Embeds {author's name}, {group name}")
latestRecordName = String(format: nameFormat, authorShortName, groupThread.groupNameOrDefault)
latestRecordAvatarDataSource = .thread(groupThread)
} else {
latestRecordName = Self.contactsManager.displayName(
for: latestRecord.authorAddress,
transaction: transaction
)
latestRecordAvatarDataSource = .address(latestRecord.authorAddress)
}
switch latestRecord.attachment {
case .file(let attachmentId):
guard let attachment = TSAttachment.anyFetch(uniqueId: attachmentId, transaction: transaction) else {
owsFailDebug("Unexpectedly missing attachment for story")
latestRecordAttachment = .missing
break
}
latestRecordAttachment = .file(attachment)
case .text(let attachment):
latestRecordAttachment = .text(attachment)
}
latestRecordHasReplies = false // TODO: replies
latestRecordTimestamp = latestRecord.timestamp
}
func copy(updatedRecords: [StoryMessageRecord], deletedRecordIds: [Int64], transaction: SDSAnyReadTransaction) throws -> Self? {
var updatedRecords = updatedRecords
let records = self.records.lazy
.filter { !deletedRecordIds.contains($0.id ?? 0) }
.map { oldRecord in
if let idx = updatedRecords.firstIndex(where: { $0.id == oldRecord.id }) {
return updatedRecords.remove(at: idx)
} else {
return oldRecord
}
} + updatedRecords
guard !records.isEmpty else { return nil }
return try .init(records: records, identifier: identifier, transaction: transaction)
}
}