/* История и composer выбранного канала/PM: модель сообщений, позиция чтения, загрузки и воспроизведение. * Применяет события bridge в GUI-потоке; сетевые операции передаёт ядру. */ #pragma once #include #include #include #include #include #include class ChatView; class InputBar; class EmojiPanel; class DbManager; class ChannelList; class AudioRecorder; class QLabel; class QPushButton; class RadioPanel; class MessageList : public QWidget { Q_OBJECT public: explicit MessageList(DbManager* db = nullptr, AudioRecorder* recorder = nullptr, QWidget *parent = nullptr); void loadChannel(const QString& channelId); void refresh(); void reloadLocalNicks(); void saveChannelState(); static void cleanupAnimations(); void updateMessageProgress(const QString& chId, int64_t msgId, int blocksDone, int numBlocks); void onAttachmentDownloaded(const QString& chId, int64_t msgId); void onDmMessages(const uint8_t* data, int len); void refreshDmCallRoute(); void onDmCallRoute(const uint8_t* data, int len); void clearDmCallRoute(); void onAttachmentPreparation(const uint8_t* data, int len); void onRadioTalk(quint64 groupId, quint64 srcNodeId, bool on); /* индикация говорящего */ signals: void readPositionChanged(const QString& channelId, qint64 lastReadMsgId); void callRequested(const QString& target, quint64 peer, const QString& name); void openVideoRequested(const QString& filePath); void openImageRequested(const QString& filePath); void inviteRequested(const QString& sourceChannel, quint64 peer, const QString& name); void linkActivated(const QString& link); private: ChatView *m_view; QStandardItemModel *m_model; InputBar *m_inputBar; EmojiPanel *m_emojiPanel; DbManager *m_db; QString m_currentChannelId; QHash m_avatarCache; qint64 m_lastReadMsgId = 0; QTimer* m_readDebounce; qint64 m_savedTopMsgId = 0; bool m_wasAtBottom = false; QMap m_localNicks; AudioRecorder* m_recorder = nullptr; QLabel* m_peerStatus = nullptr; QLabel* m_channelTitle = nullptr; /* шапка: название канала */ QLabel* m_talkingLabel = nullptr; /* шапка: кто говорит в рации */ QPushButton* m_callBtn = nullptr; QPushButton* m_inviteBtn = nullptr; quint64 m_callPeer = 0; quint64 m_callGroup = 0; QPushButton* m_radioBtn = nullptr; /* шапка: переключатель рации */ RadioPanel* m_radioPanel = nullptr; /* верхняя панель устройств, VAD и PTT */ bool m_radioOn = false; QThreadPool m_thumbnailPool; QSet m_thumbnailJobs; QString authorName(quint64 nodeId) const; void restoreScrollPosition(const QString& channelId); void updateReadPosition(); void onMessageContextMenu(const QPoint& pos); void requestDmMessages(const QString& channelId); void prepareVideoThumbnail(const QString& path); void onRadioToggled(bool on); void updateRadioUi(); static bool isDmChannel(const QString& channelId); static QString dmConvId(const QString& channelId); };