Skip to content

Commit

Permalink
Add report flag button + fix collapse on comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Itaybre committed Feb 5, 2025
1 parent 7df8ff5 commit d102175
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 23 deletions.
53 changes: 31 additions & 22 deletions ios/HackerNews/Comments/CommentRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ 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
@State private var isPressed = false
@State private var showingMoreOptions = false

var body: some View {
VStack(alignment: .leading, spacing: 0) {
Expand Down Expand Up @@ -53,12 +55,34 @@ struct CommentRow: View {
.background(state.upvoted ? .green.opacity(0.2) : .white.opacity(0.2))
.foregroundStyle(state.upvoted ? .green : .onBackground)
.clipShape(Capsule())

Button(action: {
showingMoreOptions = true
}) {
Image(systemName: "ellipsis")
.font(.system(size: 12))
.padding(.horizontal, 8)
.padding(.vertical, 4)
}
.background(.white.opacity(0.2))
.foregroundStyle(.onBackground)
.clipShape(Capsule())
.confirmationDialog("Comment Options", isPresented: $showingMoreOptions, titleVisibility: .visible) {
Button("Report Comment") {
flagComment(state)
}
Button(state.hidden ? "Show" : "Collapse") {
toggleComment()
}
}
}
}
.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 {
Expand Down Expand Up @@ -90,33 +114,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())
Expand All @@ -131,6 +135,7 @@ struct CommentViewIndentation_Preview: PreviewProvider {
CommentRow(
state: PreviewHelpers.makeFakeComment(level: index),
likeComment: { _ in },
flagComment: { _ in },
toggleComment: {}
)
.environment(Theme())
Expand All @@ -150,6 +155,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())
Expand All @@ -162,6 +168,7 @@ What do you mean that it's not truly cross-platform?<p><a href="https://develope
CommentRow(
state: PreviewHelpers.makeFakeComment(level: 0, text: text),
likeComment: { _ in },
flagComment: { _ in },
toggleComment: {}
)
.environment(Theme())
Expand All @@ -175,6 +182,7 @@ It's much much more complicated than that. Sun refused to add many language feat
CommentRow(
state: PreviewHelpers.makeFakeComment(level: 0, text: text),
likeComment: { _ in },
flagComment: { _ in },
toggleComment: {}
)
.environment(Theme())
Expand All @@ -187,6 +195,7 @@ Hudson River Trading | Hybrid | Full-time\n<p>We’re a quantitative trading fir
CommentRow(
state: PreviewHelpers.makeFakeComment(level: 0, text: text),
likeComment: { _ in },
flagComment: { _ in },
toggleComment: {}
)
.environment(Theme())
Expand Down
21 changes: 21 additions & 0 deletions ios/HackerNews/Comments/CommentsHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import SwiftUI
struct CommentsHeader: View {
let state: CommentsHeaderState
let likePost: () -> Void
let flagPost: () -> Void
let toggleBody: () -> Void
let onTitleTap: () -> Void

@State private var showingMoreOptions = false

var body: some View {
VStack(alignment: .leading) {
Expand Down Expand Up @@ -59,6 +62,23 @@ struct CommentsHeader: View {
.background(state.upvoted ? .green.opacity(0.2) : .surface)
.foregroundStyle(state.upvoted ? .green : .onBackground)
.clipShape(Capsule())

Button(action: {
showingMoreOptions = true
}) {
Image(systemName: "ellipsis")
.font(.system(size: 12))
.padding(.horizontal, 8)
.padding(.vertical, 4)
}
.background(.white.opacity(0.2))
.foregroundStyle(.onBackground)
.clipShape(Capsule())
.confirmationDialog("Post Options", isPresented: $showingMoreOptions, titleVisibility: .visible) {
Button("Report Post") {
flagPost()
}
}
}
// body
if let text = state.story.text {
Expand All @@ -85,6 +105,7 @@ struct CommentsHeader: View {
CommentsHeader(
state: CommentsHeaderState(story: PreviewHelpers.makeFakeStory()),
likePost: {},
flagPost: {},
toggleBody: {},
onTitleTap: {}
)
Expand Down
12 changes: 12 additions & 0 deletions ios/HackerNews/Comments/CommentsScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ struct CommentsScreen: View {
)
}
},
flagPost: {
Task {
await model.flagPost(
url: model.state.headerState.flagUrl
)
}
},
toggleBody: {
model.toggleHeaderBody()
},
Expand Down Expand Up @@ -64,6 +71,11 @@ struct CommentsScreen: View {
await model.likeComment(data: state)
}
},
flagComment: { state in
Task {
await model.flagComment(data: state)
}
},
toggleComment: {
model.toggleComment(commentId: commentState.id)
}
Expand Down
28 changes: 28 additions & 0 deletions ios/HackerNews/Comments/CommentsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ struct CommentState {
let id: Int64
var upvoted: Bool
let upvoteUrl: String
let hideUrl: String
let flagUrl: String
let text: String
let user: String
let age: String
Expand All @@ -39,6 +41,8 @@ extension CommentInfo {
id: self.id,
upvoted: self.upvoted,
upvoteUrl: self.upvoteUrl,
hideUrl: self.hideUrl,
flagUrl: self.flagUrl,
text: self.text,
user: self.user,
age: self.age,
Expand All @@ -53,6 +57,8 @@ struct CommentsHeaderState {
var expanded: Bool = false
var upvoteLink: String = ""
var upvoted: Bool = false
var hideUrl: String = ""
var flagUrl: String = ""
}

struct CommentComposerState {
Expand Down Expand Up @@ -119,6 +125,8 @@ class CommentsViewModel {
case .success(let data):
state.headerState.upvoted = data.postInfo.upvoted
state.headerState.upvoteLink = data.postInfo.upvoteUrl
state.headerState.hideUrl = data.postInfo.hideUrl
state.headerState.flagUrl = data.postInfo.flagUrl
state.comments = .loaded(comments: data.comments.map { $0.toCommentState() })
state.postCommentState = data.commentForm?.toCommentComposerState(auth: state.auth)
case .error:
Expand Down Expand Up @@ -162,6 +170,26 @@ class CommentsViewModel {
navigation(.login)
}
}

func flagPost(url: String) async {
switch state.auth {
case .loggedIn:
guard !url.isEmpty else { return }
await webClient.flagItem(flagUrl: url)
case .loggedOut:
navigation(.login)
}
}

func flagComment(data: CommentState) async {
switch state.auth {
case .loggedIn:
guard !data.flagUrl.isEmpty else { return }
await webClient.flagItem(flagUrl: data.flagUrl)
case .loggedOut:
navigation(.login)
}
}

func toggleHeaderBody() {
state.headerState.expanded.toggle()
Expand Down
32 changes: 31 additions & 1 deletion ios/HackerNews/Network/HNWebClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ struct CommentInfo {
let id: Int64
var upvoted: Bool
let upvoteUrl: String
let hideUrl: String
let flagUrl: String
let text: String
let user: String
let age: String
Expand All @@ -38,6 +40,8 @@ struct PostInfo {
let id: Int64
let upvoted: Bool
let upvoteUrl: String
let hideUrl: String
let flagUrl: String
}

struct CommentFormData {
Expand Down Expand Up @@ -106,6 +110,23 @@ class HNWebClient {
@discardableResult
func upvoteItem(upvoteUrl: String) async -> 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
Expand Down Expand Up @@ -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 : ""
)
}

Expand All @@ -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() ?? "",
Expand Down
2 changes: 2 additions & 0 deletions ios/HackerNews/Utils/Previews.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit d102175

Please sign in to comment.