ios_app_v2/yobble/Network/ChatModels.swift
2025-10-21 01:53:55 +03:00

337 lines
11 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
struct PrivateChatListData: Decodable {
let items: [PrivateChatListItem]
let hasMore: Bool
}
struct PrivateChatHistoryData: Decodable {
let items: [MessageItem]
let hasMore: Bool
}
struct PrivateChatCreateData: Decodable {
let chatId: String
let chatType: PrivateChatListItem.ChatType
let status: String
let message: String
private enum CodingKeys: String, CodingKey {
case chatId
case chatType
case status
case message
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.chatId = try container.decodeFlexibleString(forKey: .chatId)
self.chatType = try container.decode(PrivateChatListItem.ChatType.self, forKey: .chatType)
self.status = try container.decode(String.self, forKey: .status)
self.message = try container.decode(String.self, forKey: .message)
}
}
struct PrivateMessageSendData: Decodable {
let messageId: String
let chatId: String
let createdAt: Date?
private enum CodingKeys: String, CodingKey {
case messageId
case chatId
case createdAt
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.messageId = try container.decodeFlexibleString(forKey: .messageId)
self.chatId = try container.decodeFlexibleString(forKey: .chatId)
self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt)
}
}
struct PrivateChatListItem: Decodable, Identifiable {
enum ChatType: String, Decodable {
case `self`
case privateChat = "private"
case unknown
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(String.self)
self = ChatType(rawValue: rawValue) ?? .unknown
}
}
let chatId: String
let chatType: ChatType
let chatData: ChatProfile?
let lastMessage: MessageItem?
let createdAt: Date?
let unreadCount: Int
var id: String { chatId }
}
struct MessageItem: Decodable, Identifiable {
let messageId: String
let messageType: String
let chatId: String
let senderId: String
let senderData: ChatProfile?
let content: String?
let mediaLink: String?
let isViewed: Bool?
let createdAt: Date?
let updatedAt: Date?
let forwardMetadata: ForwardMetadata?
var id: String { messageId }
private enum CodingKeys: String, CodingKey {
case messageId
case messageType
case chatId
case senderId
case senderData
case content
case mediaLink
case isViewed
case createdAt
case updatedAt
case forwardMetadata
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.messageId = try container.decodeFlexibleString(forKey: .messageId)
self.messageType = try container.decodeFlexibleStringOrArray(forKey: .messageType)
self.chatId = try container.decodeFlexibleString(forKey: .chatId)
self.senderId = try container.decodeFlexibleString(forKey: .senderId)
self.senderData = try container.decodeIfPresent(ChatProfile.self, forKey: .senderData)
self.content = try container.decodeIfPresent(String.self, forKey: .content)
self.mediaLink = try container.decodeIfPresent(String.self, forKey: .mediaLink)
self.isViewed = try container.decodeIfPresent(Bool.self, forKey: .isViewed)
self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt)
self.updatedAt = try container.decodeIfPresent(Date.self, forKey: .updatedAt)
self.forwardMetadata = try container.decodeIfPresent(ForwardMetadata.self, forKey: .forwardMetadata)
}
init(
messageId: String,
messageType: String,
chatId: String,
senderId: String,
senderData: ChatProfile?,
content: String?,
mediaLink: String?,
isViewed: Bool?,
createdAt: Date?,
updatedAt: Date?,
forwardMetadata: ForwardMetadata?
) {
self.messageId = messageId
self.messageType = messageType
self.chatId = chatId
self.senderId = senderId
self.senderData = senderData
self.content = content
self.mediaLink = mediaLink
self.isViewed = isViewed
self.createdAt = createdAt
self.updatedAt = updatedAt
self.forwardMetadata = forwardMetadata
}
}
struct ForwardMetadata: Decodable {
let forwardType: String?
let forwardSenderId: String?
let forwardMessageId: String?
let forwardChatData: ChatProfile?
private enum CodingKeys: String, CodingKey {
case forwardType
case forwardSenderId
case forwardMessageId
case forwardChatData
}
}
struct ChatProfile: Decodable {
let userId: String
let login: String?
let fullName: String?
let customName: String?
let bio: String?
let lastSeen: Int?
let createdAt: Date?
let stories: [JSONValue]
let permissions: ChatPermissions?
let profilePermissions: ChatProfilePermissions?
let relationship: RelationshipStatus?
let isOfficial: Bool
private enum CodingKeys: String, CodingKey {
case userId
case login
case fullName
case customName
case bio
case lastSeen
case createdAt
case stories
case permissions
case profilePermissions
case relationship
case isOfficial
case isVerified
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.userId = try container.decodeFlexibleString(forKey: .userId)
self.login = try container.decodeIfPresent(String.self, forKey: .login)
self.fullName = try container.decodeIfPresent(String.self, forKey: .fullName)
self.customName = try container.decodeIfPresent(String.self, forKey: .customName)
self.bio = try container.decodeIfPresent(String.self, forKey: .bio)
self.lastSeen = try container.decodeIfPresent(Int.self, forKey: .lastSeen)
self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt)
self.stories = try container.decodeIfPresent([JSONValue].self, forKey: .stories) ?? []
self.permissions = try container.decodeIfPresent(ChatPermissions.self, forKey: .permissions)
self.profilePermissions = try container.decodeIfPresent(ChatProfilePermissions.self, forKey: .profilePermissions)
self.relationship = try container.decodeIfPresent(RelationshipStatus.self, forKey: .relationship)
let explicitOfficial = try container.decodeIfPresent(Bool.self, forKey: .isOfficial)
let verifiedFlag = try container.decodeIfPresent(Bool.self, forKey: .isVerified)
self.isOfficial = explicitOfficial ?? verifiedFlag ?? false
}
}
extension ChatProfile {
init(
userId: String,
login: String?,
fullName: String?,
customName: String?,
bio: String? = nil,
lastSeen: Int? = nil,
createdAt: Date? = nil,
stories: [JSONValue] = [],
permissions: ChatPermissions? = nil,
profilePermissions: ChatProfilePermissions? = nil,
relationship: RelationshipStatus? = nil,
isOfficial: Bool = false
) {
self.userId = userId
self.login = login
self.fullName = fullName
self.customName = customName
self.bio = bio
self.lastSeen = lastSeen
self.createdAt = createdAt
self.stories = stories
self.permissions = permissions
self.profilePermissions = profilePermissions
self.relationship = relationship
self.isOfficial = isOfficial
}
}
struct ChatPermissions: Decodable {
let youCanSendMessage: Bool
let youCanPublicInvitePermission: Bool
let youCanGroupInvitePermission: Bool
let youCanCallPermission: Bool
}
struct ChatProfilePermissions: Decodable {
let isSearchable: Bool?
let allowMessageForwarding: Bool
let allowMessagesFromNonContacts: Bool
let allowServerChats: Bool
let forceAutoDeleteMessagesInPrivate: Bool
let maxMessageAutoDeleteSeconds: Int?
}
struct RelationshipStatus: Decodable {
let isCurrentUserInContactsOfTarget: Bool
let isTargetUserBlockedByCurrentUser: Bool
let isCurrentUserInBlacklistOfTarget: Bool
}
enum JSONValue: Decodable {
case string(String)
case int(Int)
case double(Double)
case bool(Bool)
case array([JSONValue])
case object([String: JSONValue])
case null
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
self = .null
return
}
if let value = try? container.decode(Bool.self) {
self = .bool(value)
return
}
if let value = try? container.decode(Int.self) {
self = .int(value)
return
}
if let value = try? container.decode(Double.self) {
self = .double(value)
return
}
if let value = try? container.decode(String.self) {
self = .string(value)
return
}
if let value = try? container.decode([JSONValue].self) {
self = .array(value)
return
}
if let value = try? container.decode([String: JSONValue].self) {
self = .object(value)
return
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Не удалось декодировать значение JSONValue")
}
}
private extension KeyedDecodingContainer {
func decodeFlexibleString(forKey key: Key) throws -> String {
if let string = try? decode(String.self, forKey: key) {
return string
}
if let int = try? decode(Int.self, forKey: key) {
return String(int)
}
if let double = try? decode(Double.self, forKey: key) {
return String(double)
}
throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: codingPath + [key], debugDescription: "Expected to decode String or number for key \(key.stringValue)"))
}
func decodeFlexibleStringOrArray(forKey key: Key) throws -> String {
if let string = try? decode(String.self, forKey: key) {
return string
}
if let stringArray = try? decode([String].self, forKey: key), let first = stringArray.first {
return first
}
if let intArray = try? decode([Int].self, forKey: key), let first = intArray.first {
return String(first)
}
if let doubleArray = try? decode([Double].self, forKey: key), let first = doubleArray.first {
return String(first)
}
throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: codingPath + [key], debugDescription: "Expected to decode String or array for key \(key.stringValue)"))
}
}