initial commit
This commit is contained in:
46
ios_client/EncryptedChat/Models/Conversation.swift
Normal file
46
ios_client/EncryptedChat/Models/Conversation.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
import Foundation
|
||||
|
||||
struct Conversation: Identifiable, Equatable {
|
||||
let id: String
|
||||
var name: String?
|
||||
var members: [ConversationMember]
|
||||
var createdBy: String?
|
||||
var avatarFile: String?
|
||||
var unreadCount: Int
|
||||
var isFavorite: Bool
|
||||
var lastMessageTime: Date?
|
||||
|
||||
var isGroup: Bool {
|
||||
name != nil || members.count > 2
|
||||
}
|
||||
|
||||
/// Display name: group name, or DM partner username
|
||||
func displayName(currentUserId: String) -> String {
|
||||
if let name = name, !name.isEmpty {
|
||||
return name
|
||||
}
|
||||
// DM: show the other person's name
|
||||
if let other = members.first(where: { $0.userId != currentUserId }) {
|
||||
return other.username
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
/// DM partner user ID (nil for groups)
|
||||
func dmPartnerId(currentUserId: String) -> String? {
|
||||
guard !isGroup else { return nil }
|
||||
return members.first(where: { $0.userId != currentUserId })?.userId
|
||||
}
|
||||
|
||||
static func == (lhs: Conversation, rhs: Conversation) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
struct ConversationMember: Identifiable, Equatable, Codable {
|
||||
let userId: String
|
||||
var username: String
|
||||
var email: String
|
||||
|
||||
var id: String { userId }
|
||||
}
|
||||
43
ios_client/EncryptedChat/Models/DeviceBundle.swift
Normal file
43
ios_client/EncryptedChat/Models/DeviceBundle.swift
Normal file
@@ -0,0 +1,43 @@
|
||||
import Foundation
|
||||
|
||||
/// Key bundle for one device, used in X3DH
|
||||
struct DeviceBundle {
|
||||
let deviceId: String
|
||||
let identityKey: Data // Ed25519 public key (32 bytes)
|
||||
let spk: Data // X25519 public key (32 bytes)
|
||||
let spkSignature: Data // Ed25519 signature (64 bytes)
|
||||
let spkId: String
|
||||
let opk: Data? // X25519 public key (32 bytes), optional
|
||||
let opkId: String?
|
||||
|
||||
/// Parse from server response dictionary
|
||||
static func fromDict(_ dict: [String: Any]) throws -> DeviceBundle {
|
||||
guard let deviceId = dict["device_id"] as? String,
|
||||
let ikHex = dict["identity_key"] as? String,
|
||||
let ik = Data(hexString: ikHex),
|
||||
let spkHex = dict["spk"] as? String,
|
||||
let spk = Data(hexString: spkHex),
|
||||
let spkSigHex = dict["spk_signature"] as? String,
|
||||
let spkSig = Data(hexString: spkSigHex),
|
||||
let spkId = dict["spk_id"] as? String else {
|
||||
throw ChatError.invalidData("Invalid device bundle")
|
||||
}
|
||||
|
||||
var opk: Data?
|
||||
var opkId: String?
|
||||
if let opkHex = dict["opk"] as? String, let opkData = Data(hexString: opkHex) {
|
||||
opk = opkData
|
||||
opkId = dict["opk_id"] as? String
|
||||
}
|
||||
|
||||
return DeviceBundle(
|
||||
deviceId: deviceId,
|
||||
identityKey: ik,
|
||||
spk: spk,
|
||||
spkSignature: spkSig,
|
||||
spkId: spkId,
|
||||
opk: opk,
|
||||
opkId: opkId
|
||||
)
|
||||
}
|
||||
}
|
||||
9
ios_client/EncryptedChat/Models/Invitation.swift
Normal file
9
ios_client/EncryptedChat/Models/Invitation.swift
Normal file
@@ -0,0 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
struct Invitation: Identifiable {
|
||||
let id: String // invitation id (from server) or conversationId
|
||||
let conversationId: String
|
||||
let conversationName: String
|
||||
let invitedBy: String
|
||||
let invitedByUsername: String
|
||||
}
|
||||
33
ios_client/EncryptedChat/Models/Message.swift
Normal file
33
ios_client/EncryptedChat/Models/Message.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
import Foundation
|
||||
|
||||
struct Message: Identifiable, Equatable {
|
||||
let id: String
|
||||
let conversationId: String
|
||||
let senderId: String
|
||||
var senderUsername: String
|
||||
let createdAt: Date
|
||||
var text: String?
|
||||
var replyTo: String?
|
||||
var imageFileId: String?
|
||||
var file: FileInfo?
|
||||
var isDeleted: Bool
|
||||
var readBy: Set<String>
|
||||
|
||||
/// Whether this is a self-sent message
|
||||
func isMine(currentUserId: String) -> Bool {
|
||||
senderId == currentUserId
|
||||
}
|
||||
|
||||
static func == (lhs: Message, rhs: Message) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
struct FileInfo: Equatable, Codable {
|
||||
let fileId: String
|
||||
let aesKey: String // hex
|
||||
let iv: String // hex
|
||||
let filename: String
|
||||
let size: Int
|
||||
let mimeType: String
|
||||
}
|
||||
19
ios_client/EncryptedChat/Models/User.swift
Normal file
19
ios_client/EncryptedChat/Models/User.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
import Foundation
|
||||
|
||||
struct User: Identifiable, Equatable {
|
||||
let id: String
|
||||
var username: String
|
||||
var email: String
|
||||
var identityKey: Data? // Ed25519 public key (32 bytes)
|
||||
}
|
||||
|
||||
struct UserProfile: Equatable {
|
||||
var userId: String
|
||||
var username: String?
|
||||
var email: String?
|
||||
var phone: String?
|
||||
var phoneVisible: Bool
|
||||
var location: String?
|
||||
var locationVisible: Bool
|
||||
var avatarFile: String?
|
||||
}
|
||||
Reference in New Issue
Block a user