Files
2025-05-28 09:55:51 +08:00

184 lines
4.2 KiB
Swift

//
// ProfileView.swift
// UserManagement
//
// Created by Guilherme Souza on 17/11/23.
//
import PhotosUI
import Supabase
import SwiftUI
struct ProfileView: View {
@State var username = ""
@State var fullName = ""
@State var website = ""
@State var isLoading = false
@State var imageSelection: PhotosPickerItem?
@State var avatarImage: AvatarImage?
var body: some View {
NavigationStack {
Form {
Section {
HStack {
Group {
if let avatarImage {
avatarImage.image.resizable()
} else {
Color.clear
}
}
.scaledToFit()
.frame(width: 80, height: 80)
Spacer()
PhotosPicker(selection: $imageSelection, matching: .images) {
Image(systemName: "pencil.circle.fill")
.symbolRenderingMode(.multicolor)
.font(.system(size: 30))
.foregroundColor(.accentColor)
}
}
}
Section {
TextField("Username", text: $username)
.textContentType(.username)
#if os(iOS)
.textInputAutocapitalization(.never)
#endif
TextField("Full name", text: $fullName)
.textContentType(.name)
TextField("Website", text: $website)
.textContentType(.URL)
#if os(iOS)
.textInputAutocapitalization(.never)
#endif
}
Section {
Button("Update profile") {
updateProfileButtonTapped()
}
.bold()
if isLoading {
ProgressView()
}
}
}
.onMac { $0.padding() }
.navigationTitle("Profile")
.toolbar(content: {
ToolbarItem {
Button("Sign out", role: .destructive) {
Task {
try? await supabase.auth.signOut()
}
}
}
})
.onChange(of: imageSelection) { _, newValue in
guard let newValue else { return }
loadTransferable(from: newValue)
}
}
.task {
await getInitialProfile()
}
}
func getInitialProfile() async {
do {
let currentUser = try await supabase.auth.session.user
let profile: Profile = try await supabase.database
.from("profiles")
.select()
.eq("id", value: currentUser.id)
.single()
.execute()
.value
username = profile.username ?? ""
fullName = profile.fullName ?? ""
website = profile.website ?? ""
if let avatarURL = profile.avatarURL, !avatarURL.isEmpty {
try await downloadImage(path: avatarURL)
}
} catch {
debugPrint(error)
}
}
func updateProfileButtonTapped() {
Task {
isLoading = true
defer { isLoading = false }
do {
let imageURL = try await uploadImage()
let currentUser = try await supabase.auth.session.user
let updatedProfile = Profile(
username: username,
fullName: fullName,
website: website,
avatarURL: imageURL
)
try await supabase.database
.from("profiles")
.update(updatedProfile)
.eq("id", value: currentUser.id)
.execute()
} catch {
debugPrint(error)
}
}
}
private func loadTransferable(from imageSelection: PhotosPickerItem) {
Task {
do {
avatarImage = try await imageSelection.loadTransferable(type: AvatarImage.self)
} catch {
debugPrint(error)
}
}
}
private func downloadImage(path: String) async throws {
let data = try await supabase.storage.from("avatars").download(path: path)
avatarImage = AvatarImage(data: data)
}
private func uploadImage() async throws -> String? {
guard let data = avatarImage?.data else { return nil }
let filePath = "\(UUID().uuidString).jpeg"
try await supabase.storage
.from("avatars")
.upload(
path: filePath,
file: data,
options: FileOptions(contentType: "image/jpeg")
)
return filePath
}
}
#if swift(>=5.9)
#Preview {
ProfileView()
}
#endif