好友通知列表,接受or拒绝,好友列表protobuf消息
好友通知列表效果
有新的好友通知,如QQ效果,右侧新好友右侧会显示有多少好友通知没有处理的数量。
protobuf消息
好友通知列表
//好友通知消息
message FriendNotice
{
string notice_id = 1; //通知id
int64 initiator_code = 2; //发起者code
int64 invitee_code = 3; //被邀请者code
UserInfo friend_info = 4; //朋友信息
EInviteStatus invite_status = 5; //邀请状态
string remark = 6; //邀请时备注
string message = 7; //留言
uint64 created_time = 8; //创建时间
EReadStatus initiator_status = 9; //发起者读取状态
EReadStatus invitee_status = 10; //被邀请者的读取状态 //客户端使用
EInviteType invite_type = 11; //邀请类型
}
//server ---> client 服务器主动下发好友通知信息
message FriendNoticeList
{
repeated FriendNotice notices = 1; //朋友通知消息
}
接受好友邀请
//client <---> server 接受好友申请
message FriendAddAccept
{
uint64 request_id = 1; //请求序列
string notice_id = 2; //好友通知id
bool accept = 3; //接受还是拒绝
}
好友列表响应
//server ---> client 好友列表响应
message FriendListResponse
{
repeated UserFriend friends = 1; //此用户的所有朋友
}
好友通知列表显示
好友通知列表类设计
/*****************************************
* 作者: Chris
* 日期: 2024-04-12
* 功能:朋友通知列表
* ***************************************/
#ifndef FRIEND_NOTICE_LIST__H
#define FRIEND_NOTICE_LIST__H
#include "ui_friend_notice_list.h"
#include "model/user_info.h"
#include "model/friend_notice.h"
#include "widget/friend_item_control.h" //朋友列表item控件
#include "proto/common.pb.h"
#include "proto/host_internal.pb.h"
#include <QWidget>
#include <QListWidget>
#include <QListWidgetItem>
#include <QPushButton>
namespace Ui
{
class FriendNoticeList;
}
//联系人树节点数据自定义
struct FriendNoticeItemData
{
bool group; //是否为分组
uint64_t code; //分组id
uint64_t parentCode; //父级code
};
Q_DECLARE_METATYPE(FriendNoticeItemData);
class FriendNoticeControl;
class FriendNoticeList : public QWidget
{
Q_OBJECT
public:
explicit FriendNoticeList(const std::shared_ptr<client::IpcThread>& ipcThread, QWidget* parent = nullptr);
~FriendNoticeList();
//刷新朋友通知列表
void onFriendNoticeList(const proto::FriendNoticeList& friendNoticeList);
//更新某个朋友个人信息
void onFriendInfoResponse(const proto::FriendInfoResponse& response);
//更新某个朋友的通知信息, 或许邀请状态发生了改变
void onFriendNoticeResponse(const proto::FriendNoticeResponse& response);
//接受好友邀请的响应
void onFriendAcceptResponse(const proto::FriendAcceptResponse& response);
//设置用户信息
void setUserInfo(const UserInfoPtr& userInfo) { userInfo_ = userInfo; }
signals:
//UI线程信号, ---> ipc thread
void sendMessageToIpc(proto::internal::UiToService& message);
void newFriendNotice(uint32_t count);
public slots: //添加好友请求
//void requestSearchFriend(); //发起搜好友的请求
protected:
//void resizeEvent(QResizeEvent* e) override; //尺寸变化
private:
void updateListWidget();
void clearWidgets(QLayout* layout);
void initWindow(); //初始化窗体
void initButtonIcon();
Ui::FriendNoticeList* ui = new Ui::FriendNoticeList;
//----------------- 自定义控件 ------------------------------
QListWidgetItem* currentItem_; //当前的项
QList<QAbstractButton*> btns_; //按钮s
QList<int> btnIcons_; //按钮的ICONs
std::shared_ptr<client::IpcThread> ipcThread_;
UserInfoPtr userInfo_ = nullptr; //用户信息
std::vector<base::FriendNotice> notices_; //朋友通知列表
std::vector<FriendNoticeControl*> noticeControlList_; //设备列表
};
#endif // FRIEND_NOTICE_LIST__H
//朋友通知列表处理
void FriendNoticeList::onFriendNoticeList(const proto::FriendNoticeList& friendNoticeList)
{
notices_.clear();
int count = friendNoticeList.notices_size();
uint32_t newNoticeCount = 0;
LOG(LS_INFO) << "onFriendNoticeList friend notice count: " << count;
for (int i = 0; i < count; i++) {
base::FriendNotice notice = base::FriendNotice::kInvalidNotice;
notice = base::FriendNotice::parseFrom(friendNoticeList.notices(i));
if (friendNoticeList.notices(i).invite_type() == proto::EInviteType::INVITEE && friendNoticeList.notices(i).invitee_status() == proto::EReadStatus::READ_WAIT)
newNoticeCount++;
else if (friendNoticeList.notices(i).invite_type() == proto::EInviteType::INITIATOR && friendNoticeList.notices(i).initiator_status() == proto::EReadStatus::READ_WAIT)
newNoticeCount++;
notices_.emplace_back(std::move(notice));
}
//更新好友通知新消息数量
emit newFriendNotice(newNoticeCount);
ui->listWidget->clear();
noticeControlList_.clear();
update();
updateListWidget();
}
void FriendNoticeList::updateListWidget()
{
LOG(LS_INFO) << "updateListWidget noticeControlList_ size: " << noticeControlList_.size() << ", listWidget size: " << ui->listWidget->count();
//将设备添加到设备列表中
size_t listCout = notices_.size();
if (listCout <= 0) {
return;
}
for (int i = 0; i < listCout; i++) {
FriendNoticeControl* noticeControl = new FriendNoticeControl(this, i, notices_[i].inviteType);
noticeControl->setFriendNotice(notices_[i]);
//noticeControl->setFixedHeight(60);
noticeControl->update();
//接受邀请成为好友
connect(noticeControl, &FriendNoticeControl::friendAddFeedback, this, [&](int index, bool result) {
if (index >= notices_.size())
return;
proto::internal::UiToService message;
proto::FriendAddAccept* accept = message.mutable_friend_add_accept();
accept->set_request_id(base::Random::number64());
accept->set_notice_id(notices_[index].noticeId);
accept->set_accept(result);
emit sendMessageToIpc(message);
});
noticeControlList_.emplace_back(noticeControl);
//关键代码
QListWidgetItem* newItem = new QListWidgetItem(); //创建一个newItem
newItem->setSizeHint(QSize(80, 80)); //每次改变Item的高度
ui->listWidget->addItem(newItem);
ui->listWidget->setItemWidget(newItem, noticeControl);
}
update();
}
//初始化窗体
void FriendNoticeList::initWindow()
{
//1. 标题
ui->titleLabel->setText(tr("New friend notice"));
//2. 过滤显示
ui->combox->addItem(tr("Wait"), proto::EInviteStatus::INVITE_WAIT);
ui->combox->addItem(tr("Agreed"), proto::EInviteStatus::INVITE_AGREED);
ui->combox->addItem(tr("Reject"), proto::EInviteStatus::INVITE_REJECT);
ui->combox->setStyleSheet("QComboBox QAbstractItemView { background-color: white; }");
//3. 按钮样式-- 字体图标/颜色/hover和checked样式等
initButtonIcon();
//4. listWidget样式设置, 去掉默认的hover和选中的背景色改变
ui->listWidget->setStyleSheet("QListWidget{border: none; background-color: transparent; }"
"QListWidget::Item:hover{background:transparent; }"
"QListWidget::item:selected{background:transparent; }"
//"QListWidget::item:selected:!active{border-width:0px; background:lightgreen; }"
);
}
//初始化工具条按钮的图标, 样式设置等
void FriendNoticeList::initButtonIcon()
{
//字体图标: fa-trash-o 0xf104 黑色
ui->btnClear->setText("");
btnIcons_ << 0xf014;
btns_ << ui->btnClear;
//左侧导航样式,可以设置各种牛逼的参数,超级棒
IconHelper::StyleColor styleColor;
styleColor.iconSize = 18;
styleColor.iconWidth = 25;
styleColor.iconHeight = 25;
styleColor.position = "left";
styleColor.borderColor = "#F5F5F5";
styleColor.borderWidth = 0;
styleColor.hoverTextColor = "#4169E1"; //hover的颜色---#4169E1 royalblue 蓝色
styleColor.hoverBgColor = "#F5F5F5"; //背景色---#F5F5F5 wihtesmoke 烟白色
styleColor.normalBgColor = "#F5F5F5"; //背景色---#F5F5F5 wihtesmoke 烟白色
styleColor.normalTextColor = "#000000"; //正常字体颜色: #000000 黑色
styleColor.checkedBgColor = "#F5F5F5"; //选中的背景---烟白色
styleColor.checkedTextColor = "#4169E1"; //选中的字体---#4169E1 royalblue 蓝色
//styleColor.setColor(QUIConfig::NormalColorEnd, QUIConfig::TextColor, QUIConfig::PanelColor, QUIConfig::TextColor);
//1. 正常的背景色: #F5F5F5 wihtesmoke 烟白色; 2. 正常的字体图标和文字的颜色, #000000 黑色; 3. 整个按钮的颜色, 与背景同色; 4. 选中或按下的字体颜色 #4169E1 royalblue 蓝色
styleColor.setColor("#F5F5F5", "#000000", "#F5F5F5", "#4169E1");
IconHelper::setStyle(this, btns_, btnIcons_, styleColor); //将widget中所有按钮都设置成了同样的样式
//设置为无边框
ui->btnClear->setStyleSheet("border:none");
}
自定义QListWidgetItem展示好友通知信息,根据通知类型删除不必要的QWidget item
/*****************************************
* 作者: Chris
* 日期: 2024-03-19
* 功能:好友通知QListWidgetItem控件
* ***************************************/
#ifndef FRIEND_NOTICE_CONTROL__H
#define FRIEND_NOTICE_CONTROL__H
#include "ui_friend_notice_control.h"
#include "model/user_info.h"
#include "model/friend_notice.h"
#include "proto/common.pb.h"
#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
#include <QEvent>
namespace Ui
{
class FriendNoticeControl;
}
class UserInfoDialog;
class FriendNoticeControl : public QWidget
{
Q_OBJECT
public:
explicit FriendNoticeControl(QWidget* parent, int index, proto::EInviteType type);
~FriendNoticeControl();
void setFriendNotice(const base::FriendNotice& notice);
void setSelected(bool selected); //选中状态设置
private slots:
signals:
void clicked(int index); //设备控件被选择信号
void friendAddFeedback(int index, bool agree);
void requestFriendInfo(int index); //点击头像主动请求下friend信息
protected:
virtual void mousePressEvent(QMouseEvent* event);//设备左键选中重写
bool eventFilter(QObject* object, QEvent* event) override;
private:
void setHeaderImage(const std::string& headerImage);
void onHeaderClicked(); //点击头像和人名, 弹出显示个人信息
Ui::FriendNoticeControl* ui = new Ui::FriendNoticeControl();
std::unique_ptr <UserInfoDialog> userInfoDialog_ = nullptr;
int index_;
base::UserInfo userInfo_;
};
#endif // FRIEND_NOTICE_CONTROL__H
// 构造函数
FriendNoticeControl::FriendNoticeControl(QWidget* parent, int index, proto::EInviteType type) :
QWidget(parent),
index_(index)
{
ui->setupUi(this);
setStyleSheet("background-color: white; border-radius: 12px;"
// "QPushButton#btnAgree, QPushButton#btnReject{background: transparent; border: 1px solid lightgray; border-radius: 5px; height: 20px;}"
);
if (type == proto::EInviteType::INVITEE) {
//同意删除按钮
ui->btnAgree->setCheckable(true);
ui->btnAgree->setCursor(Qt::PointingHandCursor); //鼠标hover状态改变鼠标形状为小手
ui->btnReject->setCheckable(true);
ui->btnReject->setCursor(Qt::PointingHandCursor); //鼠标hover状态改变鼠标形状为小手
connect(ui->btnAgree, &QPushButton::clicked, this, [&]() {
emit friendAddFeedback(index_, true);
});
connect(ui->btnReject, &QPushButton::clicked, this, [&]() {
emit friendAddFeedback(index_, false);
});
}
else {
//主动邀请, 删除同意和拒绝按钮
QList<QPushButton*> btns = this->findChildren<QPushButton*>();
foreach(QPushButton * btn, btns)
{
if ("btnAgree" == btn->objectName() || "btnReject" == btn->objectName()) {
btn->deleteLater();
}
}
}
ui->nickNameLabel->setStyleSheet("color: royalblue");
ui->headerLabel->installEventFilter(this); //1.给label安装事件过滤器
ui->nickNameLabel->installEventFilter(this); //1.给label安装事件过滤器
ui->createdDateLabel->setStyleSheet("color: silver");
}
// 析构函数
FriendNoticeControl::~FriendNoticeControl()
{
delete ui;
}
//设置好友通知
void FriendNoticeControl::setFriendNotice(const base::FriendNotice& notice)
{
//保存用户信息
userInfo_ = notice.friendInfo;
//头像
setHeaderImage(notice.friendInfo.headerImage);
//昵称
ui->nickNameLabel->setText(QString::fromStdU16String(notice.friendInfo.nickName));
//状态不是wait则删除两个按钮
if (notice.inviteStatus != proto::EInviteStatus::INVITE_WAIT) {
//主动邀请, 删除同意和拒绝按钮
QList<QPushButton*> btns = this->findChildren<QPushButton*>();
foreach(QPushButton * btn, btns)
{
if ("btnAgree" == btn->objectName() || "btnReject" == btn->objectName()) {
btn->deleteLater();
}
}
}
//邀请信息
QString gender;
if (notice.friendInfo.gender == proto::GenderType::MALE)
gender = tr("his");
else
gender = tr("her");
QString inviteInfo;
if (notice.inviteType == proto::EInviteType::INITIATOR) { //主动邀请
if (notice.inviteStatus == proto::EInviteStatus::INVITE_WAIT) {
inviteInfo += tr(" is verifying your friend request");
ui->waitLabel->setText(tr("Wait"));
}
else if (notice.inviteStatus == proto::EInviteStatus::INVITE_AGREED) {
inviteInfo += tr(" agreed to your friend request");
ui->waitLabel->setText(tr("Agreed"));
}
else if (notice.inviteStatus == proto::EInviteStatus::INVITE_REJECT) {
inviteInfo += tr(" rejected your friend request");
ui->waitLabel->setText(tr("Rejected"));
}
}
else if (notice.inviteType == proto::EInviteType::INVITEE) { //被邀请
inviteInfo += tr(" request to add as a friend");
//被邀请者如果状态为wait则删除等待label
if (notice.inviteStatus == proto::EInviteStatus::INVITE_WAIT) {
QList<QLabel*> labels = this->findChildren<QLabel*>();
foreach(QLabel * label, labels)
{
if ("waitLabel" == label->objectName()) {
label->deleteLater();
}
}
}
else if (notice.inviteStatus == proto::EInviteStatus::INVITE_AGREED) {
ui->waitLabel->setText(tr("Agreed"));
}
else if (notice.inviteStatus == proto::EInviteStatus::INVITE_REJECT) {
ui->waitLabel->setText(tr("Rejected"));
}
}
else {
}
ui->inviteInfoLabel->setText(inviteInfo);
//创建时间
QDateTime createdTime = QDateTime::fromMSecsSinceEpoch(notice.createdTime);
ui->createdDateLabel->setText(createdTime.toString("yyyy-MM-dd HH:mm:ss"));
//预留消息
ui->messageLabel->setText(QString::fromStdU16String(notice.message));
}
//头像按钮点击
void FriendNoticeControl::onHeaderClicked()
{
//emit requestFriendInfo(index_);
// 获取按钮的位置和大小
QRect buttonRect = ui->headerLabel->geometry();
// 计算对话框的位置
QPoint dialogPos = ui->headerLabel->mapToGlobal(buttonRect.bottomLeft());
dialogPos.setX(dialogPos.x() + 60);
dialogPos.setY(dialogPos.y());
//创建智能指针的user类
UserInfoPtr user = std::make_shared<base::UserInfo>(userInfo_);
userInfoDialog_.reset(new UserInfoDialog(dialogPos, user, true, true, this));
userInfoDialog_->setModal(false);
userInfoDialog_->setFixedSize(QSize(400, 350));
//dlg.setUserInfo(userInfo_.serialize());
userInfoDialog_->setGeometry(QRect(dialogPos, QSize(400, 350))); // 宽度200,高度100
//非模态对话框自动关闭
connect(userInfoDialog_.get(), &UserInfoDialog::hideDialog, this, [&]() {
if (userInfoDialog_)
userInfoDialog_->hide();
});
this->installEventFilter(userInfoDialog_.get());
userInfoDialog_->show();
}