Compare commits
No commits in common. "aac0a25c4d387bb164a2636f1e2bd1dc50d3f1b5" and "813795aece9251f60b3813d0363cdce3ac4f4e81" have entirely different histories.
aac0a25c4d
...
813795aece
@ -11,7 +11,6 @@ struct TopBarView: View {
|
|||||||
// var viewModel: LoginViewModel
|
// var viewModel: LoginViewModel
|
||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
@Binding var isSettingsPresented: Bool
|
@Binding var isSettingsPresented: Bool
|
||||||
@Binding var isQrPresented: Bool
|
|
||||||
|
|
||||||
// Привязка для управления боковым меню
|
// Привязка для управления боковым меню
|
||||||
@Binding var isSideMenuPresented: Bool
|
@Binding var isSideMenuPresented: Bool
|
||||||
@ -29,10 +28,6 @@ struct TopBarView: View {
|
|||||||
var isProfileTab: Bool {
|
var isProfileTab: Bool {
|
||||||
return title == "Profile"
|
return title == "Profile"
|
||||||
}
|
}
|
||||||
|
|
||||||
var isContactsTab: Bool {
|
|
||||||
return title == "Contacts"
|
|
||||||
}
|
|
||||||
|
|
||||||
private var statusMessage: String? {
|
private var statusMessage: String? {
|
||||||
if viewModel.chatLoadingState == .loading {
|
if viewModel.chatLoadingState == .loading {
|
||||||
@ -117,14 +112,6 @@ struct TopBarView: View {
|
|||||||
.imageScale(.large)
|
.imageScale(.large)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
}
|
}
|
||||||
} else if isContactsTab {
|
|
||||||
NavigationLink(isActive: $isQrPresented) {
|
|
||||||
QrView()
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "qrcode.viewfinder")
|
|
||||||
.imageScale(.large)
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// else if isChatsTab {
|
// else if isChatsTab {
|
||||||
@ -233,7 +220,6 @@ struct TopBarView_Previews: PreviewProvider {
|
|||||||
@StateObject private var viewModel = LoginViewModel()
|
@StateObject private var viewModel = LoginViewModel()
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@State private var isSettingsPresented = false
|
@State private var isSettingsPresented = false
|
||||||
@State private var isQrPresented = false
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TopBarView(
|
TopBarView(
|
||||||
@ -243,7 +229,6 @@ struct TopBarView_Previews: PreviewProvider {
|
|||||||
accounts: [selectedAccount],
|
accounts: [selectedAccount],
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
isSettingsPresented: $isSettingsPresented,
|
isSettingsPresented: $isSettingsPresented,
|
||||||
isQrPresented: $isSettingsPresented,
|
|
||||||
isSideMenuPresented: $isSideMenuPresented,
|
isSideMenuPresented: $isSideMenuPresented,
|
||||||
chatSearchRevealProgress: $revealProgress,
|
chatSearchRevealProgress: $revealProgress,
|
||||||
chatSearchText: $searchText,
|
chatSearchText: $searchText,
|
||||||
|
|||||||
@ -1,173 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
enum ContactsServiceError: LocalizedError {
|
|
||||||
case unexpectedStatus(String)
|
|
||||||
case decoding(debugDescription: String)
|
|
||||||
|
|
||||||
var errorDescription: String? {
|
|
||||||
switch self {
|
|
||||||
case .unexpectedStatus(let message):
|
|
||||||
return message
|
|
||||||
case .decoding(let debugDescription):
|
|
||||||
return AppConfig.DEBUG
|
|
||||||
? debugDescription
|
|
||||||
: NSLocalizedString("Не удалось загрузить контакты.", comment: "Contacts service decoding error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ContactPayload: Decodable {
|
|
||||||
let userId: UUID
|
|
||||||
let login: String
|
|
||||||
let fullName: String?
|
|
||||||
let customName: String?
|
|
||||||
let friendCode: Bool
|
|
||||||
let createdAt: Date
|
|
||||||
}
|
|
||||||
|
|
||||||
final class ContactsService {
|
|
||||||
private let client: NetworkClient
|
|
||||||
private let decoder: JSONDecoder
|
|
||||||
|
|
||||||
init(client: NetworkClient = .shared) {
|
|
||||||
self.client = client
|
|
||||||
self.decoder = JSONDecoder()
|
|
||||||
self.decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
||||||
self.decoder.dateDecodingStrategy = .custom(Self.decodeDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchContacts(completion: @escaping (Result<[ContactPayload], Error>) -> Void) {
|
|
||||||
client.request(
|
|
||||||
path: "/v1/user/contact/list",
|
|
||||||
method: .get,
|
|
||||||
requiresAuth: true
|
|
||||||
) { [decoder] result in
|
|
||||||
switch result {
|
|
||||||
case .success(let response):
|
|
||||||
do {
|
|
||||||
let apiResponse = try decoder.decode(APIResponse<[ContactPayload]>.self, from: response.data)
|
|
||||||
guard apiResponse.status == "fine" else {
|
|
||||||
let message = apiResponse.detail ?? NSLocalizedString("Не удалось загрузить контакты.", comment: "Contacts service unexpected status")
|
|
||||||
completion(.failure(ContactsServiceError.unexpectedStatus(message)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
completion(.success(apiResponse.data))
|
|
||||||
} catch {
|
|
||||||
let debugMessage = Self.describeDecodingError(error: error, data: response.data)
|
|
||||||
if AppConfig.DEBUG {
|
|
||||||
print("[ContactsService] decode contacts failed: \(debugMessage)")
|
|
||||||
}
|
|
||||||
completion(.failure(ContactsServiceError.decoding(debugDescription: debugMessage)))
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
if case let NetworkError.server(_, data) = error,
|
|
||||||
let data,
|
|
||||||
let message = Self.errorMessage(from: data) {
|
|
||||||
completion(.failure(ContactsServiceError.unexpectedStatus(message)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchContacts() async throws -> [ContactPayload] {
|
|
||||||
try await withCheckedThrowingContinuation { continuation in
|
|
||||||
fetchContacts { result in
|
|
||||||
continuation.resume(with: result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func decodeDate(from decoder: Decoder) throws -> Date {
|
|
||||||
let container = try decoder.singleValueContainer()
|
|
||||||
let string = try container.decode(String.self)
|
|
||||||
if let date = iso8601WithFractionalSeconds.date(from: string) {
|
|
||||||
return date
|
|
||||||
}
|
|
||||||
if let date = iso8601Simple.date(from: string) {
|
|
||||||
return date
|
|
||||||
}
|
|
||||||
throw DecodingError.dataCorruptedError(
|
|
||||||
in: container,
|
|
||||||
debugDescription: "Невозможно декодировать дату: \(string)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func describeDecodingError(error: Error, data: Data) -> String {
|
|
||||||
var parts: [String] = []
|
|
||||||
|
|
||||||
if let decodingError = error as? DecodingError {
|
|
||||||
parts.append(decodingDescription(from: decodingError))
|
|
||||||
} else {
|
|
||||||
parts.append(error.localizedDescription)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let payload = truncatedPayload(from: data) {
|
|
||||||
parts.append("payload=\(payload)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.joined(separator: "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func decodingDescription(from error: DecodingError) -> String {
|
|
||||||
switch error {
|
|
||||||
case .typeMismatch(let type, let context):
|
|
||||||
return "Type mismatch for \(type) at \(codingPath(from: context)): \(context.debugDescription)"
|
|
||||||
case .valueNotFound(let type, let context):
|
|
||||||
return "Value not found for \(type) at \(codingPath(from: context)): \(context.debugDescription)"
|
|
||||||
case .keyNotFound(let key, let context):
|
|
||||||
return "Missing key '\(key.stringValue)' at \(codingPath(from: context)): \(context.debugDescription)"
|
|
||||||
case .dataCorrupted(let context):
|
|
||||||
return "Corrupted data at \(codingPath(from: context)): \(context.debugDescription)"
|
|
||||||
@unknown default:
|
|
||||||
return error.localizedDescription
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func codingPath(from context: DecodingError.Context) -> String {
|
|
||||||
let path = context.codingPath.map { $0.stringValue }.filter { !$0.isEmpty }
|
|
||||||
return path.isEmpty ? "root" : path.joined(separator: ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func truncatedPayload(from data: Data, limit: Int = 512) -> String? {
|
|
||||||
guard let string = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
||||||
!string.isEmpty else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if string.count <= limit {
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = string.index(string.startIndex, offsetBy: limit)
|
|
||||||
return String(string[string.startIndex..<index]) + "…"
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func errorMessage(from data: Data) -> String? {
|
|
||||||
if let apiError = try? JSONDecoder().decode(ErrorResponse.self, from: data) {
|
|
||||||
if let detail = apiError.detail, !detail.isEmpty {
|
|
||||||
return detail
|
|
||||||
}
|
|
||||||
if let message = apiError.data?.message, !message.isEmpty {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let string = String(data: data, encoding: .utf8), !string.isEmpty {
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
private static let iso8601WithFractionalSeconds: ISO8601DateFormatter = {
|
|
||||||
let formatter = ISO8601DateFormatter()
|
|
||||||
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
private static let iso8601Simple: ISO8601DateFormatter = {
|
|
||||||
let formatter = ISO8601DateFormatter()
|
|
||||||
formatter.formatOptions = [.withInternetDateTime]
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
@ -158,9 +158,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Qr" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Yobble" : {
|
"Yobble" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -392,9 +389,6 @@
|
|||||||
"Добавление новых блокировок появится позже." : {
|
"Добавление новых блокировок появится позже." : {
|
||||||
"comment" : "Add blocked user placeholder message"
|
"comment" : "Add blocked user placeholder message"
|
||||||
},
|
},
|
||||||
"Добавьте контакты, чтобы быстрее выходить на связь." : {
|
|
||||||
"comment" : "Contacts empty state subtitle"
|
|
||||||
},
|
|
||||||
"Другое" : {
|
"Другое" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -602,12 +596,6 @@
|
|||||||
"Кликер в разработке" : {
|
"Кликер в разработке" : {
|
||||||
"comment" : "Concept tab placeholder title"
|
"comment" : "Concept tab placeholder title"
|
||||||
},
|
},
|
||||||
"Код дружбы" : {
|
|
||||||
"comment" : "Friend code badge"
|
|
||||||
},
|
|
||||||
"Контактов пока нет" : {
|
|
||||||
"comment" : "Contacts empty state title"
|
|
||||||
},
|
|
||||||
"Контакты" : {
|
"Контакты" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -931,9 +919,6 @@
|
|||||||
},
|
},
|
||||||
"Не удалось загрузить историю чата." : {
|
"Не удалось загрузить историю чата." : {
|
||||||
|
|
||||||
},
|
|
||||||
"Не удалось загрузить контакты." : {
|
|
||||||
"comment" : "Contacts service decoding error\nContacts service unexpected status"
|
|
||||||
},
|
},
|
||||||
"Не удалось загрузить профиль." : {
|
"Не удалось загрузить профиль." : {
|
||||||
"comment" : "Profile service decoding error\nProfile unexpected status"
|
"comment" : "Profile service decoding error\nProfile unexpected status"
|
||||||
@ -1341,7 +1326,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Ошибка" : {
|
"Ошибка" : {
|
||||||
"comment" : "Common error title\nContacts load error title\nProfile update error title",
|
"comment" : "Common error title\nProfile update error title",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
|
|||||||
@ -1,213 +1,24 @@
|
|||||||
|
//
|
||||||
|
// ContactsTab.swift
|
||||||
|
// yobble
|
||||||
|
//
|
||||||
|
// Created by cheykrym on 23.10.2025.
|
||||||
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ContactsTab: View {
|
struct ContactsTab: View {
|
||||||
@State private var contacts: [Contact] = []
|
|
||||||
@State private var isLoading = false
|
|
||||||
@State private var loadError: String?
|
|
||||||
@State private var activeAlert: ContactsAlert?
|
|
||||||
|
|
||||||
private let contactsService = ContactsService()
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
VStack {
|
||||||
if isLoading && contacts.isEmpty {
|
VStack {
|
||||||
loadingState
|
Text("Здесь не будут чаты")
|
||||||
} else if let loadError, contacts.isEmpty {
|
.font(.title)
|
||||||
errorState(loadError)
|
.foregroundColor(.gray)
|
||||||
} else if contacts.isEmpty {
|
|
||||||
emptyState
|
Spacer()
|
||||||
} else {
|
|
||||||
Section(header: Text(NSLocalizedString("Контакты", comment: ""))) {
|
|
||||||
ForEach(contacts) { contact in
|
|
||||||
ContactRow(contact: contact)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listStyle(.insetGrouped)
|
// .background(Color(.secondarySystemBackground)) // Фон для всей вкладки
|
||||||
.background(Color(.systemGroupedBackground))
|
|
||||||
.task {
|
|
||||||
await loadContacts()
|
|
||||||
}
|
|
||||||
.refreshable {
|
|
||||||
await loadContacts()
|
|
||||||
}
|
|
||||||
.alert(item: $activeAlert) { alert in
|
|
||||||
switch alert {
|
|
||||||
case .error(_, let message):
|
|
||||||
return Alert(
|
|
||||||
title: Text(NSLocalizedString("Ошибка", comment: "Contacts load error title")),
|
|
||||||
message: Text(message),
|
|
||||||
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var loadingState: some View {
|
|
||||||
Section {
|
|
||||||
ProgressView()
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func errorState(_ message: String) -> some View {
|
|
||||||
Section {
|
|
||||||
Text(message)
|
|
||||||
.foregroundColor(.red)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var emptyState: some View {
|
|
||||||
Section {
|
|
||||||
VStack(spacing: 12) {
|
|
||||||
Image(systemName: "person.crop.circle.badge.questionmark")
|
|
||||||
.font(.system(size: 48))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
Text(NSLocalizedString("Контактов пока нет", comment: "Contacts empty state title"))
|
|
||||||
.font(.headline)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
Text(NSLocalizedString("Добавьте контакты, чтобы быстрее выходить на связь.", comment: "Contacts empty state subtitle"))
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.padding(.vertical, 24)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
private func loadContacts() async {
|
|
||||||
if isLoading {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
isLoading = true
|
|
||||||
loadError = nil
|
|
||||||
|
|
||||||
do {
|
|
||||||
let payloads = try await contactsService.fetchContacts()
|
|
||||||
contacts = payloads.map(Contact.init)
|
|
||||||
} catch {
|
|
||||||
loadError = error.localizedDescription
|
|
||||||
activeAlert = .error(message: error.localizedDescription)
|
|
||||||
if AppConfig.DEBUG { print("[ContactsTab] load contacts failed: \(error)") }
|
|
||||||
}
|
|
||||||
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct ContactRow: View {
|
|
||||||
let contact: Contact
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack(spacing: 12) {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.accentColor.opacity(0.15))
|
|
||||||
.frame(width: 44, height: 44)
|
|
||||||
.overlay(
|
|
||||||
Text(contact.initials)
|
|
||||||
.font(.headline)
|
|
||||||
.foregroundColor(.accentColor)
|
|
||||||
)
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
|
||||||
Text(contact.displayName)
|
|
||||||
.font(.body)
|
|
||||||
if let handle = contact.handle {
|
|
||||||
Text(handle)
|
|
||||||
.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)
|
|
||||||
.font(.caption2)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct Contact: Identifiable, Equatable {
|
|
||||||
let id: UUID
|
|
||||||
let login: String
|
|
||||||
let fullName: String?
|
|
||||||
let customName: String?
|
|
||||||
let friendCode: Bool
|
|
||||||
let createdAt: Date
|
|
||||||
|
|
||||||
let displayName: String
|
|
||||||
let handle: String?
|
|
||||||
|
|
||||||
var initials: String {
|
|
||||||
let components = displayName.split(separator: " ")
|
|
||||||
let nameInitials = components.prefix(2).compactMap { $0.first }
|
|
||||||
if !nameInitials.isEmpty {
|
|
||||||
return nameInitials
|
|
||||||
.map { String($0).uppercased() }
|
|
||||||
.joined()
|
|
||||||
}
|
|
||||||
|
|
||||||
let filtered = login.filter { $0.isLetter }.prefix(2)
|
|
||||||
if !filtered.isEmpty {
|
|
||||||
return filtered.uppercased()
|
|
||||||
}
|
|
||||||
|
|
||||||
return "??"
|
|
||||||
}
|
|
||||||
|
|
||||||
var formattedCreatedAt: String {
|
|
||||||
Self.relativeFormatter.localizedString(for: createdAt, relativeTo: Date())
|
|
||||||
}
|
|
||||||
|
|
||||||
init(payload: ContactPayload) {
|
|
||||||
self.id = payload.userId
|
|
||||||
self.login = payload.login
|
|
||||||
self.fullName = payload.fullName
|
|
||||||
self.customName = payload.customName
|
|
||||||
self.friendCode = payload.friendCode
|
|
||||||
self.createdAt = payload.createdAt
|
|
||||||
|
|
||||||
if let customName = payload.customName, !customName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
||||||
self.displayName = customName
|
|
||||||
} else if let fullName = payload.fullName, !fullName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
||||||
self.displayName = fullName
|
|
||||||
} else {
|
|
||||||
self.displayName = payload.login
|
|
||||||
}
|
|
||||||
|
|
||||||
if !payload.login.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
||||||
self.handle = "@\(payload.login)"
|
|
||||||
} else {
|
|
||||||
self.handle = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static let relativeFormatter: RelativeDateTimeFormatter = {
|
|
||||||
let formatter = RelativeDateTimeFormatter()
|
|
||||||
formatter.unitsStyle = .short
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum ContactsAlert: Identifiable {
|
|
||||||
case error(id: UUID = UUID(), message: String)
|
|
||||||
|
|
||||||
var id: String {
|
|
||||||
switch self {
|
|
||||||
case .error(let id, _):
|
|
||||||
return id.uuidString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,6 @@ struct MainView: View {
|
|||||||
@State private var chatSearchRevealProgress: CGFloat = 0
|
@State private var chatSearchRevealProgress: CGFloat = 0
|
||||||
@State private var chatSearchText: String = ""
|
@State private var chatSearchText: String = ""
|
||||||
@State private var isSettingsPresented = false
|
@State private var isSettingsPresented = false
|
||||||
@State private var isQrPresented = false
|
|
||||||
@State private var deepLinkChatItem: PrivateChatListItem?
|
@State private var deepLinkChatItem: PrivateChatListItem?
|
||||||
@State private var isDeepLinkChatActive = false
|
@State private var isDeepLinkChatActive = false
|
||||||
|
|
||||||
@ -51,7 +50,6 @@ struct MainView: View {
|
|||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
isSettingsPresented: $isSettingsPresented,
|
isSettingsPresented: $isSettingsPresented,
|
||||||
isQrPresented: $isQrPresented,
|
|
||||||
isSideMenuPresented: $isSideMenuPresented,
|
isSideMenuPresented: $isSideMenuPresented,
|
||||||
chatSearchRevealProgress: $chatSearchRevealProgress,
|
chatSearchRevealProgress: $chatSearchRevealProgress,
|
||||||
chatSearchText: $chatSearchText
|
chatSearchText: $chatSearchText
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct QrView: View {
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Form {
|
|
||||||
|
|
||||||
}
|
|
||||||
.navigationTitle("Qr")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user