Compare commits

..

No commits in common. "910eef37038ed7677b8f1fdb56a12d248b49d3e7" and "aac0a25c4d387bb164a2636f1e2bd1dc50d3f1b5" have entirely different histories.

2 changed files with 44 additions and 91 deletions

View File

@ -1240,7 +1240,6 @@
} }
}, },
"Обновить" : { "Обновить" : {
"comment" : "Contacts retry button",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -1829,9 +1828,6 @@
} }
} }
}, },
"Просмотр \"%1$@\" появится позже." : {
"comment" : "Contacts placeholder message"
},
"Профиль" : { "Профиль" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -2059,7 +2055,7 @@
"comment" : "Search placeholder copy" "comment" : "Search placeholder copy"
}, },
"Скоро" : { "Скоро" : {
"comment" : "Add blocked user placeholder title\nContacts placeholder title" "comment" : "Add blocked user placeholder title"
}, },
"Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!" : { "Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!" : {
"comment" : "Concept tab placeholder description" "comment" : "Concept tab placeholder description"

View File

@ -1,5 +1,4 @@
import SwiftUI import SwiftUI
import Foundation
struct ContactsTab: View { struct ContactsTab: View {
@State private var contacts: [Contact] = [] @State private var contacts: [Contact] = []
@ -18,23 +17,15 @@ struct ContactsTab: View {
} else if contacts.isEmpty { } else if contacts.isEmpty {
emptyState emptyState
} else { } else {
ForEach(contacts) { contact in Section(header: Text(NSLocalizedString("Контакты", comment: ""))) {
Button { ForEach(contacts) { contact in
activeAlert = .info(
title: NSLocalizedString("Скоро", comment: "Contacts placeholder title"),
message: String(format: NSLocalizedString("Просмотр \"%1$@\" появится позже.", comment: "Contacts placeholder message"), contact.displayName)
)
} label: {
ContactRow(contact: contact) ContactRow(contact: contact)
.contentShape(Rectangle())
} }
.buttonStyle(.plain)
.listRowInsets(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12))
} }
} }
} }
.background(Color(UIColor.systemBackground)) .listStyle(.insetGrouped)
.listStyle(.plain) .background(Color(.systemGroupedBackground))
.task { .task {
await loadContacts() await loadContacts()
} }
@ -49,63 +40,42 @@ struct ContactsTab: View {
message: Text(message), message: Text(message),
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK"))) dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
) )
case .info(_, let title, let message):
return Alert(
title: Text(title),
message: Text(message),
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
)
} }
} }
} }
private var loadingState: some View { private var loadingState: some View {
HStack { Section {
Spacer()
ProgressView() ProgressView()
.progressViewStyle(CircularProgressViewStyle()) .frame(maxWidth: .infinity, alignment: .center)
Spacer()
} }
.padding(.vertical, 18)
.listRowInsets(EdgeInsets(top: 18, leading: 12, bottom: 18, trailing: 12))
.listRowSeparator(.hidden)
} }
private func errorState(_ message: String) -> some View { private func errorState(_ message: String) -> some View {
HStack(alignment: .center, spacing: 8) { Section {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.orange)
Text(message) Text(message)
.font(.subheadline) .foregroundColor(.red)
.foregroundColor(.orange) .frame(maxWidth: .infinity, alignment: .center)
Spacer()
Button(action: { Task { await loadContacts() } }) {
Text(NSLocalizedString("Обновить", comment: "Contacts retry button"))
.font(.subheadline)
}
} }
.padding(.vertical, 10)
.listRowInsets(EdgeInsets(top: 10, leading: 12, bottom: 10, trailing: 12))
.listRowSeparator(.hidden)
} }
private var emptyState: some View { private var emptyState: some View {
VStack(spacing: 12) { Section {
Image(systemName: "person.crop.circle.badge.questionmark") VStack(spacing: 12) {
.font(.system(size: 52)) Image(systemName: "person.crop.circle.badge.questionmark")
.foregroundColor(.secondary) .font(.system(size: 48))
Text(NSLocalizedString("Контактов пока нет", comment: "Contacts empty state title")) .foregroundColor(.secondary)
.font(.headline) Text(NSLocalizedString("Контактов пока нет", comment: "Contacts empty state title"))
.multilineTextAlignment(.center) .font(.headline)
Text(NSLocalizedString("Добавьте контакты, чтобы быстрее выходить на связь.", comment: "Contacts empty state subtitle")) .multilineTextAlignment(.center)
.font(.subheadline) Text(NSLocalizedString("Добавьте контакты, чтобы быстрее выходить на связь.", comment: "Contacts empty state subtitle"))
.foregroundColor(.secondary) .font(.subheadline)
.multilineTextAlignment(.center) .foregroundColor(.secondary)
.multilineTextAlignment(.center)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 24)
} }
.frame(maxWidth: .infinity)
.padding(.vertical, 28)
.listRowInsets(EdgeInsets(top: 20, leading: 12, bottom: 20, trailing: 12))
.listRowSeparator(.hidden)
} }
@MainActor @MainActor
@ -134,50 +104,38 @@ private struct ContactRow: View {
let contact: Contact let contact: Contact
var body: some View { var body: some View {
HStack(alignment: .top, spacing: 10) { HStack(spacing: 12) {
Circle() Circle()
.fill(Color.accentColor.opacity(0.15)) .fill(Color.accentColor.opacity(0.15))
.frame(width: 40, height: 40) .frame(width: 44, height: 44)
.overlay( .overlay(
Text(contact.initials) Text(contact.initials)
.font(.headline) .font(.headline)
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
) )
VStack(alignment: .leading, spacing: 3) { VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .firstTextBaseline) { Text(contact.displayName)
Text(contact.displayName) .font(.body)
.font(.subheadline.weight(.semibold)) if let handle = contact.handle {
.foregroundColor(.primary) Text(handle)
Spacer() .font(.caption)
.foregroundColor(.secondary)
}
HStack(spacing: 8) {
if contact.friendCode {
Label(NSLocalizedString("Код дружбы", comment: "Friend code badge"), systemImage: "heart.circle")
.font(.caption2)
.foregroundColor(.secondary)
}
Text(contact.formattedCreatedAt) Text(contact.formattedCreatedAt)
.font(.caption2) .font(.caption2)
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
if let handle = contact.handle {
Text(handle)
.font(.caption2)
.foregroundColor(.secondary)
}
if contact.friendCode {
friendCodeBadge
}
} }
.frame(maxWidth: .infinity, alignment: .leading) Spacer()
} }
.padding(.vertical, 6) .padding(.vertical, 4)
}
private var friendCodeBadge: some View {
Text(NSLocalizedString("Код дружбы", comment: "Friend code badge"))
.font(.caption2.weight(.medium))
.foregroundColor(Color.accentColor)
.padding(.horizontal, 6)
.padding(.vertical, 3)
.background(Color.accentColor.opacity(0.12))
.clipShape(Capsule())
} }
} }
@ -245,11 +203,10 @@ private struct Contact: Identifiable, Equatable {
private enum ContactsAlert: Identifiable { private enum ContactsAlert: Identifiable {
case error(id: UUID = UUID(), message: String) case error(id: UUID = UUID(), message: String)
case info(id: UUID = UUID(), title: String, message: String)
var id: String { var id: String {
switch self { switch self {
case .error(let id, _), .info(let id, _, _): case .error(let id, _):
return id.uuidString return id.uuidString
} }
} }