Skip to content

Commit 990616e

Browse files
committed
Preserve synthesized unvote state in UI
1 parent 567f97b commit 990616e

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

DesignSystem/Sources/DesignSystem/Components/PostDisplayView.swift

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public struct PostDisplayView: View {
2626
@State private var displayedScore: Int
2727
@State private var displayedUpvoted: Bool
2828
@State private var displayedBookmarked: Bool
29+
@State private var displayedVoteLinks: VoteLinks?
2930

3031
public init(
3132
post: Post,
@@ -50,6 +51,7 @@ public struct PostDisplayView: View {
5051
_displayedScore = State(initialValue: post.score)
5152
_displayedUpvoted = State(initialValue: post.upvoted)
5253
_displayedBookmarked = State(initialValue: post.isBookmarked)
54+
_displayedVoteLinks = State(initialValue: post.voteLinks)
5355
}
5456

5557
public var body: some View {
@@ -101,6 +103,7 @@ public struct PostDisplayView: View {
101103
displayedScore = post.score
102104
displayedUpvoted = post.upvoted
103105
displayedBookmarked = post.isBookmarked
106+
displayedVoteLinks = post.voteLinks
104107
}
105108
.onChange(of: post.score) { newValue in
106109
displayedScore = newValue
@@ -111,6 +114,9 @@ public struct PostDisplayView: View {
111114
.onChange(of: post.isBookmarked) { newValue in
112115
displayedBookmarked = newValue
113116
}
117+
.onChange(of: post.voteLinks) { newValue in
118+
displayedVoteLinks = newValue
119+
}
114120
.onChange(of: votingState?.score) { newValue in
115121
if let newValue {
116122
displayedScore = newValue
@@ -127,8 +133,9 @@ public struct PostDisplayView: View {
127133
let score = displayedScore
128134
let isUpvoted = displayedUpvoted
129135
let isLoading = isSubmittingUpvote
130-
let canVote = post.voteLinks?.upvote != nil
131-
let canUnvote = post.voteLinks?.unvote != nil
136+
let currentVoteLinks = displayedVoteLinks ?? post.voteLinks
137+
let canVote = currentVoteLinks?.upvote != nil
138+
let canUnvote = currentVoteLinks?.unvote != nil
132139
let canInteract = ((canVote && !isUpvoted) || (canUnvote && isUpvoted)) && !isLoading
133140
// Avoid keeping a disabled Button so the upvoted state retains the bright tint
134141
let (backgroundColor, textColor): (Color, Color) = {
@@ -217,22 +224,26 @@ public struct PostDisplayView: View {
217224
guard !isSubmittingUpvote else { return }
218225

219226
let isCurrentlyUpvoted = displayedUpvoted
220-
let canUnvote = post.voteLinks?.unvote != nil
227+
let currentVoteLinks = displayedVoteLinks ?? post.voteLinks
228+
let canUnvote = currentVoteLinks?.unvote != nil
221229

222230
// If already upvoted and can unvote, perform unvote
223231
if isCurrentlyUpvoted && canUnvote {
224232
guard let onUnvoteTap else { return }
225233
isSubmittingUpvote = true
226234
let previousScore = displayedScore
227235
let previousUpvoted = displayedUpvoted
236+
let previousVoteLinks = currentVoteLinks
228237
displayedUpvoted = false
229238
displayedScore -= 1
239+
displayedVoteLinks = VoteLinks(upvote: previousVoteLinks?.upvote, unvote: nil)
230240
Task {
231241
let success = await onUnvoteTap()
232242
await MainActor.run {
233243
if !success {
234244
displayedScore = previousScore
235245
displayedUpvoted = previousUpvoted
246+
displayedVoteLinks = previousVoteLinks
236247
}
237248
isSubmittingUpvote = false
238249
}
@@ -243,14 +254,17 @@ public struct PostDisplayView: View {
243254
isSubmittingUpvote = true
244255
let previousScore = displayedScore
245256
let previousUpvoted = displayedUpvoted
257+
let previousVoteLinks = currentVoteLinks
246258
displayedUpvoted = true
247259
displayedScore += 1
260+
displayedVoteLinks = derivedVoteLinks(afterUpvoteFrom: previousVoteLinks)
248261
Task {
249262
let success = await onUpvoteTap()
250263
await MainActor.run {
251264
if !success {
252265
displayedScore = previousScore
253266
displayedUpvoted = previousUpvoted
267+
displayedVoteLinks = previousVoteLinks
254268
}
255269
isSubmittingUpvote = false
256270
}
@@ -259,6 +273,28 @@ public struct PostDisplayView: View {
259273
}
260274
}
261275

276+
private func derivedVoteLinks(afterUpvoteFrom voteLinks: VoteLinks?) -> VoteLinks? {
277+
guard let voteLinks else { return nil }
278+
if voteLinks.unvote != nil {
279+
return voteLinks
280+
}
281+
guard let upvoteURL = voteLinks.upvote else {
282+
return voteLinks
283+
}
284+
let absolute = upvoteURL.absoluteString
285+
if absolute.contains("how=up"),
286+
let unvoteURL = URL(string: absolute.replacingOccurrences(of: "how=up", with: "how=un"))
287+
{
288+
return VoteLinks(upvote: upvoteURL, unvote: unvoteURL)
289+
}
290+
if absolute.contains("how%3Dup"),
291+
let unvoteURL = URL(string: absolute.replacingOccurrences(of: "how%3Dup", with: "how%3Dun"))
292+
{
293+
return VoteLinks(upvote: upvoteURL, unvote: unvoteURL)
294+
}
295+
return voteLinks
296+
}
297+
262298
private func makeBookmarkAction() -> (() -> Void)? {
263299
guard let onBookmarkTap else { return nil }
264300
return {

Features/Comments/Sources/Comments/CommentsComponents.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ struct CommentsContentView: View {
6262
await votingViewModel.unvote(post: &mutablePost)
6363
await MainActor.run {
6464
if !mutablePost.upvoted {
65+
if let existingLinks = mutablePost.voteLinks {
66+
mutablePost.voteLinks = VoteLinks(upvote: existingLinks.upvote, unvote: nil)
67+
}
6568
viewModel.post = mutablePost
6669
}
6770
}
@@ -276,6 +279,9 @@ struct PostHeader: View {
276279
let wasUnvoted = !mutablePost.upvoted
277280

278281
if wasUnvoted {
282+
if let existingLinks = mutablePost.voteLinks {
283+
mutablePost.voteLinks = VoteLinks(upvote: existingLinks.upvote, unvote: nil)
284+
}
279285
await MainActor.run {
280286
onPostUpdated(mutablePost)
281287
}

Features/Feed/Sources/Feed/FeedView.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ public struct FeedView<NavigationStore: NavigationStoreProtocol>: View {
220220
await votingViewModel.unvote(post: &mutablePost)
221221
await MainActor.run {
222222
if !mutablePost.upvoted {
223+
if let existingLinks = mutablePost.voteLinks {
224+
mutablePost.voteLinks = VoteLinks(upvote: existingLinks.upvote, unvote: nil)
225+
}
223226
viewModel.replacePost(mutablePost)
224227
}
225228
}
@@ -270,6 +273,9 @@ public struct FeedView<NavigationStore: NavigationStoreProtocol>: View {
270273
await votingViewModel.unvote(post: &mutablePost)
271274
await MainActor.run {
272275
if !mutablePost.upvoted {
276+
if let existingLinks = mutablePost.voteLinks {
277+
mutablePost.voteLinks = VoteLinks(upvote: existingLinks.upvote, unvote: nil)
278+
}
273279
viewModel.replacePost(mutablePost)
274280
}
275281
}
@@ -446,6 +452,9 @@ struct PostRowView: View {
446452
let wasUnvoted = !mutablePost.upvoted
447453

448454
if wasUnvoted {
455+
if let existingLinks = mutablePost.voteLinks {
456+
mutablePost.voteLinks = VoteLinks(upvote: existingLinks.upvote, unvote: nil)
457+
}
449458
await MainActor.run {
450459
onPostUpdated?(mutablePost)
451460
}

0 commit comments

Comments
 (0)