118 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			118 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
import Foundation
 | 
						|
import Combine
 | 
						|
 | 
						|
final class IncomingMessageCenter: ObservableObject {
 | 
						|
    @Published private(set) var banner: IncomingMessageBanner?
 | 
						|
    @Published var presentedChat: PrivateChatListItem?
 | 
						|
    var currentUserId: String?
 | 
						|
    var activeChatId: String?
 | 
						|
 | 
						|
    private var dismissWorkItem: DispatchWorkItem?
 | 
						|
    private var cancellables = Set<AnyCancellable>()
 | 
						|
    private let notificationCenter: NotificationCenter
 | 
						|
 | 
						|
    init(notificationCenter: NotificationCenter = .default) {
 | 
						|
        self.notificationCenter = notificationCenter
 | 
						|
        notificationCenter.publisher(for: .socketDidReceivePrivateMessage)
 | 
						|
            .compactMap { $0.object as? MessageItem }
 | 
						|
            .receive(on: RunLoop.main)
 | 
						|
            .sink { [weak self] message in
 | 
						|
                self?.handleIncoming(message)
 | 
						|
            }
 | 
						|
            .store(in: &cancellables)
 | 
						|
    }
 | 
						|
 | 
						|
    func dismissBanner() {
 | 
						|
        dismissWorkItem?.cancel()
 | 
						|
        dismissWorkItem = nil
 | 
						|
        banner = nil
 | 
						|
    }
 | 
						|
 | 
						|
    func openCurrentChat() {
 | 
						|
        guard let banner else { return }
 | 
						|
        activeChatId = banner.message.chatId
 | 
						|
        let chatItem = makeChatItem(from: banner.message)
 | 
						|
        presentedChat = chatItem
 | 
						|
        dismissBanner()
 | 
						|
    }
 | 
						|
 | 
						|
    private func handleIncoming(_ message: MessageItem) {
 | 
						|
        if let currentUserId, message.senderId == currentUserId {
 | 
						|
            return
 | 
						|
        }
 | 
						|
 | 
						|
        if let activeChatId, activeChatId == message.chatId {
 | 
						|
            return
 | 
						|
        }
 | 
						|
 | 
						|
        if let presentedChat,
 | 
						|
           presentedChat.chatId == message.chatId {
 | 
						|
            return
 | 
						|
        }
 | 
						|
 | 
						|
        banner = buildBanner(from: message)
 | 
						|
        scheduleDismiss()
 | 
						|
    }
 | 
						|
 | 
						|
    private func buildBanner(from message: MessageItem) -> IncomingMessageBanner {
 | 
						|
        let sender = senderName(for: message)
 | 
						|
        let preview = messagePreview(for: message)
 | 
						|
        return IncomingMessageBanner(message: message, senderName: sender, messagePreview: preview)
 | 
						|
    }
 | 
						|
 | 
						|
    private func senderName(for message: MessageItem) -> String {
 | 
						|
        let candidates: [String?] = [
 | 
						|
            message.senderData?.customName,
 | 
						|
            message.senderData?.fullName,
 | 
						|
            message.senderData?.login.map { "@\($0)" }
 | 
						|
        ]
 | 
						|
 | 
						|
        for candidate in candidates {
 | 
						|
            if let value = candidate?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty {
 | 
						|
                return value
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return message.senderId
 | 
						|
    }
 | 
						|
 | 
						|
    private func messagePreview(for message: MessageItem) -> String {
 | 
						|
        if let content = message.content?.trimmingCharacters(in: .whitespacesAndNewlines), !content.isEmpty {
 | 
						|
            return content
 | 
						|
        }
 | 
						|
 | 
						|
        switch message.messageType.lowercased() {
 | 
						|
        case "image":
 | 
						|
            return NSLocalizedString("Изображение", comment: "Image message placeholder")
 | 
						|
        case "audio":
 | 
						|
            return NSLocalizedString("Аудио", comment: "Audio message placeholder")
 | 
						|
        case "video":
 | 
						|
            return NSLocalizedString("Видео", comment: "Video message placeholder")
 | 
						|
        default:
 | 
						|
            return NSLocalizedString("Новое сообщение", comment: "Default banner subtitle")
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private func makeChatItem(from message: MessageItem) -> PrivateChatListItem {
 | 
						|
        let profile = message.senderData
 | 
						|
        return PrivateChatListItem(
 | 
						|
            chatId: message.chatId,
 | 
						|
            chatType: .privateChat,
 | 
						|
            chatData: profile,
 | 
						|
            lastMessage: message,
 | 
						|
            createdAt: message.createdAt,
 | 
						|
            unreadCount: 0
 | 
						|
        )
 | 
						|
    }
 | 
						|
 | 
						|
    private func scheduleDismiss(after delay: TimeInterval = 5) {
 | 
						|
        dismissWorkItem?.cancel()
 | 
						|
        let workItem = DispatchWorkItem { [weak self] in
 | 
						|
            self?.banner = nil
 | 
						|
            self?.dismissWorkItem = nil
 | 
						|
        }
 | 
						|
        dismissWorkItem = workItem
 | 
						|
        DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem)
 | 
						|
    }
 | 
						|
}
 |