mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 20:45:19 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94e19dc039 | ||
|
|
438357486f | ||
|
|
4a382716bb | ||
|
|
1563b06d73 | ||
|
|
abb7489e2f | ||
|
|
2bc875dee5 | ||
|
|
588cef72ce |
@@ -21,7 +21,7 @@ final class BitchatMessage: Codable {
|
||||
let originalSender: String?
|
||||
let isPrivate: Bool
|
||||
let recipientNickname: String?
|
||||
let senderPeerID: String?
|
||||
let senderPeer: Peer?
|
||||
let mentions: [String]? // Array of mentioned nicknames
|
||||
var deliveryStatus: DeliveryStatus? // Delivery tracking
|
||||
|
||||
@@ -39,10 +39,23 @@ final class BitchatMessage: Codable {
|
||||
// Codable implementation
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id, sender, content, timestamp, isRelay, originalSender
|
||||
case isPrivate, recipientNickname, senderPeerID, mentions, deliveryStatus
|
||||
case isPrivate, recipientNickname, mentions, deliveryStatus
|
||||
case senderPeer = "senderPeerID" // backwards compatibility
|
||||
}
|
||||
|
||||
init(id: String? = nil, sender: String, content: String, timestamp: Date, isRelay: Bool, originalSender: String? = nil, isPrivate: Bool = false, recipientNickname: String? = nil, senderPeerID: String? = nil, mentions: [String]? = nil, deliveryStatus: DeliveryStatus? = nil) {
|
||||
init(
|
||||
id: String? = nil,
|
||||
sender: String,
|
||||
content: String,
|
||||
timestamp: Date,
|
||||
isRelay: Bool,
|
||||
originalSender: String? = nil,
|
||||
isPrivate: Bool = false,
|
||||
recipientNickname: String? = nil,
|
||||
senderPeer: Peer? = nil,
|
||||
mentions: [String]? = nil,
|
||||
deliveryStatus: DeliveryStatus? = nil
|
||||
) {
|
||||
self.id = id ?? UUID().uuidString
|
||||
self.sender = sender
|
||||
self.content = content
|
||||
@@ -51,7 +64,7 @@ final class BitchatMessage: Codable {
|
||||
self.originalSender = originalSender
|
||||
self.isPrivate = isPrivate
|
||||
self.recipientNickname = recipientNickname
|
||||
self.senderPeerID = senderPeerID
|
||||
self.senderPeer = senderPeer
|
||||
self.mentions = mentions
|
||||
self.deliveryStatus = deliveryStatus ?? (isPrivate ? .sending : nil)
|
||||
}
|
||||
@@ -69,7 +82,7 @@ extension BitchatMessage: Equatable {
|
||||
lhs.originalSender == rhs.originalSender &&
|
||||
lhs.isPrivate == rhs.isPrivate &&
|
||||
lhs.recipientNickname == rhs.recipientNickname &&
|
||||
lhs.senderPeerID == rhs.senderPeerID &&
|
||||
lhs.senderPeer == rhs.senderPeer &&
|
||||
lhs.mentions == rhs.mentions &&
|
||||
lhs.deliveryStatus == rhs.deliveryStatus
|
||||
}
|
||||
@@ -101,7 +114,7 @@ extension BitchatMessage {
|
||||
if isPrivate { flags |= 0x02 }
|
||||
if originalSender != nil { flags |= 0x04 }
|
||||
if recipientNickname != nil { flags |= 0x08 }
|
||||
if senderPeerID != nil { flags |= 0x10 }
|
||||
if senderPeer != nil { flags |= 0x10 }
|
||||
if mentions != nil && !mentions!.isEmpty { flags |= 0x20 }
|
||||
|
||||
data.append(flags)
|
||||
@@ -151,7 +164,7 @@ extension BitchatMessage {
|
||||
data.append(recipData.prefix(255))
|
||||
}
|
||||
|
||||
if let senderPeerID = senderPeerID, let peerData = senderPeerID.data(using: .utf8) {
|
||||
if let peerData = senderPeer?.data {
|
||||
data.append(UInt8(min(peerData.count, 255)))
|
||||
data.append(peerData.prefix(255))
|
||||
}
|
||||
@@ -264,11 +277,11 @@ extension BitchatMessage {
|
||||
}
|
||||
}
|
||||
|
||||
var senderPeerID: String?
|
||||
var senderPeer: Peer?
|
||||
if hasSenderPeerID && offset < dataCopy.count {
|
||||
let length = Int(dataCopy[offset]); offset += 1
|
||||
if offset + length <= dataCopy.count {
|
||||
senderPeerID = String(data: dataCopy[offset..<offset+length], encoding: .utf8)
|
||||
senderPeer = Peer(data: dataCopy[offset..<offset+length])
|
||||
offset += length
|
||||
}
|
||||
}
|
||||
@@ -302,7 +315,7 @@ extension BitchatMessage {
|
||||
originalSender: originalSender,
|
||||
isPrivate: isPrivate,
|
||||
recipientNickname: recipientNickname,
|
||||
senderPeerID: senderPeerID,
|
||||
senderPeer: senderPeer,
|
||||
mentions: mentions
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Peer.swift
|
||||
// BitLogger
|
||||
//
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// For more information, see <https://unlicense.org>
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Peer: Equatable, Hashable {
|
||||
let id: String
|
||||
}
|
||||
|
||||
extension Peer {
|
||||
var data: Data? {
|
||||
id.data(using: .utf8)
|
||||
}
|
||||
|
||||
var isNostr: Bool {
|
||||
id.hasPrefix("nostr")
|
||||
}
|
||||
|
||||
var isNostrColon: Bool {
|
||||
id.hasPrefix("nostr:")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ExpressibleByStringLiteral
|
||||
|
||||
extension Peer: ExpressibleByStringLiteral {
|
||||
init(stringLiteral value: String) {
|
||||
self.init(str: value)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ExpressibleByStringInterpolation
|
||||
|
||||
extension Peer: ExpressibleByStringInterpolation {
|
||||
init(extendedGraphemeClusterLiteral value: String) {
|
||||
self.init(str: value)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Codable
|
||||
|
||||
extension Peer: Codable {
|
||||
init(from decoder: any Decoder) throws {
|
||||
id = try decoder.singleValueContainer().decode(String.self)
|
||||
}
|
||||
|
||||
func encode(to encoder: any Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(id)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience Inits
|
||||
|
||||
extension Peer {
|
||||
init(str: String) {
|
||||
id = str.lowercased()
|
||||
}
|
||||
|
||||
init(str: String.SubSequence) {
|
||||
self.init(str: String(str))
|
||||
}
|
||||
|
||||
init?(data: Data) {
|
||||
guard let str = String(data: data, encoding: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
self.init(str: str)
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ final class PrivateChatManager: ObservableObject {
|
||||
originalSender: nil,
|
||||
isPrivate: true,
|
||||
recipientNickname: peerNickname,
|
||||
senderPeerID: meshService.myPeerID,
|
||||
senderPeer: Peer(str: meshService.myPeerID),
|
||||
mentions: nil,
|
||||
deliveryStatus: .sending
|
||||
)
|
||||
@@ -94,7 +94,7 @@ final class PrivateChatManager: ObservableObject {
|
||||
|
||||
/// Handle incoming private message
|
||||
func handleIncomingMessage(_ message: BitchatMessage) {
|
||||
guard let senderPeerID = message.senderPeerID else { return }
|
||||
guard let senderPeerID = message.senderPeer?.id else { return }
|
||||
|
||||
// Initialize chat if needed
|
||||
if privateChats[senderPeerID] == nil {
|
||||
@@ -161,7 +161,7 @@ final class PrivateChatManager: ObservableObject {
|
||||
// Send read receipts for unread messages that haven't been sent yet
|
||||
if let messages = privateChats[peerID] {
|
||||
for message in messages {
|
||||
if message.senderPeerID == peerID && !message.isRelay && !sentReadReceipts.contains(message.id) {
|
||||
if message.senderPeer?.id == peerID && !message.isRelay && !sentReadReceipts.contains(message.id) {
|
||||
sendReadReceipt(for: message)
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ final class PrivateChatManager: ObservableObject {
|
||||
|
||||
private func sendReadReceipt(for message: BitchatMessage) {
|
||||
guard !sentReadReceipts.contains(message.id),
|
||||
let senderPeerID = message.senderPeerID else {
|
||||
let senderPeerID = message.senderPeer?.id else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -459,7 +459,7 @@ struct ContentView: View {
|
||||
if let name = viewModel.meshService.peerNickname(peerID: peerID) {
|
||||
selectedMessageSender = name
|
||||
} else {
|
||||
selectedMessageSender = viewModel.messages.last(where: { $0.senderPeerID == peerID && $0.sender != "system" })?.sender
|
||||
selectedMessageSender = viewModel.messages.last(where: { $0.senderPeer?.id == peerID && $0.sender != "system" })?.sender
|
||||
}
|
||||
}
|
||||
if viewModel.isSelfSender(peerID: selectedMessageSenderID, displayName: selectedMessageSender) {
|
||||
|
||||
@@ -134,7 +134,7 @@ final class BLEServiceTests: XCTestCase {
|
||||
originalSender: nil,
|
||||
isPrivate: false,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: "REMOTE123",
|
||||
senderPeer: "REMOTE123",
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
@@ -161,7 +161,7 @@ final class BLEServiceTests: XCTestCase {
|
||||
originalSender: nil,
|
||||
isPrivate: false,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: "PACKET123",
|
||||
senderPeer: "PACKET123",
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
@@ -243,7 +243,7 @@ final class BLEServiceTests: XCTestCase {
|
||||
originalSender: nil,
|
||||
isPrivate: false,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: "TEST123",
|
||||
senderPeer: "TEST123",
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ final class PublicChatE2ETests: XCTestCase {
|
||||
originalSender: message.sender,
|
||||
isPrivate: message.isPrivate,
|
||||
recipientNickname: message.recipientNickname,
|
||||
senderPeerID: message.senderPeerID,
|
||||
senderPeerID: message.senderPeer?.id,
|
||||
mentions: message.mentions
|
||||
)
|
||||
|
||||
@@ -439,7 +439,7 @@ final class PublicChatE2ETests: XCTestCase {
|
||||
|
||||
if let message = BitchatMessage(packet.payload) {
|
||||
// Don't relay own messages
|
||||
guard message.senderPeerID != node.peerID else { return }
|
||||
guard message.senderPeer?.id != node.peerID else { return }
|
||||
|
||||
// Create relay message
|
||||
let relayMessage = BitchatMessage(
|
||||
@@ -451,7 +451,7 @@ final class PublicChatE2ETests: XCTestCase {
|
||||
originalSender: message.isRelay ? message.originalSender : message.sender,
|
||||
isPrivate: message.isPrivate,
|
||||
recipientNickname: message.recipientNickname,
|
||||
senderPeerID: message.senderPeerID,
|
||||
senderPeerID: message.senderPeer?.id,
|
||||
mentions: message.mentions
|
||||
)
|
||||
|
||||
|
||||
@@ -627,7 +627,7 @@ final class IntegrationTests: XCTestCase {
|
||||
guard packet.ttl > 1 else { return }
|
||||
|
||||
if let message = BitchatMessage(packet.payload) {
|
||||
guard message.senderPeerID != node.peerID else { return }
|
||||
guard message.senderPeer?.id != node.peerID else { return }
|
||||
|
||||
let relayMessage = BitchatMessage(
|
||||
id: message.id,
|
||||
@@ -638,7 +638,7 @@ final class IntegrationTests: XCTestCase {
|
||||
originalSender: message.isRelay ? message.originalSender : message.sender,
|
||||
isPrivate: message.isPrivate,
|
||||
recipientNickname: message.recipientNickname,
|
||||
senderPeerID: message.senderPeerID,
|
||||
senderPeerID: message.senderPeer?.id,
|
||||
mentions: message.mentions
|
||||
)
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ final class MockBLEService: NSObject {
|
||||
let nextTTL = packet.ttl > 0 ? packet.ttl - 1 : 0
|
||||
for neighbor in neighbors() {
|
||||
// Avoid immediate echo loopback to sender if known
|
||||
if let sender = message.senderPeerID, sender == neighbor.peerID { continue }
|
||||
if let sender = message.senderPeer?.id, sender == neighbor.peerID { continue }
|
||||
var relay = packet
|
||||
relay.ttl = nextTTL
|
||||
neighbor.simulateIncomingPacket(relay)
|
||||
|
||||
@@ -204,7 +204,7 @@ final class BinaryProtocolTests: XCTestCase {
|
||||
|
||||
XCTAssertEqual(decodedMessage.content, message.content)
|
||||
XCTAssertEqual(decodedMessage.sender, message.sender)
|
||||
XCTAssertEqual(decodedMessage.senderPeerID, message.senderPeerID)
|
||||
XCTAssertEqual(decodedMessage.senderPeerID, message.senderPeer?.id)
|
||||
XCTAssertEqual(decodedMessage.isPrivate, message.isPrivate)
|
||||
|
||||
// Timestamp should be close (within 1 second due to conversion)
|
||||
|
||||
@@ -44,7 +44,7 @@ final class TestHelpers {
|
||||
originalSender: nil,
|
||||
isPrivate: isPrivate,
|
||||
recipientNickname: recipientNickname,
|
||||
senderPeerID: senderPeerID,
|
||||
senderPeer: senderPeerID,
|
||||
mentions: mentions
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user