From e8adec624423b5cac33d2af1823422733d992b43 Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Thu, 6 Feb 2025 12:18:02 -0300 Subject: [PATCH] Add report flag button + fix collapse on comments (#356) --- ios/HackerNews/Comments/CommentRow.swift | 51 +++++++++++-------- ios/HackerNews/Comments/CommentsHeader.swift | 17 +++++++ ios/HackerNews/Comments/CommentsScreen.swift | 12 +++++ .../Comments/CommentsViewModel.swift | 28 ++++++++++ ios/HackerNews/Network/HNWebClient.swift | 32 +++++++++++- ios/HackerNews/Utils/Previews.swift | 2 + 6 files changed, 119 insertions(+), 23 deletions(-) diff --git a/ios/HackerNews/Comments/CommentRow.swift b/ios/HackerNews/Comments/CommentRow.swift index 73f58835..bf7ba310 100644 --- a/ios/HackerNews/Comments/CommentRow.swift +++ b/ios/HackerNews/Comments/CommentRow.swift @@ -14,6 +14,7 @@ private let maxIndentationLevel: Int = 5 struct CommentRow: View { let state: CommentState let likeComment: (CommentState) -> Void + let flagComment: (CommentState) -> Void let toggleComment: () -> Void @Environment(Theme.self) private var theme @@ -50,15 +51,36 @@ struct CommentRow: View { .padding(.horizontal, 8) .padding(.vertical, 4) } + .frame(height: 20) .background(state.upvoted ? .green.opacity(0.2) : .white.opacity(0.2)) .foregroundStyle(state.upvoted ? .green : .onBackground) .clipShape(Capsule()) + + Menu { + Button("Report Comment") { + flagComment(state) + } + Button(state.hidden ? "Show" : "Collapse") { + toggleComment() + } + } label: { + Image(systemName: "ellipsis") + .font(.system(size: 12)) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .frame(height: 20) + .background(.white.opacity(0.2)) + .foregroundStyle(.onBackground) + .clipShape(Capsule()) + } } } .padding(8) .background(isPressed ? .surface.opacity(0.85) : .surface) .zIndex(1) // Ensure header stays on top - .simultaneousGesture(makeCommentGesture()) + .onTapGesture(count: 1) { + toggleComment() + } // Comment Body if !state.hidden { @@ -90,33 +112,13 @@ struct CommentRow: View { } } -extension CommentRow { - fileprivate func makeCommentGesture() -> some Gesture { - DragGesture(minimumDistance: 0) - .onChanged { value in - // Only show press effect if we haven't moved far - if abs(value.translation.height) < 2 && abs(value.translation.width) < 2 { - isPressed = true - } else { - isPressed = false - } - } - .onEnded { value in - isPressed = false - // Trigger tap if it was a small movement (effectively a tap) - if abs(value.translation.height) < 2 && abs(value.translation.width) < 2 { - toggleComment() - } - } - } -} - struct CommentView_Preview: PreviewProvider { static var previews: some View { PreviewVariants { CommentRow( state: PreviewHelpers.makeFakeComment(), likeComment: { _ in }, + flagComment: { _ in }, toggleComment: {} ) .environment(Theme()) @@ -131,6 +133,7 @@ struct CommentViewIndentation_Preview: PreviewProvider { CommentRow( state: PreviewHelpers.makeFakeComment(level: index), likeComment: { _ in }, + flagComment: { _ in }, toggleComment: {} ) .environment(Theme()) @@ -150,6 +153,7 @@ C# is a hugely underrated language that I feel like often gets overlooked when t CommentRow( state: PreviewHelpers.makeFakeComment(level: 0, text: text), likeComment: { _ in }, + flagComment: { _ in }, toggleComment: {} ) .environment(Theme()) @@ -162,6 +166,7 @@ What do you mean that it's not truly cross-platform?

Bool { let url = URL(string: upvoteUrl)! + return await actionOnItem(url: url) + } + + @discardableResult + func hideItem(hideUrl: String) async -> Bool { + let url = URL(string: hideUrl)! + return await actionOnItem(url: url) + } + + @discardableResult + func flagItem(flagUrl: String) async -> Bool { + let url = URL(string: flagUrl)! + return await actionOnItem(url: url) + } + + @discardableResult + private func actionOnItem(url: URL) async -> Bool { do { let (_, response) = try await session.data(from: url) let httpResponse = response as! HTTPURLResponse @@ -151,11 +172,15 @@ extension Document { let postUpvoteLinkElement = try self.select("#up_\(id)").first() let upvoteUrl = try postUpvoteLinkElement?.attr("href") ?? "" let upvoted = postUpvoteLinkElement?.hasClass("nosee") ?? false + let hideLink = !upvoteUrl.isEmpty ? upvoteUrl.replacingOccurrences(of: "vote", with: "hide") : "" + let flagUrl = !upvoteUrl.isEmpty ? upvoteUrl.replacingOccurrences(of: "vote", with: "flag") : "" return PostInfo( id: id, upvoted: upvoted, - upvoteUrl: !upvoteUrl.isEmpty ? BASE_WEB_URL + upvoteUrl : "" + upvoteUrl: !upvoteUrl.isEmpty ? BASE_WEB_URL + upvoteUrl : "", + hideUrl: !hideLink.isEmpty ? BASE_WEB_URL + hideLink : "", + flagUrl: !flagUrl.isEmpty ? BASE_WEB_URL + flagUrl : "" ) } @@ -173,11 +198,16 @@ extension Document { let upvoteUrl = try upvoteLinkElement?.attr("href") ?? "" let upvoted = upvoteLinkElement?.hasClass("nosee") ?? false let date = String(commentDate).asDate() + + let hideLink = !upvoteUrl.isEmpty ? upvoteUrl.replacingOccurrences(of: "vote", with: "hide") : "" + let flagUrl = !upvoteUrl.isEmpty ? upvoteUrl.replacingOccurrences(of: "vote", with: "flag") : "" return CommentInfo( id: commentId, upvoted: upvoted, upvoteUrl: !upvoteUrl.isEmpty ? BASE_WEB_URL + upvoteUrl : "", + hideUrl: !hideLink.isEmpty ? BASE_WEB_URL + hideLink : "", + flagUrl: !flagUrl.isEmpty ? BASE_WEB_URL + flagUrl : "", text: commentText, user: commentAuthor, age: date?.timeAgoDisplay() ?? "", diff --git a/ios/HackerNews/Utils/Previews.swift b/ios/HackerNews/Utils/Previews.swift index 5fca0cfa..45de93fd 100644 --- a/ios/HackerNews/Utils/Previews.swift +++ b/ios/HackerNews/Utils/Previews.swift @@ -127,6 +127,8 @@ struct PreviewHelpers { id: 1, upvoted: false, upvoteUrl: "", + hideUrl: "", + flagUrl: "", text: text ?? """ Totally useless commentary: It makes me deeply happy to hear success stories like this for a project that's moving in the correctly opposite direction to that of the rest of the world.