fix contact
This commit is contained in:
parent
e44d56e71b
commit
e9b43e76fa
@ -25,6 +25,11 @@ struct ContactPayload: Decodable {
|
|||||||
let createdAt: Date
|
let createdAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ContactsListPayload: Decodable {
|
||||||
|
let items: [ContactPayload]
|
||||||
|
let hasMore: Bool
|
||||||
|
}
|
||||||
|
|
||||||
final class ContactsService {
|
final class ContactsService {
|
||||||
private let client: NetworkClient
|
private let client: NetworkClient
|
||||||
private let decoder: JSONDecoder
|
private let decoder: JSONDecoder
|
||||||
@ -36,16 +41,20 @@ final class ContactsService {
|
|||||||
self.decoder.dateDecodingStrategy = .custom(Self.decodeDate)
|
self.decoder.dateDecodingStrategy = .custom(Self.decodeDate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchContacts(completion: @escaping (Result<[ContactPayload], Error>) -> Void) {
|
func fetchContacts(limit: Int, offset: Int, completion: @escaping (Result<ContactsListPayload, Error>) -> Void) {
|
||||||
client.request(
|
client.request(
|
||||||
path: "/v1/user/contact/list",
|
path: "/v1/user/contact/list",
|
||||||
method: .get,
|
method: .get,
|
||||||
|
query: [
|
||||||
|
"limit": String(limit),
|
||||||
|
"offset": String(offset)
|
||||||
|
],
|
||||||
requiresAuth: true
|
requiresAuth: true
|
||||||
) { [decoder] result in
|
) { [decoder] result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let response):
|
case .success(let response):
|
||||||
do {
|
do {
|
||||||
let apiResponse = try decoder.decode(APIResponse<[ContactPayload]>.self, from: response.data)
|
let apiResponse = try decoder.decode(APIResponse<ContactsListPayload>.self, from: response.data)
|
||||||
guard apiResponse.status == "fine" else {
|
guard apiResponse.status == "fine" else {
|
||||||
let message = apiResponse.detail ?? NSLocalizedString("Не удалось загрузить контакты.", comment: "Contacts service unexpected status")
|
let message = apiResponse.detail ?? NSLocalizedString("Не удалось загрузить контакты.", comment: "Contacts service unexpected status")
|
||||||
completion(.failure(ContactsServiceError.unexpectedStatus(message)))
|
completion(.failure(ContactsServiceError.unexpectedStatus(message)))
|
||||||
@ -71,9 +80,9 @@ final class ContactsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchContacts() async throws -> [ContactPayload] {
|
func fetchContacts(limit: Int, offset: Int) async throws -> ContactsListPayload {
|
||||||
try await withCheckedThrowingContinuation { continuation in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
fetchContacts { result in
|
fetchContacts(limit: limit, offset: offset) { result in
|
||||||
continuation.resume(with: result)
|
continuation.resume(with: result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,13 @@ struct ContactsTab: View {
|
|||||||
@State private var contacts: [Contact] = []
|
@State private var contacts: [Contact] = []
|
||||||
@State private var isLoading = false
|
@State private var isLoading = false
|
||||||
@State private var loadError: String?
|
@State private var loadError: String?
|
||||||
|
@State private var pagingError: String?
|
||||||
@State private var activeAlert: ContactsAlert?
|
@State private var activeAlert: ContactsAlert?
|
||||||
|
@State private var hasMore = true
|
||||||
|
@State private var offset = 0
|
||||||
|
|
||||||
private let contactsService = ContactsService()
|
private let contactsService = ContactsService()
|
||||||
|
private let pageSize = 25
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
@ -20,7 +24,7 @@ struct ContactsTab: View {
|
|||||||
} else if contacts.isEmpty {
|
} else if contacts.isEmpty {
|
||||||
emptyState
|
emptyState
|
||||||
} else {
|
} else {
|
||||||
ForEach(contacts) { contact in
|
ForEach(Array(contacts.enumerated()), id: \.element.id) { index, contact in
|
||||||
Button {
|
Button {
|
||||||
showContactPlaceholder(for: contact)
|
showContactPlaceholder(for: contact)
|
||||||
} label: {
|
} label: {
|
||||||
@ -57,16 +61,29 @@ struct ContactsTab: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listRowInsets(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12))
|
.listRowInsets(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12))
|
||||||
|
.onAppear {
|
||||||
|
if index >= contacts.count - 5 {
|
||||||
|
Task {
|
||||||
|
await loadContacts(reset: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isLoading && !contacts.isEmpty {
|
||||||
|
loadingState
|
||||||
|
} else if let pagingError, !contacts.isEmpty {
|
||||||
|
pagingErrorState(pagingError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(Color(UIColor.systemBackground))
|
.background(Color(UIColor.systemBackground))
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
.task {
|
.task {
|
||||||
await loadContacts()
|
await loadContacts(reset: false)
|
||||||
}
|
}
|
||||||
.refreshable {
|
.refreshable {
|
||||||
await loadContacts()
|
await refreshContacts()
|
||||||
}
|
}
|
||||||
.alert(item: $activeAlert) { alert in
|
.alert(item: $activeAlert) { alert in
|
||||||
switch alert {
|
switch alert {
|
||||||
@ -106,7 +123,25 @@ struct ContactsTab: View {
|
|||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.orange)
|
.foregroundColor(.orange)
|
||||||
Spacer()
|
Spacer()
|
||||||
Button(action: { Task { await loadContacts() } }) {
|
Button(action: { Task { await refreshContacts() } }) {
|
||||||
|
Text(NSLocalizedString("Обновить", comment: "Contacts retry button"))
|
||||||
|
.font(.subheadline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.listRowInsets(EdgeInsets(top: 10, leading: 12, bottom: 10, trailing: 12))
|
||||||
|
.listRowSeparator(.hidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func pagingErrorState(_ message: String) -> some View {
|
||||||
|
HStack(alignment: .center, spacing: 8) {
|
||||||
|
Image(systemName: "exclamationmark.triangle.fill")
|
||||||
|
.foregroundColor(.orange)
|
||||||
|
Text(message)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.orange)
|
||||||
|
Spacer()
|
||||||
|
Button(action: { Task { await loadContacts(reset: false) } }) {
|
||||||
Text(NSLocalizedString("Обновить", comment: "Contacts retry button"))
|
Text(NSLocalizedString("Обновить", comment: "Contacts retry button"))
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
}
|
}
|
||||||
@ -136,20 +171,43 @@ struct ContactsTab: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func loadContacts() async {
|
private func refreshContacts() async {
|
||||||
if isLoading {
|
hasMore = true
|
||||||
return
|
offset = 0
|
||||||
}
|
pagingError = nil
|
||||||
|
loadError = nil
|
||||||
|
contacts.removeAll()
|
||||||
|
await loadContacts(reset: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
private func loadContacts(reset: Bool) async {
|
||||||
|
if isLoading { return }
|
||||||
|
if !reset && !hasMore { return }
|
||||||
|
|
||||||
isLoading = true
|
isLoading = true
|
||||||
loadError = nil
|
if offset == 0 {
|
||||||
|
loadError = nil
|
||||||
|
}
|
||||||
|
pagingError = nil
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let payloads = try await contactsService.fetchContacts()
|
let payload = try await contactsService.fetchContacts(limit: pageSize, offset: offset)
|
||||||
contacts = payloads.map(Contact.init)
|
let newContacts = payload.items.map(Contact.init)
|
||||||
|
if reset {
|
||||||
|
contacts = newContacts
|
||||||
|
} else {
|
||||||
|
contacts.append(contentsOf: newContacts)
|
||||||
|
}
|
||||||
|
offset += newContacts.count
|
||||||
|
hasMore = payload.hasMore
|
||||||
} catch {
|
} catch {
|
||||||
loadError = error.localizedDescription
|
let message = error.localizedDescription
|
||||||
// activeAlert = .error(message: error.localizedDescription)
|
if contacts.isEmpty {
|
||||||
|
loadError = message
|
||||||
|
} else {
|
||||||
|
pagingError = message
|
||||||
|
}
|
||||||
if AppConfig.DEBUG { print("[ContactsTab] load contacts failed: \(error)") }
|
if AppConfig.DEBUG { print("[ContactsTab] load contacts failed: \(error)") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user