Skip to main content
Logo
Explore APIsContact Us
  • Home
  1. Resources
  2. Virtual Stadium
  3. Profile Component

Profile Component

The Profile component displays user information, statistics, and betting activity within the Virtual Stadium community. It shows a user's shared bets, copy count, and engagement metrics, and is typically accessed through the Chat component's user interactions.

#Overview

The Profile component provides a detailed view of a user's activity in the Virtual Stadium community:

  1. Display the ProfileView when users click on profiles in Chat
  2. Configure with the selected user ID and chat settings
  3. Show user statistics, bet history, and engagement metrics
  4. Handle profile dismissal and navigation
  5. Customize appearance to match your Chat component theme

This guide will walk you through each step with code examples and best practices.

info

Prerequisites

  • The SDK must be initialized with Managers.SDK.shared.initialize()

#Basic Integration

#SwiftUI

The Profile component is independent from the Chat component. It can be displayed by only providing the userId parameter.

Required Parameters:

userId String required

The unique identifier of the user whose profile to display. This is typically provided by the Chat component's onOpenProfile callback.

Optional Parameters:

onCopyBet ((BetPayload) -> Void)? optional

Callback invoked when a user copies a bet from the profile. Returns the BetPayload to add to your bet slip.

onClose (() -> Void)? optional

Callback invoked when the user closes the profile screen. Use this to handle navigation back to the chat.

chatSettings ChatSettings optional

Customization settings for theming, fonts, icons, and translations. Should match the Chat component settings for visual consistency. Default: ChatSettings()

info

The Profile component is designed to overlay or navigate from the Chat component. Use state management to toggle its visibility based on user interactions.

swift
import SwiftUI
import VirtualStadiumSDK

struct ContentView: View {
    let jwt: String
    let channelId: String
    
    @State private var showProfile = false
    @State private var selectedUserId = ""
    
    var body: some View {
        NavigationView {
            ZStack {
                // Chat component
                ChatView(
                    userJWT: jwt,
                    channelId: channelId,
                    supportedLanguage: .english,
                    onCopyToBetslip: { betPayload in
                        // Handle bet copy
                        print("Bet copied: \(betPayload.id)")
                    },
                    onOpenProfile: { userId in
                        selectedUserId = userId
                        showProfile = true
                    },
                    chatSettings: ChatSettings()
                )
                
                // Profile overlay
                if showProfile {
                    ProfileView(
                        userId: selectedUserId,
                        onCopyBet: { betPayload in
                            // Handle bet copy from profile
                            print("Bet copied from profile: \(betPayload.id)")
                        },
                        onClose: {
                            showProfile = false
                            selectedUserId = ""
                        },
                        chatSettings: ChatSettings()
                    )
                    .transition(.move(edge: .trailing))
                }
            }
            .navigationTitle("Chat")
            .animation(.easeInOut, value: showProfile)
        }
    }
}

#Profile Content

The Profile component displays the following information:

#User Header

  • Display Name - The user's name in the community
  • Avatar - User profile picture
  • Close Button - Dismiss the profile view

#Statistics Section

MetricDescription
Bets SharedTotal number of bet slips the user has shared with the community
Bets CopiedNumber of times other users have copied this user's bets

#Bet History

  • Chronological List - All bet slips shared by the user
  • Bet Details - Each bet slip shows:
    • Bet type (Single, Multi, Bet Builder, etc.)
    • Markets and outcomes
    • Odds and potential payout
    • Timestamp
  • Copy Action - Other users can copy bets directly from the profile
  • Load More - Pagination support for extensive bet histories

#Styling and Theming

For a cohesive user experience, use the same ChatSettings for both Chat and Profile components. This ensures consistent colors, fonts, and icons throughout your application.

#Shared Settings Configuration

Create a single ChatSettings instance and reuse it across both components. This maintains visual consistency and simplifies theme management.

Best Practice:

  • Define your settings once
  • Share across Chat and Profile components
  • Update in a single location when styling changes
warning

Using different ChatSettings for Chat and Profile will result in an inconsistent user experience. Always share the same configuration.

swift
import SwiftUI
import VirtualStadiumSDK

struct ChatWithProfileView: View {
    let jwt: String
    let channelId: String
    
    @State private var showProfile = false
    @State private var selectedUserId = ""
    
    // Shared settings for consistency
    private let sharedSettings = ChatSettings(
        generalSettings: GeneralSettings(
            oddsType: .us,
            presentedInBottomSheet: false
        ),
        themeSettings: ThemeSettings(
            mainColorPaletteTheme: MainColorPaletteTheme(
                primaryColor: .blue,
                secondaryColor: .orange
            )
        ),
        fontSettings: FontSettings(
            fontRegular: UIFont(name: "CustomFont-Regular", size: 16)
        )
    )
    
    var body: some View {
        NavigationView {
            ZStack {
                ChatView(
                    userJWT: jwt,
                    channelId: channelId,
                    supportedLanguage: .english,
                    onOpenProfile: { userId in
                        selectedUserId = userId
                        showProfile = true
                    },
                    chatSettings: sharedSettings  // Shared settings
                )
                
                if showProfile {
                    ProfileView(
                        userId: selectedUserId,
                        onCopyBet: { bet in
                            copyBetToSlip(bet)
                        },
                        onClose: {
                            showProfile = false
                        },
                        chatSettings: sharedSettings  // Same settings
                    )
                }
            }
        }
    }
    
    private func copyBetToSlip(_ bet: BetPayload) {
        // Add to bet slip
        print("Copying bet: \(bet.id)")
    }
}

#Advanced Implementation

#Navigation Integration

Integrate with SwiftUI's native navigation for proper back stack management and deep linking support. This approach provides better control over navigation and state preservation.

Benefits:

  • Proper navigation stack management
  • Support for deep linking to profiles
  • Better state preservation
  • System back button handling
  • Compatible with iOS navigation patterns
swift
import SwiftUI
import VirtualStadiumSDK

struct ChatNavigationView: View {
    let jwt: String
    let channelId: String
    
    var body: some View {
        NavigationView {
            ChatView(
                userJWT: jwt,
                channelId: channelId,
                supportedLanguage: .english,
                onOpenProfile: { userId in
                    // Navigation handled by NavigationLink
                },
                chatSettings: ChatSettings()
            )
            .background(
                NavigationLink(
                    destination: ProfileDestination(),
                    isActive: $showProfile
                ) {
                    EmptyView()
                }
                .hidden()
            )
            .navigationTitle("Chat")
        }
    }
}

struct ProfileDestination: View {
    let userId: String
    
    var body: some View {
        ProfileView(
            userId: userId,
            onCopyBet: { bet in
                // Handle bet copy
            },
            onClose: nil,  // Navigation handled by back button
            chatSettings: ChatSettings()
        )
        .navigationBarBackButtonHidden(false)
    }
}

#Sheet Presentation

Display the profile in a modal sheet for a more native iOS experience. This pattern is ideal when you want to maintain visibility of the chat in the background.

Use Cases:

  • Quick profile previews
  • Maintaining chat context
  • iOS-native design patterns
  • Non-intrusive profile viewing
swift
import SwiftUI
import VirtualStadiumSDK

struct ChatWithSheetProfile: View {
    let jwt: String
    let channelId: String
    
    @State private var showProfile = false
    @State private var selectedUserId = ""
    
    var body: some View {
        NavigationView {
            ChatView(
                userJWT: jwt,
                channelId: channelId,
                supportedLanguage: .english,
                onOpenProfile: { userId in
                    selectedUserId = userId
                    showProfile = true
                },
                chatSettings: ChatSettings()
            )
            .navigationTitle("Chat")
            .sheet(isPresented: $showProfile) {
                NavigationView {
                    ProfileView(
                        userId: selectedUserId,
                        onCopyBet: { bet in
                            copyBetToSlip(bet)
                        },
                        onClose: {
                            showProfile = false
                        },
                        chatSettings: ChatSettings()
                    )
                    .navigationTitle("Profile")
                    .navigationBarTitleDisplayMode(.inline)
                }
            }
        }
    }
    
    private func copyBetToSlip(_ bet: BetPayload) {
        print("Copying bet: \(bet.id)")
    }
}

#Full Screen Cover

Use a full screen cover for an immersive profile experience. This is ideal for viewing detailed user statistics and bet history without distractions.

Use Cases:

  • Detailed profile analysis
  • Full-screen bet history browsing
  • Immersive user experience
  • Profile as primary focus
swift
import SwiftUI
import VirtualStadiumSDK

struct ChatWithFullScreenProfile: View {
    let jwt: String
    let channelId: String
    
    @State private var showProfile = false
    @State private var selectedUserId = ""
    
    var body: some View {
        NavigationView {
            ChatView(
                userJWT: jwt,
                channelId: channelId,
                supportedLanguage: .english,
                onOpenProfile: { userId in
                    selectedUserId = userId
                    showProfile = true
                },
                chatSettings: ChatSettings()
            )
            .navigationTitle("Chat")
            .fullScreenCover(isPresented: $showProfile) {
                ProfileView(
                    userId: selectedUserId,
                    onCopyBet: { bet in
                        copyBetToSlip(bet)
                    },
                    onClose: {
                        showProfile = false
                    },
                    chatSettings: ChatSettings()
                )
            }
        }
    }
    
    private func copyBetToSlip(_ bet: BetPayload) {
        print("Copying bet: \(bet.id)")
    }
}

#Complete Example

Here's a full implementation combining all best practices:

Includes:

  • ViewModel for state management
  • Shared settings for consistency
  • Animated transitions
  • User ID validation
  • Proper callbacks for bet copying

This example demonstrates production-ready code with proper architecture and error handling.

swift
import SwiftUI
import VirtualStadiumSDK
import Combine

class ChatProfileViewModel: ObservableObject {
    @Published var showProfile = false
    @Published var selectedUserId = ""
    @Published var showError = false
    @Published var errorMessage = ""
    
    func openProfile(userId: String) {
        guard !userId.isEmpty else {
            errorMessage = "Invalid user ID"
            showError = true
            return
        }
        selectedUserId = userId
        showProfile = true
    }
    
    func closeProfile() {
        showProfile = false
        selectedUserId = ""
    }
    
    func handleBetCopy(_ bet: BetPayload) {
        // Add to bet slip
        print("Bet copied: \(bet.id)")
        // Show confirmation if needed
    }
}

struct CompleteChatProfileView: View {
    let jwt: String
    let channelId: String
    
    @StateObject private var viewModel = ChatProfileViewModel()
    
    // Shared settings for consistency
    private let sharedSettings: ChatSettings = {
        let customTheme = ThemeSettings(
            mainColorPaletteTheme: MainColorPaletteTheme(
                primaryColor: .blue,
                secondaryColor: .orange,
                tertiaryColor: .green
            ),
            chatViewTheme: ChatViewTheme(
                scrollToBottomBackgroundColor: Color.blue.opacity(0.15),
                loadingIndicatorColor: .blue
            )
        )
        
        let customFont = FontSettings(
            fontRegular: UIFont(name: "CustomFont-Regular", size: 16),
            fontMedium: UIFont(name: "CustomFont-Medium", size: 16),
            fontBold: UIFont(name: "CustomFont-Bold", size: 16)
        )
        
        return ChatSettings(
            generalSettings: GeneralSettings(
                oddsType: .us,
                presentedInBottomSheet: false,
                flashBetTimeoutDuration: 60.0,
                useTestInsights: false
            ),
            themeSettings: customTheme,
            fontSettings: customFont
        )
    }()
    
    var body: some View {
        NavigationView {
            ZStack {
                // Main chat component
                ChatView(
                    userJWT: jwt,
                    channelId: channelId,
                    supportedLanguage: .english,
                    onBetShareHandler: { completion in
                        Task {
                            let betSlips = await getBetSlips()
                            completion(betSlips)
                        }
                    },
                    onCopyToBetslip: { bet in
                        viewModel.handleBetCopy(bet)
                    },
                    onTagSelected: { tag in
                        handleTagSelection(tag)
                    },
                    onReachSupportTeam: { reason in
                        openSupport(reason: reason)
                    },
                    onOpenProfile: { userId in
                        viewModel.openProfile(userId: userId)
                    },
                    analyticsProvider: AnalyticsProvider(
                        onEventReceived: { eventType in
                            print("Event: \(eventType)")
                        },
                        onBetEventReceived: { eventType, betPayload, builderId in
                            print("Bet event: \(eventType), Bet: \(betPayload.id)")
                        }
                    ),
                    onError: { error in
                        viewModel.errorMessage = error.localizedDescription
                        viewModel.showError = true
                    },
                    chatSettings: sharedSettings
                )
                .navigationTitle("Chat")
                
                // Animated profile overlay
                if viewModel.showProfile {
                    ProfileView(
                        userId: viewModel.selectedUserId,
                        onCopyBet: { bet in
                            viewModel.handleBetCopy(bet)
                        },
                        onClose: {
                            viewModel.closeProfile()
                        },
                        chatSettings: sharedSettings
                    )
                    .transition(.move(edge: .trailing))
                    .zIndex(1)
                }
            }
            .animation(.easeInOut(duration: 0.3), value: viewModel.showProfile)
        }
        .alert("Error", isPresented: $viewModel.showError) {
            Button("OK", role: .cancel) { }
        } message: {
            Text(viewModel.errorMessage)
        }
    }
    
    // Helper methods
    private func getBetSlips() async -> [BetPayload] {
        /**
         * TODO(developer): Fetch bet slips from your backend
         */
        // Example: return await apiClient.fetchUserBetSlips()
        return []
    }
    
    private func handleTagSelection(_ tag: Tag) {
        /**
         * TODO(developer): Handle tag selection
         */
        print("Tag selected: \(tag.name)")
    }
    
    private func openSupport(reason: InfractionReason?) {
        /**
         * TODO(developer): Open support interface
         */
        print("Opening support")
    }
}

// Preview
struct CompleteChatProfileView_Previews: PreviewProvider {
    static var previews: some View {
        CompleteChatProfileView(
            jwt: "test-jwt",
            channelId: "test-channel"
        )
    }
}
Last updated about 1 month ago
Is this site helpful?
Virtual Stadium, Moderation, Engagement Tools
Chat ComponentCentral Hub Component
On this page
  • Overview
  • Basic Integration
  • SwiftUI
  • Profile Content
  • User Header
  • Statistics Section
  • Bet History
  • Styling and Theming
  • Shared Settings Configuration
  • Advanced Implementation
  • Navigation Integration
  • Sheet Presentation
  • Full Screen Cover
  • Complete Example