Compare commits
2 Commits
12be44673a
...
af89cea3fb
| Author | SHA1 | Date | |
|---|---|---|---|
| af89cea3fb | |||
| 3c32ef6c70 |
@ -133,6 +133,60 @@ final class BlockedUsersService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func add(userId: UUID, completion: @escaping (Result<BlockedUserInfo, Error>) -> Void) {
|
||||||
|
let request = BlockedUserCreateRequest(userId: userId, login: nil)
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
encoder.keyEncodingStrategy = .convertToSnakeCase
|
||||||
|
|
||||||
|
guard let body = try? encoder.encode(request) else {
|
||||||
|
let message = NSLocalizedString("Не удалось подготовить данные запроса.", comment: "Blocked users create encoding error")
|
||||||
|
completion(.failure(BlockedUsersServiceError.encoding(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client.request(
|
||||||
|
path: "/v1/user/blacklist/add",
|
||||||
|
method: .post,
|
||||||
|
body: body,
|
||||||
|
requiresAuth: true
|
||||||
|
) { [decoder] result in
|
||||||
|
switch result {
|
||||||
|
case .success(let response):
|
||||||
|
do {
|
||||||
|
let apiResponse = try decoder.decode(APIResponse<BlockedUserInfo>.self, from: response.data)
|
||||||
|
guard apiResponse.status == "fine" else {
|
||||||
|
let message = apiResponse.detail ?? NSLocalizedString("Не удалось заблокировать пользователя.", comment: "Blocked users create unexpected status")
|
||||||
|
completion(.failure(BlockedUsersServiceError.unexpectedStatus(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completion(.success(apiResponse.data))
|
||||||
|
} catch {
|
||||||
|
let debugMessage = Self.describeDecodingError(error: error, data: response.data)
|
||||||
|
if AppConfig.DEBUG {
|
||||||
|
print("[BlockedUsersService] decode create response failed: \(debugMessage)")
|
||||||
|
}
|
||||||
|
completion(.failure(BlockedUsersServiceError.decoding(debugDescription: debugMessage)))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
if case let NetworkError.server(_, data) = error,
|
||||||
|
let data,
|
||||||
|
let message = Self.errorMessage(from: data) {
|
||||||
|
completion(.failure(BlockedUsersServiceError.unexpectedStatus(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(userId: UUID) async throws -> BlockedUserInfo {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
add(userId: userId) { result in
|
||||||
|
continuation.resume(with: result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func decodeDate(from decoder: Decoder) throws -> Date {
|
private static func decodeDate(from decoder: Decoder) throws -> Date {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
let string = try container.decode(String.self)
|
let string = try container.decode(String.self)
|
||||||
@ -229,3 +283,8 @@ final class BlockedUsersService {
|
|||||||
private struct BlockedUserDeleteRequest: Encodable {
|
private struct BlockedUserDeleteRequest: Encodable {
|
||||||
let userId: UUID
|
let userId: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct BlockedUserCreateRequest: Encodable {
|
||||||
|
let userId: UUID?
|
||||||
|
let login: String?
|
||||||
|
}
|
||||||
|
|||||||
@ -74,6 +74,57 @@ final class ProfileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fetchProfile(userId: UUID, completion: @escaping (Result<ChatProfile, Error>) -> Void) {
|
||||||
|
fetchProfile(userId: userId.uuidString, completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchProfile(userId: String, completion: @escaping (Result<ChatProfile, Error>) -> Void) {
|
||||||
|
let sanitizedId = userId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
|
client.request(
|
||||||
|
path: "/v1/profile/\(sanitizedId)",
|
||||||
|
method: .get,
|
||||||
|
requiresAuth: true
|
||||||
|
) { [decoder] result in
|
||||||
|
switch result {
|
||||||
|
case .success(let response):
|
||||||
|
do {
|
||||||
|
let profile = try Self.decodeProfileResponse(
|
||||||
|
data: response.data,
|
||||||
|
decoder: decoder,
|
||||||
|
requestedId: sanitizedId
|
||||||
|
)
|
||||||
|
completion(.success(profile))
|
||||||
|
} catch {
|
||||||
|
if AppConfig.DEBUG {
|
||||||
|
print("[ProfileService] decode profile by id failed: \(error)")
|
||||||
|
}
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
if case let NetworkError.server(_, data) = error,
|
||||||
|
let data,
|
||||||
|
let message = Self.errorMessage(from: data) {
|
||||||
|
completion(.failure(ProfileServiceError.unexpectedStatus(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchProfile(userId: UUID) async throws -> ChatProfile {
|
||||||
|
try await fetchProfile(userId: userId.uuidString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchProfile(userId: String) async throws -> ChatProfile {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
fetchProfile(userId: userId, completion: { result in
|
||||||
|
continuation.resume(with: result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateProfile(_ payload: ProfileUpdateRequestPayload, completion: @escaping (Result<String, Error>) -> Void) {
|
func updateProfile(_ payload: ProfileUpdateRequestPayload, completion: @escaping (Result<String, Error>) -> Void) {
|
||||||
let encoder = JSONEncoder()
|
let encoder = JSONEncoder()
|
||||||
encoder.keyEncodingStrategy = .convertToSnakeCase
|
encoder.keyEncodingStrategy = .convertToSnakeCase
|
||||||
@ -201,6 +252,59 @@ final class ProfileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func decodeProfileResponse(
|
||||||
|
data: Data,
|
||||||
|
decoder: JSONDecoder,
|
||||||
|
requestedId: String
|
||||||
|
) throws -> ChatProfile {
|
||||||
|
let defaultErrorMessage = NSLocalizedString("Не удалось загрузить профиль.", comment: "Profile unexpected status")
|
||||||
|
var dictionaryDecodeError: String?
|
||||||
|
|
||||||
|
do {
|
||||||
|
let apiResponse = try decoder.decode(APIResponse<[String: ChatProfile]>.self, from: data)
|
||||||
|
guard apiResponse.status == "fine" else {
|
||||||
|
let message = apiResponse.detail ?? defaultErrorMessage
|
||||||
|
throw ProfileServiceError.unexpectedStatus(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalizedKey = requestedId.lowercased()
|
||||||
|
if let profile = apiResponse.data[requestedId]
|
||||||
|
?? apiResponse.data[normalizedKey]
|
||||||
|
?? apiResponse.data[requestedId.uppercased()]
|
||||||
|
?? apiResponse.data.first?.value {
|
||||||
|
return profile
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ProfileServiceError.unexpectedStatus(
|
||||||
|
NSLocalizedString("Профиль не найден.", comment: "Profile by id missing")
|
||||||
|
)
|
||||||
|
} catch let error as ProfileServiceError {
|
||||||
|
throw error
|
||||||
|
} catch {
|
||||||
|
dictionaryDecodeError = Self.describeDecodingError(error: error, data: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let apiResponse = try decoder.decode(APIResponse<ChatProfile>.self, from: data)
|
||||||
|
guard apiResponse.status == "fine" else {
|
||||||
|
let message = apiResponse.detail ?? defaultErrorMessage
|
||||||
|
throw ProfileServiceError.unexpectedStatus(message)
|
||||||
|
}
|
||||||
|
return apiResponse.data
|
||||||
|
} catch let error as ProfileServiceError {
|
||||||
|
throw error
|
||||||
|
} catch {
|
||||||
|
let singleError = Self.describeDecodingError(error: error, data: data)
|
||||||
|
let combined: String
|
||||||
|
if let dictionaryDecodeError {
|
||||||
|
combined = dictionaryDecodeError + "\nOR\n" + singleError
|
||||||
|
} else {
|
||||||
|
combined = singleError
|
||||||
|
}
|
||||||
|
throw ProfileServiceError.decoding(debugDescription: combined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func decodeDate(from decoder: Decoder) throws -> Date {
|
private static func decodeDate(from decoder: Decoder) throws -> Date {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
let string = try container.decode(String.self)
|
let string = try container.decode(String.self)
|
||||||
|
|||||||
@ -280,9 +280,6 @@
|
|||||||
"Блокировка контакта \"%1$@\" появится позже." : {
|
"Блокировка контакта \"%1$@\" появится позже." : {
|
||||||
"comment" : "Contacts block placeholder message"
|
"comment" : "Contacts block placeholder message"
|
||||||
},
|
},
|
||||||
"Блокировка чата пока в дизайне. Готовим отдельный экран со статусом и жалобой." : {
|
|
||||||
"comment" : "Message profile block alert message"
|
|
||||||
},
|
|
||||||
"Бот" : {
|
"Бот" : {
|
||||||
"comment" : "Тип сессии — бот"
|
"comment" : "Тип сессии — бот"
|
||||||
},
|
},
|
||||||
@ -639,7 +636,7 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
"Заблокировать" : {
|
"Заблокировать" : {
|
||||||
"comment" : "Message profile block alert title\nMessage profile block title"
|
"comment" : "Message profile block title"
|
||||||
},
|
},
|
||||||
"Заблокировать контакт" : {
|
"Заблокировать контакт" : {
|
||||||
"comment" : "Contacts context action block"
|
"comment" : "Contacts context action block"
|
||||||
@ -1285,6 +1282,9 @@
|
|||||||
"Не удалось выполнить поиск." : {
|
"Не удалось выполнить поиск." : {
|
||||||
"comment" : "Search error fallback\nSearch service decoding error"
|
"comment" : "Search error fallback\nSearch service decoding error"
|
||||||
},
|
},
|
||||||
|
"Не удалось заблокировать пользователя." : {
|
||||||
|
"comment" : "Blocked users create unexpected status"
|
||||||
|
},
|
||||||
"Не удалось завершить другие сессии." : {
|
"Не удалось завершить другие сессии." : {
|
||||||
"comment" : "Sessions service revoke-all unexpected status"
|
"comment" : "Sessions service revoke-all unexpected status"
|
||||||
},
|
},
|
||||||
@ -1348,6 +1348,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Не удалось обновить профиль" : {
|
||||||
|
"comment" : "Message profile refresh error title"
|
||||||
|
},
|
||||||
"Не удалось обработать данные чатов." : {
|
"Не удалось обработать данные чатов." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1369,6 +1372,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Не удалось определить пользователя для блокировки." : {
|
||||||
|
"comment" : "Message profile missing user id error"
|
||||||
|
},
|
||||||
"Не удалось открыть чат" : {
|
"Не удалось открыть чат" : {
|
||||||
"comment" : "Chat creation error title"
|
"comment" : "Chat creation error title"
|
||||||
},
|
},
|
||||||
@ -1382,7 +1388,7 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
"Не удалось подготовить данные запроса." : {
|
"Не удалось подготовить данные запроса." : {
|
||||||
"comment" : "Blocked users delete encoding error\nProfile update encoding error"
|
"comment" : "Blocked users create encoding error\nBlocked users delete encoding error\nProfile update encoding error"
|
||||||
},
|
},
|
||||||
"Не удалось подготовить изображение для загрузки." : {
|
"Не удалось подготовить изображение для загрузки." : {
|
||||||
"comment" : "Avatar encoding error"
|
"comment" : "Avatar encoding error"
|
||||||
@ -2005,6 +2011,9 @@
|
|||||||
},
|
},
|
||||||
"Подключение" : {
|
"Подключение" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Подождите..." : {
|
||||||
|
"comment" : "Message profile block action pending subtitle"
|
||||||
},
|
},
|
||||||
"Подтвердите пароль" : {
|
"Подтвердите пароль" : {
|
||||||
|
|
||||||
@ -2377,6 +2386,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Профиль не найден." : {
|
||||||
|
"comment" : "Profile by id missing"
|
||||||
|
},
|
||||||
"Профиль пока не загружен. Попробуйте позже." : {
|
"Профиль пока не загружен. Попробуйте позже." : {
|
||||||
"comment" : "Profile not ready error"
|
"comment" : "Profile not ready error"
|
||||||
},
|
},
|
||||||
@ -2390,12 +2402,12 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
"Разблокировать" : {
|
"Разблокировать" : {
|
||||||
"comment" : "Message profile unblock alert title\nMessage profile unblock title\nUnblock confirmation action"
|
"comment" : "Message profile unblock title\nUnblock confirmation action"
|
||||||
},
|
},
|
||||||
"Раздел скоро станет активным — собираем и индексируем вложения." : {
|
"Раздел скоро станет активным — собираем и индексируем вложения." : {
|
||||||
"comment" : "Message profile media placeholder message"
|
"comment" : "Message profile media placeholder message"
|
||||||
},
|
},
|
||||||
"Разделы временно показывают заглушки — позже спрячем пустые категории." : {
|
"Разделы временно показывают заглушки." : {
|
||||||
"comment" : "Message profile media footer new"
|
"comment" : "Message profile media footer new"
|
||||||
},
|
},
|
||||||
"Разрешить пересылку сообщений" : {
|
"Разрешить пересылку сообщений" : {
|
||||||
@ -2645,9 +2657,6 @@
|
|||||||
"Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!" : {
|
"Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!" : {
|
||||||
"comment" : "Concept tab placeholder description"
|
"comment" : "Concept tab placeholder description"
|
||||||
},
|
},
|
||||||
"Скоро появится разблокировка с подтверждением и синхронизацией." : {
|
|
||||||
"comment" : "Message profile unblock alert message"
|
|
||||||
},
|
|
||||||
"Скрыть" : {
|
"Скрыть" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@ -10,10 +10,22 @@ struct MessageProfileView: View {
|
|||||||
let chat: PrivateChatListItem
|
let chat: PrivateChatListItem
|
||||||
let currentUserId: String?
|
let currentUserId: String?
|
||||||
private let avatarSize: CGFloat = 96
|
private let avatarSize: CGFloat = 96
|
||||||
|
private let blockedUsersService = BlockedUsersService()
|
||||||
|
private let profileService = ProfileService()
|
||||||
|
|
||||||
@State private var areNotificationsEnabled: Bool = true
|
@State private var areNotificationsEnabled: Bool = true
|
||||||
@State private var placeholderAlert: PlaceholderAlert?
|
@State private var placeholderAlert: PlaceholderAlert?
|
||||||
@State private var isBioExpanded: Bool = false
|
@State private var isBioExpanded: Bool = false
|
||||||
|
@State private var chatProfile: ChatProfile?
|
||||||
|
@State private var isBlockedByCurrentUserState: Bool
|
||||||
|
@State private var isProcessingBlockAction = false
|
||||||
|
|
||||||
|
init(chat: PrivateChatListItem, currentUserId: String?) {
|
||||||
|
self.chat = chat
|
||||||
|
self.currentUserId = currentUserId
|
||||||
|
_chatProfile = State(initialValue: chat.chatData)
|
||||||
|
_isBlockedByCurrentUserState = State(initialValue: chat.chatData?.relationship?.isTargetUserBlockedByCurrentUser ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
@ -270,10 +282,14 @@ struct MessageProfileView: View {
|
|||||||
title: isBlockedByCurrentUser
|
title: isBlockedByCurrentUser
|
||||||
? NSLocalizedString("Разблокировать", comment: "Message profile unblock title")
|
? NSLocalizedString("Разблокировать", comment: "Message profile unblock title")
|
||||||
: NSLocalizedString("Заблокировать", comment: "Message profile block title"),
|
: NSLocalizedString("Заблокировать", comment: "Message profile block title"),
|
||||||
|
subtitle: isProcessingBlockAction
|
||||||
|
? NSLocalizedString("Подождите...", comment: "Message profile block action pending subtitle")
|
||||||
|
: nil,
|
||||||
tint: isBlockedByCurrentUser ? Color.green : Color.red
|
tint: isBlockedByCurrentUser ? Color.green : Color.red
|
||||||
) {
|
) {
|
||||||
handleBlockToggleTap()
|
handleBlockToggleTap()
|
||||||
}
|
}
|
||||||
|
.disabled(isProcessingBlockAction)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,13 +527,72 @@ struct MessageProfileView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handleBlockToggleTap() {
|
private func handleBlockToggleTap() {
|
||||||
let title = isBlockedByCurrentUser
|
guard !isProcessingBlockAction else { return }
|
||||||
? NSLocalizedString("Разблокировать", comment: "Message profile unblock alert title")
|
|
||||||
: NSLocalizedString("Заблокировать", comment: "Message profile block alert title")
|
guard let userIdString = currentChatProfile?.userId,
|
||||||
let message = isBlockedByCurrentUser
|
let userId = UUID(uuidString: userIdString) else {
|
||||||
? NSLocalizedString("Скоро появится разблокировка с подтверждением и синхронизацией.", comment: "Message profile unblock alert message")
|
placeholderAlert = PlaceholderAlert(
|
||||||
: NSLocalizedString("Блокировка чата пока в дизайне. Готовим отдельный экран со статусом и жалобой.", comment: "Message profile block alert message")
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
showPlaceholderAction(title: title, message: message)
|
message: NSLocalizedString("Не удалось определить пользователя для блокировки.", comment: "Message profile missing user id error")
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isProcessingBlockAction = true
|
||||||
|
let shouldBlock = !isBlockedByCurrentUser
|
||||||
|
Task {
|
||||||
|
await performBlockAction(shouldBlock: shouldBlock, userId: userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func performBlockAction(shouldBlock: Bool, userId: UUID) async {
|
||||||
|
do {
|
||||||
|
if shouldBlock {
|
||||||
|
_ = try await blockedUsersService.add(userId: userId)
|
||||||
|
} else {
|
||||||
|
_ = try await blockedUsersService.remove(userId: userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
await MainActor.run {
|
||||||
|
isBlockedByCurrentUserState = shouldBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshChatProfile(userId: userId)
|
||||||
|
} catch {
|
||||||
|
if AppConfig.DEBUG {
|
||||||
|
print("[MessageProfileView] block action failed: \(error)")
|
||||||
|
}
|
||||||
|
await MainActor.run {
|
||||||
|
placeholderAlert = PlaceholderAlert(
|
||||||
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
|
message: error.localizedDescription
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await MainActor.run {
|
||||||
|
isProcessingBlockAction = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func refreshChatProfile(userId: UUID) async {
|
||||||
|
do {
|
||||||
|
let profile = try await profileService.fetchProfile(userId: userId)
|
||||||
|
await MainActor.run {
|
||||||
|
chatProfile = profile
|
||||||
|
isBlockedByCurrentUserState = profile.relationship?.isTargetUserBlockedByCurrentUser ?? isBlockedByCurrentUserState
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
if AppConfig.DEBUG {
|
||||||
|
print("[MessageProfileView] refresh profile failed: \(error)")
|
||||||
|
}
|
||||||
|
await MainActor.run {
|
||||||
|
placeholderAlert = PlaceholderAlert(
|
||||||
|
title: NSLocalizedString("Не удалось обновить профиль", comment: "Message profile refresh error title"),
|
||||||
|
message: error.localizedDescription
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var mediaCategories: [MediaCategory] {
|
private var mediaCategories: [MediaCategory] {
|
||||||
@ -537,11 +612,11 @@ struct MessageProfileView: View {
|
|||||||
// MARK: - Derived Data
|
// MARK: - Derived Data
|
||||||
|
|
||||||
private var profileBio: String? {
|
private var profileBio: String? {
|
||||||
trimmed(chat.chatData?.bio)
|
trimmed(currentChatProfile?.bio)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var membershipDescription: String? {
|
private var membershipDescription: String? {
|
||||||
guard let createdAt = chat.chatData?.createdAt else { return nil }
|
guard let createdAt = currentChatProfile?.createdAt else { return nil }
|
||||||
let formatted = MessageProfileView.joinedFormatter.string(from: createdAt)
|
let formatted = MessageProfileView.joinedFormatter.string(from: createdAt)
|
||||||
return String(
|
return String(
|
||||||
// format: NSLocalizedString("На платформе с %@", comment: "Message profile joined format"),
|
// format: NSLocalizedString("На платформе с %@", comment: "Message profile joined format"),
|
||||||
@ -551,12 +626,12 @@ struct MessageProfileView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var shouldShowRelationshipQuickActions: Bool {
|
private var shouldShowRelationshipQuickActions: Bool {
|
||||||
guard let relationship = chat.chatData?.relationship else { return false }
|
guard let relationship = currentChatProfile?.relationship else { return false }
|
||||||
return !relationship.isTargetInContactsOfCurrentUser
|
return !relationship.isTargetInContactsOfCurrentUser
|
||||||
}
|
}
|
||||||
|
|
||||||
private var ratingDisplayValue: String {
|
private var ratingDisplayValue: String {
|
||||||
guard let rating = chat.chatData?.rating else {
|
guard let rating = currentChatProfile?.rating else {
|
||||||
return NSLocalizedString("Недоступно", comment: "Message profile rating unavailable")
|
return NSLocalizedString("Недоступно", comment: "Message profile rating unavailable")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +665,7 @@ struct MessageProfileView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let lastSeen = chat.chatData?.lastSeen else { return nil }
|
guard let lastSeen = currentChatProfile?.lastSeen else { return nil }
|
||||||
let lastSeenDate = Date(timeIntervalSince1970: TimeInterval(lastSeen))
|
let lastSeenDate = Date(timeIntervalSince1970: TimeInterval(lastSeen))
|
||||||
let interval = Date().timeIntervalSince(lastSeenDate)
|
let interval = Date().timeIntervalSince(lastSeenDate)
|
||||||
|
|
||||||
@ -625,7 +700,7 @@ struct MessageProfileView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let relationship = chat.chatData?.relationship {
|
if let relationship = currentChatProfile?.relationship {
|
||||||
if relationship.isTargetInContactsOfCurrentUser {
|
if relationship.isTargetInContactsOfCurrentUser {
|
||||||
tags.append(
|
tags.append(
|
||||||
StatusTag(
|
StatusTag(
|
||||||
@ -747,12 +822,14 @@ struct MessageProfileView: View {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isBlockedByCurrentUser: Bool {
|
private var currentChatProfile: ChatProfile? {
|
||||||
chat.chatData?.relationship?.isTargetUserBlockedByCurrentUser ?? false
|
chatProfile ?? chat.chatData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isBlockedByCurrentUser: Bool { isBlockedByCurrentUserState }
|
||||||
|
|
||||||
private var avatarUrl: URL? {
|
private var avatarUrl: URL? {
|
||||||
guard let chatData = chat.chatData,
|
guard let chatData = currentChatProfile,
|
||||||
let fileId = chatData.avatars?.current?.fileId else {
|
let fileId = chatData.avatars?.current?.fileId else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -763,7 +840,7 @@ struct MessageProfileView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var profileAvatar: some View {
|
private var profileAvatar: some View {
|
||||||
if let url = avatarUrl,
|
if let url = avatarUrl,
|
||||||
let fileId = chat.chatData?.avatars?.current?.fileId,
|
let fileId = currentChatProfile?.avatars?.current?.fileId,
|
||||||
let userId = currentUserId {
|
let userId = currentUserId {
|
||||||
CachedAvatarView(url: url, fileId: fileId, userId: userId) {
|
CachedAvatarView(url: url, fileId: fileId, userId: userId) {
|
||||||
placeholderAvatar
|
placeholderAvatar
|
||||||
@ -811,7 +888,7 @@ struct MessageProfileView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var avatarInitial: String {
|
private var avatarInitial: String {
|
||||||
if let name = trimmed(chat.chatData?.customName) ?? trimmed(chat.chatData?.fullName) {
|
if let name = trimmed(currentChatProfile?.customName) ?? trimmed(currentChatProfile?.fullName) {
|
||||||
let components = name.split(separator: " ")
|
let components = name.split(separator: " ")
|
||||||
let initials = components.prefix(2).compactMap { $0.first }
|
let initials = components.prefix(2).compactMap { $0.first }
|
||||||
if !initials.isEmpty {
|
if !initials.isEmpty {
|
||||||
@ -819,7 +896,7 @@ struct MessageProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let login = trimmed(chat.chatData?.login) {
|
if let login = trimmed(currentChatProfile?.login) {
|
||||||
return String(login.prefix(1)).uppercased()
|
return String(login.prefix(1)).uppercased()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,13 +911,13 @@ struct MessageProfileView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var displayName: String {
|
private var displayName: String {
|
||||||
if let custom = trimmed(chat.chatData?.customName) {
|
if let custom = trimmed(currentChatProfile?.customName) {
|
||||||
return custom
|
return custom
|
||||||
}
|
}
|
||||||
if let full = trimmed(chat.chatData?.fullName) {
|
if let full = trimmed(currentChatProfile?.fullName) {
|
||||||
return full
|
return full
|
||||||
}
|
}
|
||||||
if let login = trimmed(chat.chatData?.login) {
|
if let login = trimmed(currentChatProfile?.login) {
|
||||||
return "@\(login)"
|
return "@\(login)"
|
||||||
}
|
}
|
||||||
return NSLocalizedString("Неизвестный пользователь", comment: "Message profile fallback title")
|
return NSLocalizedString("Неизвестный пользователь", comment: "Message profile fallback title")
|
||||||
@ -848,9 +925,9 @@ struct MessageProfileView: View {
|
|||||||
|
|
||||||
private var publicFullName: String? {
|
private var publicFullName: String? {
|
||||||
guard
|
guard
|
||||||
let custom = trimmed(chat.chatData?.customName),
|
let custom = trimmed(currentChatProfile?.customName),
|
||||||
!custom.isEmpty, // custom должен быть
|
!custom.isEmpty, // custom должен быть
|
||||||
let full = trimmed(chat.chatData?.fullName),
|
let full = trimmed(currentChatProfile?.fullName),
|
||||||
!full.isEmpty // full должен быть
|
!full.isEmpty // full должен быть
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
@ -860,16 +937,16 @@ struct MessageProfileView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var loginDisplay: String? {
|
private var loginDisplay: String? {
|
||||||
guard let login = trimmed(chat.chatData?.login) else { return nil }
|
guard let login = trimmed(currentChatProfile?.login) else { return nil }
|
||||||
return "@\(login)"
|
return "@\(login)"
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isDeletedUser: Bool {
|
private var isDeletedUser: Bool {
|
||||||
trimmed(chat.chatData?.login) == nil
|
trimmed(currentChatProfile?.login) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isOfficial: Bool {
|
private var isOfficial: Bool {
|
||||||
chat.chatData?.isOfficial ?? false
|
currentChatProfile?.isOfficial ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Formatters & Models
|
// MARK: - Formatters & Models
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user