diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index d604d5af90..45b8760e97 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -719,39 +719,21 @@ struct ChatView: View { struct TimeSeparation { let isTimestampShown: Bool - let isDateShown: Bool // TODO: Implement UI to show date let isGapLarge: Bool init(for chatItem: ChatItem, at index: Int?) { let im = ItemsModel.shared if let index, index > 0 && !im.reversedChatItems.isEmpty { let nextItem = im.reversedChatItems[index - 1] - isTimestampShown = nextItem.chatDir != chatItem.chatDir || - Self.componentsToMinute(nextItem.meta.createdAt) != Self.componentsToMinute(chatItem.meta.createdAt) - isDateShown = - Self.componentsToDate(nextItem.meta.createdAt) != Self.componentsToDate(chatItem.meta.createdAt) isGapLarge = nextItem.chatDir != chatItem.chatDir || nextItem.meta.createdAt.timeIntervalSince(chatItem.meta.createdAt) > 30 + isTimestampShown = isGapLarge || + formatTimestampText(chatItem.meta.createdAt) != formatTimestampText(nextItem.meta.createdAt) } else { isTimestampShown = true - isDateShown = false isGapLarge = true } } - - private static func componentsToMinute(_ date: Date) -> DateComponents { - Calendar.current.dateComponents( - [.year, .month, .day, .hour, .minute], - from: date - ) - } - - private static func componentsToDate(_ date: Date) -> DateComponents { - Calendar.current.dateComponents( - [.year, .month, .day], - from: date - ) - } } var body: some View { @@ -934,7 +916,7 @@ struct ChatView: View { allowMenu: $allowMenu ) .environment(\.showTimestamp, timeSeparation.isTimestampShown) - .modifier(ChatItemClipped(ci, showsTail: timeSeparation.isGapLarge)) + .modifier(ChatItemClipped(ci)) .contextMenu { menu(ci, range, live: composeState.liveMessage != nil) } .accessibilityLabel("") if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 { diff --git a/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift index a74e0bd200..0bde81e228 100644 --- a/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift +++ b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift @@ -9,65 +9,54 @@ import SwiftUI import SimpleXChat -/// Modifier, which provides clipping mask for ``ChatItemWithMenu`` view +/// Modifier, which provides clipping mask for ``ChatItemWithMenu`` view /// and it's previews: (drag interaction, context menu, etc.) /// Supports [Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically) /// by retaining pill shape, even when ``ChatItem``'s height is less that twice its corner radius struct ChatItemClipped: ViewModifier { - - enum TailEdge { - case leading - case trailing - } - struct ClipShape: Shape { let maxCornerRadius: Double - var tailEdge: TailEdge? func path(in rect: CGRect) -> Path { - .roundedRectangle( - in: rect, - radius: maxCornerRadius, - bottomLeading: tailEdge == .leading ? 0 : nil, - bottomTrailing: tailEdge == .trailing ? 0 : nil + Path( + roundedRect: rect, + cornerRadius: min((rect.height / 2), maxCornerRadius), + style: .circular ) } } - + init() { clipShape = ClipShape( maxCornerRadius: 18 ) } - - init(_ chatItem: ChatItem, showsTail: Bool) { + + init(_ chatItem: ChatItem) { clipShape = ClipShape( maxCornerRadius: { switch chatItem.content { - case - .sndMsgContent, + case + .sndMsgContent, .rcvMsgContent, .rcvDecryptionError, .rcvGroupInvitation, .sndGroupInvitation, - .sndDeleted, + .sndDeleted, .rcvDeleted, .rcvIntegrityError, - .sndModerated, - .rcvModerated, + .sndModerated, + .rcvModerated, .rcvBlocked, .invalidJSON: 18 default: 8 } - }(), - tailEdge: showsTail - ? chatItem.chatDir.sent ? .trailing : .leading - : nil + }() ) } - + private let clipShape: ClipShape - + func body(content: Content) -> some View { content .contentShape(.dragPreview, clipShape) @@ -75,50 +64,3 @@ struct ChatItemClipped: ViewModifier { .clipShape(clipShape) } } - -extension Path { - static func roundedRectangle( - in rect: CGRect, - radius: CGFloat, - topLeading: CGFloat? = nil, - bottomLeading: CGFloat? = nil, - bottomTrailing: CGFloat? = nil, - topTrailing: CGFloat? = nil - ) -> Path { - let maxRadius = min(rect.width, rect.height) / 2 - let tl = min(maxRadius, topLeading ?? radius) - let bl = min(maxRadius, bottomLeading ?? radius) - let bt = min(maxRadius, bottomTrailing ?? radius) - let tt = min(maxRadius, topLeading ?? radius) - var path = Path() - path.addArc( - center: CGPoint(x: tl, y: tl), - radius: tl, - startAngle: .degrees(270), - endAngle: .degrees(180), - clockwise: true - ) - path.addArc( - center: CGPoint(x: bl, y: rect.height - bl), - radius: bl, - startAngle: .degrees(180), - endAngle: .degrees(90), - clockwise: true - ) - path.addArc( - center: CGPoint(x: rect.width - bt, y: rect.height - bt), - radius: bt, - startAngle: .degrees(90), - endAngle: .degrees(0), - clockwise: true - ) - path.addArc( - center: CGPoint(x: rect.width - tt, y: tt), - radius: tt, - startAngle: .degrees(0), - endAngle: .degrees(270), - clockwise: true - ) - return path - } -} diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 1a9cf4a216..9895c06233 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2762,7 +2762,7 @@ let msgTimeFormat = Date.FormatStyle.dateTime.hour().minute() let msgDateFormat = Date.FormatStyle.dateTime.day(.twoDigits).month(.twoDigits) public func formatTimestampText(_ date: Date) -> Text { - return Text(date, format: recent(date) ? msgTimeFormat : msgDateFormat) + Text(verbatim: date.formatted(recent(date) ? msgTimeFormat : msgDateFormat)) } private func recent(_ date: Date) -> Bool {