From 2e5a0fca1a52217e307b81e7740536d3d19c9ffd Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 12 Jul 2023 19:25:03 +0400 Subject: [PATCH 1/3] ios: add quoted message to chat item info (#2680) --- .../Shared/Views/Chat/ChatItemInfoView.swift | 180 ++++++++++++++---- apps/ios/SimpleXChat/ChatTypes.swift | 6 +- 2 files changed, 146 insertions(+), 40 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift b/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift index d6d33c4a1..4db32bc74 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift @@ -13,7 +13,13 @@ struct ChatItemInfoView: View { @Environment(\.colorScheme) var colorScheme var ci: ChatItem @Binding var chatItemInfo: ChatItemInfo? + @State private var selection: CIInfoTab = .history @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false + + enum CIInfoTab { + case history + case quote + } var body: some View { NavigationView { @@ -35,43 +41,66 @@ struct ChatItemInfoView: View { } @ViewBuilder private func itemInfoView() -> some View { + if let qi = ci.quotedItem { + TabView(selection: $selection) { + historyTab() + .tabItem { + Label("History", systemImage: "clock") + } + .tag(CIInfoTab.history) + quoteTab(qi) + .tabItem { + Label("In reply to", systemImage: "arrowshape.turn.up.left") + } + .tag(CIInfoTab.quote) + } + } else { + historyTab() + } + } + + @ViewBuilder private func details() -> some View { let meta = ci.meta + VStack(alignment: .leading, spacing: 16) { + Text(title) + .font(.largeTitle) + .bold() + .padding(.bottom) + + infoRow("Sent at", localTimestamp(meta.itemTs)) + if !ci.chatDir.sent { + infoRow("Received at", localTimestamp(meta.createdAt)) + } + switch (meta.itemDeleted) { + case let .deleted(deletedTs): + if let deletedTs = deletedTs { + infoRow("Deleted at", localTimestamp(deletedTs)) + } + case let .moderated(deletedTs, _): + if let deletedTs = deletedTs { + infoRow("Moderated at", localTimestamp(deletedTs)) + } + default: EmptyView() + } + if let deleteAt = meta.itemTimed?.deleteAt { + infoRow("Disappears at", localTimestamp(deleteAt)) + } + if developerTools { + infoRow("Database ID", "\(meta.itemId)") + infoRow("Record updated at", localTimestamp(meta.updatedAt)) + } + } + } + + @ViewBuilder private func historyTab() -> some View { GeometryReader { g in + let maxWidth = (g.size.width - 32) * 0.84 ScrollView { VStack(alignment: .leading, spacing: 16) { - Text(title) - .font(.largeTitle) - .bold() - .padding(.bottom) - - let maxWidth = (g.size.width - 32) * 0.84 - infoRow("Sent at", localTimestamp(meta.itemTs)) - if !ci.chatDir.sent { - infoRow("Received at", localTimestamp(meta.createdAt)) - } - switch (meta.itemDeleted) { - case let .deleted(deletedTs): - if let deletedTs = deletedTs { - infoRow("Deleted at", localTimestamp(deletedTs)) - } - case let .moderated(deletedTs, _): - if let deletedTs = deletedTs { - infoRow("Moderated at", localTimestamp(deletedTs)) - } - default: EmptyView() - } - if let deleteAt = meta.itemTimed?.deleteAt { - infoRow("Disappears at", localTimestamp(deleteAt)) - } - if developerTools { - infoRow("Database ID", "\(meta.itemId)") - infoRow("Record updated at", localTimestamp(meta.updatedAt)) - } - + details() + Divider().padding(.vertical) if let chatItemInfo = chatItemInfo, !chatItemInfo.itemVersions.isEmpty { - Divider().padding(.vertical) - Text("History") .font(.title2) .padding(.bottom, 4) @@ -81,16 +110,21 @@ struct ChatItemInfoView: View { } } } + else { + Text("No history") + .foregroundColor(.secondary) + .frame(maxWidth: .infinity) + } } + .padding() } - .padding() .frame(maxHeight: .infinity, alignment: .top) } } - + @ViewBuilder private func itemVersionView(_ itemVersion: ChatItemVersion, _ maxWidth: CGFloat, current: Bool) -> some View { VStack(alignment: .leading, spacing: 4) { - versionText(itemVersion) + textBubble(itemVersion.msgContent.text, itemVersion.formattedText, nil) .allowsHitTesting(false) .padding(.horizontal, 12) .padding(.vertical, 6) @@ -119,9 +153,9 @@ struct ChatItemInfoView: View { .frame(maxWidth: maxWidth, alignment: .leading) } - @ViewBuilder private func versionText(_ itemVersion: ChatItemVersion) -> some View { - if itemVersion.msgContent.text != "" { - messageText(itemVersion.msgContent.text, itemVersion.formattedText, nil) + @ViewBuilder private func textBubble(_ text: String, _ formattedText: [FormattedText]?, _ sender: String? = nil) -> some View { + if text != "" { + messageText(text, formattedText, sender) } else { Text("no text") .italic() @@ -129,6 +163,60 @@ struct ChatItemInfoView: View { } } + @ViewBuilder private func quoteTab(_ qi: CIQuote) -> some View { + GeometryReader { g in + let maxWidth = (g.size.width - 32) * 0.84 + ScrollView { + VStack(alignment: .leading, spacing: 16) { + details() + Divider().padding(.vertical) + Text("In reply to") + .font(.title2) + .padding(.bottom, 4) + quotedMsgView(qi, maxWidth) + } + .padding() + } + .frame(maxHeight: .infinity, alignment: .top) + } + } + + @ViewBuilder private func quotedMsgView(_ qi: CIQuote, _ maxWidth: CGFloat) -> some View { + VStack(alignment: .leading, spacing: 4) { + textBubble(qi.text, qi.formattedText, qi.getSender(nil)) + .allowsHitTesting(false) + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(quotedMsgFrameColor(qi, colorScheme)) + .cornerRadius(18) + .contextMenu { + if qi.text != "" { + Button { + showShareSheet(items: [qi.text]) + } label: { + Label("Share", systemImage: "square.and.arrow.up") + } + Button { + UIPasteboard.general.string = qi.text + } label: { + Label("Copy", systemImage: "doc.on.doc") + } + } + } + Text(localTimestamp(qi.sentAt)) + .foregroundStyle(.secondary) + .font(.caption) + .padding(.horizontal, 12) + } + .frame(maxWidth: maxWidth, alignment: .leading) + } + + func quotedMsgFrameColor(_ qi: CIQuote, _ colorScheme: ColorScheme) -> Color { + (qi.chatDir?.sent ?? false) + ? (colorScheme == .light ? sentColorLight : sentColorDark) + : Color(uiColor: .tertiarySystemGroupedBackground) + } + private func itemInfoShareText() -> String { let meta = ci.meta var shareText: [String] = [title, ""] @@ -156,6 +244,24 @@ struct ChatItemInfoView: View { String.localizedStringWithFormat(NSLocalizedString("Record updated at: %@", comment: "copied message info"), localTimestamp(meta.updatedAt)) ] } + if let qi = ci.quotedItem { + shareText += ["", NSLocalizedString("In reply to", comment: "copied message info")] + let t = qi.text + shareText += [""] + if let sender = qi.getSender(nil) { + shareText += [String.localizedStringWithFormat( + NSLocalizedString("%@ at %@:", comment: "copied message info, at