Files
mobile-ios/Rosetta/Features/Settings/ProfileEditView.swift

198 lines
6.2 KiB
Swift

import PhotosUI
import SwiftUI
/// Embedded profile editing content (no NavigationStack lives inside SettingsView's).
/// Matches Telegram's edit screen: avatar + photo picker, name fields,
/// helper texts, "Add Another Account", and "Log Out".
struct ProfileEditView: View {
@Binding var displayName: String
@Binding var username: String
let publicKey: String
var onLogout: () -> Void
@State private var selectedPhotoItem: PhotosPickerItem?
@State private var selectedPhoto: UIImage?
private var initials: String {
RosettaColors.initials(name: displayName, publicKey: publicKey)
}
private var avatarColorIndex: Int {
RosettaColors.avatarColorIndex(for: displayName, publicKey: publicKey)
}
var body: some View {
VStack(spacing: 0) {
avatarSection
.padding(.bottom, 24)
nameSection
helperText("Enter your name and add an optional profile photo.")
.padding(.top, 8)
.padding(.bottom, 24)
addAccountSection
helperText("You can connect multiple accounts with different phone numbers.")
.padding(.top, 8)
.padding(.bottom, 24)
logoutSection
}
.padding(.horizontal, 16)
.padding(.top, 24)
.padding(.bottom, 100)
}
}
// MARK: - Avatar Section
private extension ProfileEditView {
var avatarSection: some View {
VStack(spacing: 12) {
if let selectedPhoto {
Image(uiImage: selectedPhoto)
.resizable()
.scaledToFill()
.frame(width: 80, height: 80)
.clipShape(Circle())
} else {
AvatarView(
initials: initials,
colorIndex: avatarColorIndex,
size: 80,
isSavedMessages: false
)
}
PhotosPicker(selection: $selectedPhotoItem, matching: .images) {
Text("Set New Photo")
.font(.system(size: 15, weight: .medium))
.foregroundStyle(RosettaColors.primaryBlue)
}
.buttonStyle(.plain)
.onChange(of: selectedPhotoItem) { _, item in
Task {
if let data = try? await item?.loadTransferable(type: Data.self),
let image = UIImage(data: data) {
selectedPhoto = image
}
}
}
}
}
}
// MARK: - Name Section
private extension ProfileEditView {
var nameSection: some View {
GlassCard(cornerRadius: 26, fillOpacity: 0.08) {
VStack(spacing: 0) {
HStack {
TextField("First Name", text: $displayName)
.font(.system(size: 17))
.foregroundStyle(RosettaColors.Adaptive.text)
.autocorrectionDisabled()
.textInputAutocapitalization(.words)
if !displayName.isEmpty {
Button { displayName = "" } label: {
Image(systemName: "xmark.circle.fill")
.font(.system(size: 18))
.foregroundStyle(RosettaColors.tertiaryText)
}
.buttonStyle(.plain)
}
}
.padding(.horizontal, 16)
.frame(height: 52)
Divider()
.background(RosettaColors.Adaptive.divider)
.padding(.leading, 16)
HStack {
TextField("Username", text: $username)
.font(.system(size: 17))
.foregroundStyle(RosettaColors.Adaptive.text)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
if !username.isEmpty {
Button { username = "" } label: {
Image(systemName: "xmark.circle.fill")
.font(.system(size: 18))
.foregroundStyle(RosettaColors.tertiaryText)
}
.buttonStyle(.plain)
}
}
.padding(.horizontal, 16)
.frame(height: 52)
}
}
}
}
// MARK: - Add Account & Logout
private extension ProfileEditView {
var addAccountSection: some View {
GlassCard(cornerRadius: 26, fillOpacity: 0.08) {
Button {} label: {
HStack {
Spacer()
Text("Add Another Account")
.font(.system(size: 17))
.foregroundStyle(RosettaColors.primaryBlue)
Spacer()
}
.frame(height: 52)
}
.buttonStyle(.plain)
}
}
var logoutSection: some View {
GlassCard(cornerRadius: 26, fillOpacity: 0.08) {
Button(action: onLogout) {
HStack {
Spacer()
Text("Log Out")
.font(.system(size: 17))
.foregroundStyle(RosettaColors.error)
Spacer()
}
.frame(height: 52)
}
}
}
func helperText(_ text: String) -> some View {
Text(text)
.font(.system(size: 13))
.foregroundStyle(RosettaColors.secondaryText)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 16)
}
}
// MARK: - Preview
#Preview {
NavigationStack {
ScrollView {
ProfileEditView(
displayName: .constant("Gaidar"),
username: .constant("GaidarTheDev"),
publicKey: "028d1c9d0000000000000000000000000000000000000000000000000000008e03ec",
onLogout: {}
)
}
.background(RosettaColors.Adaptive.background)
}
.preferredColorScheme(.dark)
}