127 lines
4.4 KiB
Swift
127 lines
4.4 KiB
Swift
|
|
import SwiftUI
|
||
|
|
|
||
|
|
/// Shown at **reveal time** when a connection becomes mutual.
|
||
|
|
///
|
||
|
|
/// This is the only moment a social identity can be attached. The user can
|
||
|
|
/// choose to enrich their revealed profile with an Instagram or X handle —
|
||
|
|
/// or decline and stay minimal. Importing is always optional and always
|
||
|
|
/// scoped to this specific connection.
|
||
|
|
struct ProfileImportSheet: View {
|
||
|
|
@EnvironmentObject private var profileService: ProfileImportService
|
||
|
|
@Environment(\.dismiss) private var dismiss
|
||
|
|
|
||
|
|
/// The connection that just became mutual.
|
||
|
|
let connection: Connection
|
||
|
|
|
||
|
|
@State private var imported: [ImportedProfile] = []
|
||
|
|
@State private var isImporting = false
|
||
|
|
@State private var errorMessage: String?
|
||
|
|
|
||
|
|
var body: some View {
|
||
|
|
NavigationStack {
|
||
|
|
VStack(spacing: 24) {
|
||
|
|
// Header
|
||
|
|
VStack(spacing: 8) {
|
||
|
|
Image(systemName: "person.crop.circle.badge.checkmark")
|
||
|
|
.font(.system(size: 48))
|
||
|
|
.foregroundColor(.green)
|
||
|
|
Text("You're connected with \(connection.displayName)")
|
||
|
|
.font(.headline)
|
||
|
|
.multilineTextAlignment(.center)
|
||
|
|
Text("Want to share a bit more about who you are?")
|
||
|
|
.font(.subheadline)
|
||
|
|
.foregroundColor(.secondary)
|
||
|
|
}
|
||
|
|
.padding(.top, 24)
|
||
|
|
|
||
|
|
// Import buttons
|
||
|
|
VStack(spacing: 12) {
|
||
|
|
ForEach(ProfileProviderID.allCases) { provider in
|
||
|
|
importButton(for: provider)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.padding(.horizontal)
|
||
|
|
|
||
|
|
// Already imported
|
||
|
|
if !imported.isEmpty {
|
||
|
|
importedList
|
||
|
|
}
|
||
|
|
|
||
|
|
Spacer()
|
||
|
|
|
||
|
|
// Done
|
||
|
|
Button("Done") { dismiss() }
|
||
|
|
.buttonStyle(.borderedProminent)
|
||
|
|
.padding(.bottom, 24)
|
||
|
|
}
|
||
|
|
.navigationTitle("Share your profile")
|
||
|
|
.navigationBarTitleDisplayMode(.inline)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private func importButton(for provider: ProfileProviderID) -> some View {
|
||
|
|
Button {
|
||
|
|
Task { await importProfile(provider) }
|
||
|
|
} label: {
|
||
|
|
HStack {
|
||
|
|
Image(systemName: provider == .instagram ? "camera" : "xmark.square")
|
||
|
|
Text("Import from \(provider.displayName)")
|
||
|
|
Spacer()
|
||
|
|
if isImporting {
|
||
|
|
ProgressView()
|
||
|
|
} else {
|
||
|
|
Image(systemName: "chevron.right")
|
||
|
|
.font(.caption)
|
||
|
|
.foregroundColor(.secondary)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.padding()
|
||
|
|
.background(Color(.secondarySystemBackground))
|
||
|
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||
|
|
}
|
||
|
|
.buttonStyle(.plain)
|
||
|
|
.disabled(isImporting)
|
||
|
|
}
|
||
|
|
|
||
|
|
private var importedList: some View {
|
||
|
|
VStack(alignment: .leading, spacing: 8) {
|
||
|
|
Text("Imported")
|
||
|
|
.font(.subheadline)
|
||
|
|
.foregroundColor(.secondary)
|
||
|
|
ForEach(imported) { profile in
|
||
|
|
HStack {
|
||
|
|
Text("@\(profile.handle)")
|
||
|
|
.font(.headline)
|
||
|
|
Spacer()
|
||
|
|
Text(profile.providerID.displayName)
|
||
|
|
.font(.caption)
|
||
|
|
.foregroundColor(.secondary)
|
||
|
|
Button {
|
||
|
|
profileService.remove(profile)
|
||
|
|
imported.removeAll { $0.id == profile.id }
|
||
|
|
} label: {
|
||
|
|
Image(systemName: "xmark.circle")
|
||
|
|
.foregroundColor(.red)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.padding()
|
||
|
|
.background(Color(.secondarySystemBackground))
|
||
|
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.padding(.horizontal)
|
||
|
|
}
|
||
|
|
|
||
|
|
private func importProfile(_ provider: ProfileProviderID) async {
|
||
|
|
isImporting = true
|
||
|
|
defer { isImporting = false }
|
||
|
|
do {
|
||
|
|
let profile = try await profileService.importProfile(from: provider)
|
||
|
|
profileService.attach(profile, to: connection.id)
|
||
|
|
imported.append(profile)
|
||
|
|
} catch {
|
||
|
|
errorMessage = error.localizedDescription
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|