diff --git a/Signal/src/ViewControllers/HomeView/Stories/MyStoryCell.swift b/Signal/src/ViewControllers/HomeView/Stories/MyStoryCell.swift
index dacb8a8b73..82389b7e86 100644
--- a/Signal/src/ViewControllers/HomeView/Stories/MyStoryCell.swift
+++ b/Signal/src/ViewControllers/HomeView/Stories/MyStoryCell.swift
@@ -12,10 +12,12 @@ class MyStoryCell: UITableViewCell {
let titleLabel = UILabel()
let titleChevron = UIImageView()
- let timestampLabel = UILabel()
+ let subtitleLabel = UILabel()
let avatarView = ConversationAvatarView(sizeClass: .fiftySix, localUserDisplayMode: .asUser, useAutolayout: true)
let attachmentThumbnail = UIView()
+ let failedIconView = UIImageView()
+
let addStoryButton = OWSButton()
let contentHStackView = UIStackView()
@@ -36,12 +38,21 @@ class MyStoryCell: UITableViewCell {
titleChevron.image = chevronImage.withRenderingMode(.alwaysTemplate)
- let hStack = UIStackView(arrangedSubviews: [titleLabel, titleChevron])
- hStack.axis = .horizontal
- hStack.alignment = .center
- hStack.spacing = 6
+ let titleStack = UIStackView(arrangedSubviews: [titleLabel, titleChevron])
+ titleStack.axis = .horizontal
+ titleStack.alignment = .center
+ titleStack.spacing = 6
- let vStack = UIStackView(arrangedSubviews: [hStack, timestampLabel])
+ failedIconView.autoSetDimension(.width, toSize: 16)
+ failedIconView.contentMode = .scaleAspectFit
+ failedIconView.tintColor = .ows_accentRed
+
+ let subtitleStack = UIStackView(arrangedSubviews: [failedIconView, subtitleLabel])
+ subtitleStack.axis = .horizontal
+ subtitleStack.alignment = .center
+ subtitleStack.spacing = 6
+
+ let vStack = UIStackView(arrangedSubviews: [titleStack, subtitleStack])
vStack.axis = .vertical
vStack.alignment = .leading
@@ -66,7 +77,7 @@ class MyStoryCell: UITableViewCell {
}
func configure(with model: MyStoryViewModel, addStoryAction: @escaping () -> Void) {
- configureTimestamp(with: model)
+ configureSubtitle(with: model)
titleLabel.font = .ows_dynamicTypeHeadline
titleLabel.textColor = Theme.primaryTextColor
@@ -114,13 +125,24 @@ class MyStoryCell: UITableViewCell {
}
}
- func configureTimestamp(with model: MyStoryViewModel) {
- timestampLabel.font = .ows_dynamicTypeSubheadline
- timestampLabel.textColor = Theme.secondaryTextAndIconColor
- if let latestMessageTimestamp = model.latestMessageTimestamp {
- timestampLabel.text = DateUtil.formatTimestampRelatively(latestMessageTimestamp)
+ func configureSubtitle(with model: MyStoryViewModel) {
+ subtitleLabel.font = .ows_dynamicTypeSubheadline
+ subtitleLabel.textColor = Theme.secondaryTextAndIconColor
+ failedIconView.image = Theme.iconImage(.error16)
+
+ if model.sendingCount > 0 {
+ let format = NSLocalizedString("STORY_SENDING_%d", tableName: "PluralAware", comment: "Indicates that N stories are currently sending")
+ subtitleLabel.text = .localizedStringWithFormat(format, model.sendingCount)
+ failedIconView.isHiddenInStackView = !model.hasFailedSends
+ } else if model.hasFailedSends {
+ failedIconView.isHiddenInStackView = false
+ subtitleLabel.text = NSLocalizedString("STORY_SEND_FAILED", comment: "Text indicating that the story send has failed")
+ } else if let latestMessageTimestamp = model.latestMessageTimestamp {
+ subtitleLabel.text = DateUtil.formatTimestampRelatively(latestMessageTimestamp)
+ failedIconView.isHiddenInStackView = true
} else {
- timestampLabel.text = nil
+ subtitleLabel.text = nil
+ failedIconView.isHiddenInStackView = true
}
}
}
diff --git a/Signal/src/ViewControllers/HomeView/Stories/MyStoryViewModel.swift b/Signal/src/ViewControllers/HomeView/Stories/MyStoryViewModel.swift
index 0985cacc7c..ae7ba5bb14 100644
--- a/Signal/src/ViewControllers/HomeView/Stories/MyStoryViewModel.swift
+++ b/Signal/src/ViewControllers/HomeView/Stories/MyStoryViewModel.swift
@@ -10,12 +10,19 @@ struct MyStoryViewModel: Dependencies {
let latestMessageAttachment: StoryThumbnailView.Attachment?
let latestMessageTimestamp: UInt64?
+ let sendingCount: UInt64
+ let hasFailedSends: Bool
let secondLatestMessageAttachment: StoryThumbnailView.Attachment?
init(messages: [StoryMessage], transaction: SDSAnyReadTransaction) {
- let sortedFilteredMessages = messages.sorted { $0.timestamp < $1.timestamp }
- self.messages = sortedFilteredMessages
+ sendingCount = messages.reduce(0) {
+ $0 + ([.sending, .pending].contains($1.sendingState) ? 1 : 0)
+ }
+ hasFailedSends = messages.contains { $0.sendingState == .failed }
+
+ let sortedFilteredMessages = messages.sorted { $0.timestamp < $1.timestamp }.prefix(2)
+ self.messages = Array(sortedFilteredMessages)
if let latestMessage = sortedFilteredMessages.last {
latestMessageAttachment = .from(latestMessage.attachment, transaction: transaction)
diff --git a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift
index bf16d3d46c..1b6b07523b 100644
--- a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift
+++ b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift
@@ -76,7 +76,7 @@ class StoriesViewController: OWSViewController, StoryListDataSourceDelegate {
case .myStory:
guard let cell = self.tableView.cellForRow(at: indexPath) as? MyStoryCell else { continue }
guard let model = self.dataSource.myStory else { continue }
- cell.configureTimestamp(with: model)
+ cell.configureSubtitle(with: model)
case .visibleStories, .hiddenStories:
guard let cell = self.tableView.cellForRow(at: indexPath) as? StoryCell else { continue }
guard let model = self.model(for: indexPath) else { continue }
diff --git a/Signal/src/ViewControllers/HomeView/Stories/StoryListDataSource.swift b/Signal/src/ViewControllers/HomeView/Stories/StoryListDataSource.swift
index 770b299d59..0ac0d00c9b 100644
--- a/Signal/src/ViewControllers/HomeView/Stories/StoryListDataSource.swift
+++ b/Signal/src/ViewControllers/HomeView/Stories/StoryListDataSource.swift
@@ -74,7 +74,7 @@ class StoryListDataSource: NSObject, Dependencies {
let (listStories, outgoingStories) = Self.databaseStorage.read {
(
StoryFinder.storiesForListView(transaction: $0),
- StoryFinder.outgoingStories(limit: 2, transaction: $0)
+ StoryFinder.outgoingStories(transaction: $0)
)
}
let myStoryModel = Self.databaseStorage.read { MyStoryViewModel(messages: outgoingStories, transaction: $0) }
@@ -429,7 +429,7 @@ class StoryListDataSource: NSObject, Dependencies {
) -> MyStoryViewModel? {
let oldStoryModel = oldModel.myStory
let outgoingStories = Self.databaseStorage.read {
- StoryFinder.outgoingStories(limit: 2, transaction: $0)
+ StoryFinder.outgoingStories(transaction: $0)
}
let myStoryChanged = changedMessageRowIds.intersection(outgoingStories.map { $0.id! }).count > 0
|| Set(oldStoryModel?.messages.map { $0.uniqueId } ?? []) != Set(outgoingStories.map { $0.uniqueId })
@@ -458,7 +458,7 @@ class StoryListDataSource: NSObject, Dependencies {
// My story should never go away after being loaded, but for the sake of completeness...
tableView.deleteRows(at: [IndexPath(row: 0, section: Section.myStory.rawValue)], with: .automatic)
} else if changes.myStoryChanged {
- tableView.reloadRows(at: [IndexPath(row: 0, section: Section.myStory.rawValue)], with: .automatic)
+ tableView.reloadRows(at: [IndexPath(row: 0, section: Section.myStory.rawValue)], with: .none)
}
// Visible stories section is always visible, directly apply changes.
diff --git a/Signal/translations/en.lproj/PluralAware.stringsdict b/Signal/translations/en.lproj/PluralAware.stringsdict
index 96f184a77e..d00c3d6eae 100644
--- a/Signal/translations/en.lproj/PluralAware.stringsdict
+++ b/Signal/translations/en.lproj/PluralAware.stringsdict
@@ -1149,6 +1149,22 @@
%d Excluded
+ STORY_SENDING_%d
+
+ NSStringLocalizedFormatKey
+ %#@caption@
+ caption
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ d
+ one
+ Sending...
+ other
+ Sending %d...
+
+
STORY_VIEWS_COUNT_%d
NSStringLocalizedFormatKey