commit 99380cca5615528148ccdf247fe98c84efd95ffa
parent c9beaa643ce5ca3374387e3775b15d444fd3f156
Author: Michael Camilleri <[email protected]>
Date: Tue, 19 May 2026 10:38:25 +0900
Use invitation animations in short list as well
Diffstat:
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/Crossmate/Views/GameShareItem.swift b/Crossmate/Views/GameShareItem.swift
@@ -162,27 +162,17 @@ struct GameShareSheet: View {
@ViewBuilder
private func friendInviteButton(for friend: FriendEntity) -> some View {
let authorID = friend.authorID ?? ""
- let isInviting = invitingAuthorID == authorID
let wasInvited = invitedAuthorIDs.contains(authorID)
Button {
Task { await invite(authorID) }
} label: {
VStack(spacing: 8) {
- ZStack {
- FriendAvatarView(authorID: authorID, size: 40)
- if isInviting {
- ProgressView()
- .controlSize(.small)
- .background(.background, in: Circle())
- } else if wasInvited {
- Image(systemName: "checkmark.circle.fill")
- .font(.caption)
- .foregroundStyle(.secondary)
- .background(.background, in: Circle())
- .offset(x: 12, y: -10)
- }
- }
+ FriendAvatarView(
+ authorID: authorID,
+ size: 40,
+ invitePhase: invitePhase(authorID: authorID, invited: wasInvited)
+ )
Text(displayName(for: friend))
.font(.callout.weight(.medium))
.lineLimit(1)
@@ -193,6 +183,15 @@ struct GameShareSheet: View {
.disabled(authorID.isEmpty || invitingAuthorID != nil || wasInvited)
}
+ /// Maps the row's invite state to the avatar's animation phase. Sending
+ /// is checked first so the glyph keeps wiggling right up until the work
+ /// finishes, then resolves straight into the spin-to-checkmark.
+ private func invitePhase(authorID: String, invited: Bool) -> FriendAvatarView.InvitePhase? {
+ if invitingAuthorID == authorID { return .sending }
+ if invited { return .sent }
+ return nil
+ }
+
private func displayName(for friend: FriendEntity) -> String {
if let name = friend.displayName?.trimmingCharacters(in: .whitespacesAndNewlines),
!name.isEmpty {
@@ -203,13 +202,13 @@ struct GameShareSheet: View {
private func invite(_ authorID: String) async {
guard !authorID.isEmpty, let inviteFriend else { return }
- invitingAuthorID = authorID
+ withAnimation(.snappy) { invitingAuthorID = authorID }
errorMessage = nil
- defer { invitingAuthorID = nil }
+ defer { withAnimation(.snappy) { invitingAuthorID = nil } }
do {
try await inviteFriend(gameID, authorID)
- invitedAuthorIDs.insert(authorID)
+ withAnimation(.snappy) { invitedAuthorIDs.insert(authorID) }
} catch {
errorMessage = describe(error)
}