mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 20:39:24 +00:00
Artificial delay when a message fails to send (#2091)
This commit is contained in:
@@ -24,6 +24,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
@Environment(\.timelineGroupStyle) private var timelineGroupStyle
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
let adjustedDeliveryStatus: TimelineItemDeliveryStatus?
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
@State private var showItemActionMenu = false
|
||||
@@ -62,7 +63,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
if !timelineItem.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
TimelineItemStatusView(timelineItem: timelineItem)
|
||||
TimelineItemStatusView(timelineItem: timelineItem, adjustedDeliveryStatus: adjustedDeliveryStatus)
|
||||
.environmentObject(context)
|
||||
.padding(.top, 8)
|
||||
.padding(.bottom, 3)
|
||||
@@ -162,7 +163,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
|
||||
@ViewBuilder
|
||||
var interactiveLocalizedSendInfo: some View {
|
||||
if timelineItem.hasFailedToSend {
|
||||
if adjustedDeliveryStatus == .sendingFailed {
|
||||
layoutedLocalizedSendInfo
|
||||
.onTapGesture {
|
||||
context.sendFailedConfirmationDialogInfo = .init(itemID: timelineItem.id)
|
||||
@@ -203,12 +204,12 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
Text(timelineItem.timestamp)
|
||||
}
|
||||
|
||||
if timelineItem.hasFailedToSend {
|
||||
if adjustedDeliveryStatus == .sendingFailed {
|
||||
CompoundIcon(\.error, size: .xSmall, relativeTo: .compound.bodyXS)
|
||||
}
|
||||
}
|
||||
.font(.compound.bodyXS)
|
||||
.foregroundColor(timelineItem.hasFailedToSend ? .compound.textCriticalPrimary : .compound.textSecondary)
|
||||
.foregroundColor(adjustedDeliveryStatus == .sendingFailed ? .compound.textCriticalPrimary : .compound.textSecondary)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
@@ -22,6 +22,7 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
||||
@Environment(\.timelineGroupStyle) private var timelineGroupStyle
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
let adjustedDeliveryStatus: TimelineItemDeliveryStatus?
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
@State private var showItemActionMenu = false
|
||||
@@ -36,7 +37,7 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
||||
supplementaryViews
|
||||
}
|
||||
}
|
||||
TimelineItemStatusView(timelineItem: timelineItem)
|
||||
TimelineItemStatusView(timelineItem: timelineItem, adjustedDeliveryStatus: adjustedDeliveryStatus)
|
||||
.environmentObject(context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,46 @@ struct TimelineStyler<Content: View>: View {
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
@State private var adjustedDeliveryStatus: TimelineItemDeliveryStatus?
|
||||
@State private var task: Task<Void, Never>?
|
||||
|
||||
init(timelineItem: EventBasedTimelineItemProtocol, @ViewBuilder content: @escaping () -> Content) {
|
||||
self.timelineItem = timelineItem
|
||||
self.content = content
|
||||
_adjustedDeliveryStatus = State(initialValue: timelineItem.properties.deliveryStatus)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
mainContent
|
||||
.onChange(of: timelineItem.properties.deliveryStatus) { newStatus in
|
||||
if newStatus == .sendingFailed {
|
||||
guard task == nil else {
|
||||
return
|
||||
}
|
||||
task = Task {
|
||||
try? await Task.sleep(for: .milliseconds(700))
|
||||
if !Task.isCancelled {
|
||||
adjustedDeliveryStatus = newStatus
|
||||
}
|
||||
task = nil
|
||||
}
|
||||
} else {
|
||||
task?.cancel()
|
||||
task = nil
|
||||
adjustedDeliveryStatus = newStatus
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: adjustedDeliveryStatus)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var mainContent: some View {
|
||||
switch style {
|
||||
case .plain:
|
||||
TimelineItemPlainStylerView(timelineItem: timelineItem, content: content)
|
||||
TimelineItemPlainStylerView(timelineItem: timelineItem, adjustedDeliveryStatus: adjustedDeliveryStatus, content: content)
|
||||
case .bubbles:
|
||||
TimelineItemBubbledStylerView(timelineItem: timelineItem, content: content)
|
||||
TimelineItemBubbledStylerView(timelineItem: timelineItem, adjustedDeliveryStatus: adjustedDeliveryStatus, content: content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import SwiftUI
|
||||
|
||||
struct TimelineItemStatusView: View {
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
let adjustedDeliveryStatus: TimelineItemDeliveryStatus?
|
||||
@Environment(\.timelineStyle) private var style
|
||||
@Environment(\.readReceiptsEnabled) private var readReceiptsEnabled
|
||||
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||
@@ -36,13 +37,13 @@ struct TimelineItemStatusView: View {
|
||||
if !timelineItem.properties.orderedReadReceipts.isEmpty, readReceiptsEnabled {
|
||||
readReceipts
|
||||
} else {
|
||||
deliveryStatus
|
||||
deliveryStatusBadge
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var deliveryStatus: some View {
|
||||
switch timelineItem.properties.deliveryStatus {
|
||||
var deliveryStatusBadge: some View {
|
||||
switch adjustedDeliveryStatus {
|
||||
case .sending:
|
||||
TimelineDeliveryStatusView(deliveryStatus: .sending)
|
||||
case .sent, .none:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Added a small artificial delay when resending a failed message if it fails instantly, for better UX.
|
||||
@@ -0,0 +1 @@
|
||||
Added a disclaimer on the Mentions Only option for notifications, so that the user will know if it will work properly or not on their home server.
|
||||
Reference in New Issue
Block a user