Compare commits

...

3 Commits

Author SHA1 Message Date
fa10e389cd fix edit profile 2025-12-13 05:33:30 +03:00
c7f51b30cd image edit fix in profile 2025-12-13 05:31:10 +03:00
fc3a534496 fix contact add and contact edit 2025-12-13 05:26:32 +03:00
4 changed files with 79 additions and 12 deletions

View File

@ -3,4 +3,22 @@
uuid = "AEE1609A-17B4-4FCC-80A6-0D556940F4D7"
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "09699199-8124-4F89-892D-6880A0EB7C04"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "yobble/Views/Contacts/ContactEditView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "74"
endingLineNumber = "74"
landmarkName = "ContactEditView"
landmarkType = "14">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View File

@ -97,8 +97,8 @@ struct ContactAddView: View {
private var avatarInitial: String {
let trimmedName = displayName.trimmedNonEmpty ?? contact.preferredName
if let first = trimmedName.trimmingCharacters(in: .whitespacesAndNewlines).first {
return String(first).uppercased()
if let initials = initials(from: trimmedName) {
return initials
}
if let login = contact.login?.trimmingCharacters(in: .whitespacesAndNewlines), !login.isEmpty {
return String(login.prefix(1)).uppercased()
@ -185,3 +185,12 @@ private extension String {
return value.isEmpty ? nil : value
}
}
private func initials(from text: String) -> String? {
let components = text
.split { $0.isWhitespace }
.filter { !$0.isEmpty }
let letters = components.prefix(2).compactMap { $0.first }
guard !letters.isEmpty else { return nil }
return letters.map { String($0).uppercased() }.joined()
}

View File

@ -203,8 +203,8 @@ struct ContactEditView: View {
private var avatarInitial: String {
let trimmedName = displayName.trimmedNonEmpty ?? contact.preferredName
if let first = trimmedName.trimmingCharacters(in: .whitespacesAndNewlines).first {
return String(first).uppercased()
if let initials = initials(from: trimmedName) {
return initials
}
if let login = contact.login?.trimmingCharacters(in: .whitespacesAndNewlines), !login.isEmpty {
return String(login.prefix(1)).uppercased()
@ -336,3 +336,12 @@ private extension String {
return value.isEmpty ? nil : value
}
}
private func initials(from text: String) -> String? {
let components = text
.split { $0.isWhitespace }
.filter { !$0.isEmpty }
let letters = components.prefix(2).compactMap { $0.first }
guard !letters.isEmpty else { return nil }
return letters.map { String($0).uppercased() }.joined()
}

View File

@ -22,9 +22,10 @@ struct EditProfileView: View {
@State private var avatarViewerState: AvatarViewerState?
@State private var shareItems: [Any] = []
@State private var showShareSheet = false
private let profileService = ProfileService()
private let descriptionLimit = 1024
private let nameLimit = 32
var body: some View {
ZStack {
@ -75,13 +76,18 @@ struct EditProfileView: View {
Section(header: Text("Публичная информация")) {
TextField("Отображаемое имя", text: $displayName)
.onChange(of: displayName) { newValue in
if newValue.count > nameLimit {
displayName = String(newValue.prefix(nameLimit))
}
}
VStack(alignment: .leading, spacing: 5) {
Text("Описание")
.font(.caption)
.foregroundColor(.secondary)
TextEditor(text: $description)
.frame(height: 150)
Text("Описание")
.font(.caption)
.foregroundColor(.secondary)
TextEditor(text: $description)
.frame(height: 150)
.onChange(of: description) { newValue in
if newValue.count > descriptionLimit {
description = String(newValue.prefix(descriptionLimit))
@ -154,11 +160,27 @@ struct EditProfileView: View {
.fill(Color.secondary.opacity(0.2))
.frame(width: 120, height: 120)
.overlay(
Image(systemName: "person.fill")
.font(.system(size: 60))
Text(profileInitials)
.font(.system(size: 48, weight: .semibold))
.foregroundColor(.gray)
)
}
private var profileInitials: String {
if let initials = initials(from: displayName) {
return initials
}
if let profile = profile,
let name = profile.fullName?.trimmingCharacters(in: .whitespacesAndNewlines),
!name.isEmpty,
let initials = initials(from: name) {
return initials
}
if let username = profile?.login.trimmingCharacters(in: .whitespacesAndNewlines), !username.isEmpty {
return String(username.prefix(1)).uppercased()
}
return "?"
}
private func avatarUrl(for profile: ProfileDataPayload, fileId: String) -> URL? {
return URL(string: "\(AppConfig.API_SERVER)/v1/storage/download/avatar/\(profile.userId)?file_id=\(fileId)")
@ -360,6 +382,15 @@ struct EditProfileView: View {
}
}
private func initials(from text: String) -> String? {
let components = text
.split { $0.isWhitespace }
.filter { !$0.isEmpty }
let letters = components.prefix(2).compactMap { $0.first }
guard !letters.isEmpty else { return nil }
return letters.map { String($0).uppercased() }.joined()
}
struct ImagePicker: UIViewControllerRepresentable {
@Binding var image: UIImage?
var allowsEditing: Bool = false