337 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			337 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
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)"))
 | 
						||
    }
 | 
						||
}
 |