diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift index ce54fdd99..f292d49dd 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift @@ -24,6 +24,7 @@ struct TimelineItemBubbledStylerView: 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: 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: 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: 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 diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift index a5f701edc..300ef1422 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift @@ -22,6 +22,7 @@ struct TimelineItemPlainStylerView: 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: View { supplementaryViews } } - TimelineItemStatusView(timelineItem: timelineItem) + TimelineItemStatusView(timelineItem: timelineItem, adjustedDeliveryStatus: adjustedDeliveryStatus) .environmentObject(context) } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineStyler.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineStyler.swift index f399c614f..116eb0c45 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineStyler.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineStyler.swift @@ -24,13 +24,46 @@ struct TimelineStyler: View { let timelineItem: EventBasedTimelineItemProtocol @ViewBuilder let content: () -> Content + + @State private var adjustedDeliveryStatus: TimelineItemDeliveryStatus? + @State private var task: Task? + + 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) } } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift index d826eee80..c58febb39 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift @@ -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: diff --git a/changelog.d/1352.bugfix b/changelog.d/1352.bugfix new file mode 100644 index 000000000..33fdc4977 --- /dev/null +++ b/changelog.d/1352.bugfix @@ -0,0 +1 @@ +Added a small artificial delay when resending a failed message if it fails instantly, for better UX. \ No newline at end of file diff --git a/changelog.d/2078.feature b/changelog.d/2078.feature new file mode 100644 index 000000000..9a0bea26b --- /dev/null +++ b/changelog.d/2078.feature @@ -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. \ No newline at end of file