@@ -27,6 +27,12 @@ public struct PostDisplayView: View {
2727 @State private var displayedUpvoted : Bool
2828 @State private var displayedBookmarked : Bool
2929 @State private var displayedVoteLinks : VoteLinks ?
30+ @State private var pendingVoteIntent : VoteIntent ?
31+
32+ private enum VoteIntent {
33+ case upvote
34+ case unvote
35+ }
3036
3137 public init (
3238 post: Post ,
@@ -127,6 +133,11 @@ public struct PostDisplayView: View {
127133 displayedUpvoted = newValue
128134 }
129135 }
136+ . onChange ( of: votingState? . canUnvote) { _ in
137+ if let voteLinks = post. voteLinks {
138+ displayedVoteLinks = voteLinks
139+ }
140+ }
130141 }
131142
132143 private var upvotePill : some View {
@@ -221,58 +232,91 @@ public struct PostDisplayView: View {
221232
222233 private func makeUpvoteAction( ) -> ( ( ) -> Void ) ? {
223234 return {
224- guard !isSubmittingUpvote else { return }
225-
226- let isCurrentlyUpvoted = displayedUpvoted
227235 let currentVoteLinks = displayedVoteLinks ?? post. voteLinks
228236 let canUnvote = currentVoteLinks? . unvote != nil
237+ let intent : VoteIntent = ( displayedUpvoted && canUnvote) ? . unvote : . upvote
238+ Task { @MainActor in
239+ handleVoteIntent ( intent)
240+ }
241+ }
242+ }
229243
230- // If already upvoted and can unvote, perform unvote
231- if isCurrentlyUpvoted && canUnvote {
232- guard let onUnvoteTap else { return }
233- isSubmittingUpvote = true
234- let previousScore = displayedScore
235- let previousUpvoted = displayedUpvoted
236- let previousVoteLinks = currentVoteLinks
237- displayedUpvoted = false
238- displayedScore -= 1
239- displayedVoteLinks = VoteLinks ( upvote: previousVoteLinks? . upvote, unvote: nil )
240- Task {
241- let success = await onUnvoteTap ( )
242- await MainActor . run {
243- if !success {
244- displayedScore = previousScore
245- displayedUpvoted = previousUpvoted
246- displayedVoteLinks = previousVoteLinks
247- }
248- isSubmittingUpvote = false
244+ @MainActor
245+ private func handleVoteIntent( _ intent: VoteIntent ) {
246+ if isSubmittingUpvote {
247+ pendingVoteIntent = intent
248+ return
249+ }
250+ executeVoteIntent ( intent)
251+ }
252+
253+ @MainActor
254+ private func executeVoteIntent( _ intent: VoteIntent ) {
255+ pendingVoteIntent = nil
256+
257+ let currentVoteLinks = displayedVoteLinks ?? post. voteLinks
258+ let previousScore = displayedScore
259+ let previousUpvoted = displayedUpvoted
260+ let previousVoteLinks = currentVoteLinks
261+
262+ switch intent {
263+ case . unvote:
264+ guard let onUnvoteTap else { return }
265+ guard displayedUpvoted else { return }
266+
267+ isSubmittingUpvote = true
268+ displayedUpvoted = false
269+ displayedScore -= 1
270+ displayedVoteLinks = VoteLinks ( upvote: previousVoteLinks? . upvote, unvote: nil )
271+
272+ Task {
273+ let success = await onUnvoteTap ( )
274+ await MainActor . run {
275+ if !success {
276+ displayedScore = previousScore
277+ displayedUpvoted = previousUpvoted
278+ displayedVoteLinks = previousVoteLinks
249279 }
280+ isSubmittingUpvote = false
281+ processPendingVoteIntentIfNeeded ( )
250282 }
251- } else {
252- // Perform upvote
253- guard let onUpvoteTap else { return }
254- isSubmittingUpvote = true
255- let previousScore = displayedScore
256- let previousUpvoted = displayedUpvoted
257- let previousVoteLinks = currentVoteLinks
258- displayedUpvoted = true
259- displayedScore += 1
260- displayedVoteLinks = derivedVoteLinks ( afterUpvoteFrom: previousVoteLinks)
261- Task {
262- let success = await onUpvoteTap ( )
263- await MainActor . run {
264- if !success {
265- displayedScore = previousScore
266- displayedUpvoted = previousUpvoted
267- displayedVoteLinks = previousVoteLinks
268- }
269- isSubmittingUpvote = false
283+ }
284+
285+ case . upvote:
286+ guard let onUpvoteTap else { return }
287+ guard !displayedUpvoted else { return }
288+ guard currentVoteLinks? . upvote != nil else { return }
289+
290+ isSubmittingUpvote = true
291+ displayedUpvoted = true
292+ displayedScore += 1
293+ displayedVoteLinks = derivedVoteLinks ( afterUpvoteFrom: previousVoteLinks)
294+
295+ Task {
296+ let success = await onUpvoteTap ( )
297+ await MainActor . run {
298+ if !success {
299+ displayedScore = previousScore
300+ displayedUpvoted = previousUpvoted
301+ displayedVoteLinks = previousVoteLinks
302+ } else {
303+ displayedVoteLinks = derivedVoteLinks ( afterUpvoteFrom: displayedVoteLinks ?? previousVoteLinks)
270304 }
305+ isSubmittingUpvote = false
306+ processPendingVoteIntentIfNeeded ( )
271307 }
272308 }
273309 }
274310 }
275311
312+ @MainActor
313+ private func processPendingVoteIntentIfNeeded( ) {
314+ if let nextIntent = pendingVoteIntent {
315+ pendingVoteIntent = nil
316+ handleVoteIntent ( nextIntent)
317+ }
318+ }
319+
276320 private func derivedVoteLinks( afterUpvoteFrom voteLinks: VoteLinks ? ) -> VoteLinks ? {
277321 guard let voteLinks else { return nil }
278322 if voteLinks. unvote != nil {
0 commit comments