完善定时提醒稳定性与管理能力
This commit is contained in:
+263
-14
@@ -24,6 +24,7 @@
|
||||
#include <QContextMenuEvent>
|
||||
#include <QCursor>
|
||||
#include <QDialog>
|
||||
#include <QFrame>
|
||||
#include <QGuiApplication>
|
||||
#include <QHideEvent>
|
||||
#include <QList>
|
||||
@@ -32,6 +33,7 @@
|
||||
#include <QPixmap>
|
||||
#include <QPointF>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
#include <QRandomGenerator>
|
||||
#include <QScreen>
|
||||
#include <QSet>
|
||||
@@ -333,6 +335,7 @@ void PetWindow::resumeAnimation()
|
||||
|
||||
void PetWindow::showBubbleMessage(const QString &message)
|
||||
{
|
||||
hideReminderActions();
|
||||
m_chatBubble->showMessage(message, bubbleAnchorPosition());
|
||||
}
|
||||
|
||||
@@ -352,8 +355,47 @@ void PetWindow::openSettingsDialog()
|
||||
[this](const QString &reminderId, QString *errorMessage) {
|
||||
return m_reminderManager && m_reminderManager->cancelReminder(reminderId, errorMessage);
|
||||
},
|
||||
[this](const QString &reminderId, const QString &title, const QDateTime &remindAt, const ReminderRecurrence &recurrence, ReminderItem *updatedItem, QString *errorMessage) {
|
||||
if (!m_reminderManager)
|
||||
{
|
||||
if (errorMessage != nullptr)
|
||||
{
|
||||
*errorMessage = QStringLiteral("提醒功能初始化失败。");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_reminderManager->updateReminder(reminderId, title, remindAt, recurrence, errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (updatedItem != nullptr)
|
||||
{
|
||||
const QVector<ReminderItem> reminders = m_reminderManager->allReminders();
|
||||
bool found = false;
|
||||
for (const ReminderItem &item : reminders)
|
||||
{
|
||||
if (item.id == reminderId)
|
||||
{
|
||||
*updatedItem = item;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
if (errorMessage != nullptr)
|
||||
{
|
||||
*errorMessage = QStringLiteral("提醒已更新,但没有找到更新后的记录。");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[this](QString *errorMessage) {
|
||||
return m_reminderManager && m_reminderManager->clearFinishedReminders(errorMessage);
|
||||
return m_reminderManager && m_reminderManager->pruneFinishedReminders(20, errorMessage);
|
||||
},
|
||||
[this](const QString &soundId, double volume) {
|
||||
if (m_reminderSoundPlayer)
|
||||
@@ -402,6 +444,10 @@ void PetWindow::activateFromExternalInstance()
|
||||
|
||||
raise();
|
||||
activateWindow();
|
||||
if (m_reminderManager)
|
||||
{
|
||||
m_reminderManager->checkDueRemindersNow();
|
||||
}
|
||||
updateBubblePosition();
|
||||
}
|
||||
|
||||
@@ -410,7 +456,7 @@ void PetWindow::setSettingsFallbackInContextMenuEnabled(bool enabled)
|
||||
m_settingsFallbackInContextMenuEnabled = enabled;
|
||||
}
|
||||
|
||||
void PetWindow::setTrayNotificationCallback(std::function<void(const QString &, const QString &)> callback)
|
||||
void PetWindow::setTrayNotificationCallback(std::function<bool(const QString &, const QString &)> callback)
|
||||
{
|
||||
if (m_notificationDispatcher)
|
||||
{
|
||||
@@ -547,37 +593,240 @@ bool PetWindow::handleReminderChatMessage(const QString &message)
|
||||
}
|
||||
|
||||
void PetWindow::handleTriggeredReminder(const ReminderItem &item)
|
||||
{
|
||||
playReminderSound();
|
||||
|
||||
if (shouldNotifyOnlyForReminder())
|
||||
{
|
||||
showReminderNotification(item);
|
||||
return;
|
||||
}
|
||||
|
||||
enqueueVisibleTriggeredReminder(item);
|
||||
}
|
||||
|
||||
void PetWindow::playReminderSound()
|
||||
{
|
||||
if (m_appConfig.reminderSoundEnabled && m_reminderSoundPlayer)
|
||||
{
|
||||
m_reminderSoundPlayer->play(m_appConfig.reminderSoundId, m_appConfig.reminderSoundVolume);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isVisible())
|
||||
void PetWindow::showReminderNotification(const ReminderItem &item)
|
||||
{
|
||||
if (m_notificationDispatcher)
|
||||
{
|
||||
if (m_notificationDispatcher)
|
||||
const bool shown = m_notificationDispatcher->showReminder(QStringLiteral("定时提醒"), QStringLiteral("到时间啦:%1").arg(item.title));
|
||||
if (!shown)
|
||||
{
|
||||
m_notificationDispatcher->showReminder(QStringLiteral("定时提醒"), QStringLiteral("到时间啦:%1").arg(item.title));
|
||||
Logger::warning(QStringLiteral("Reminder notification backend unavailable: id=%1").arg(item.id));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dragging)
|
||||
Logger::warning(QStringLiteral("Reminder notification dispatcher is unavailable: id=%1").arg(item.id));
|
||||
}
|
||||
|
||||
bool PetWindow::shouldNotifyOnlyForReminder() const
|
||||
{
|
||||
return !isVisible() || hasActiveAIRequest() || m_streamingChatActive;
|
||||
}
|
||||
|
||||
void PetWindow::enqueueVisibleTriggeredReminder(const ReminderItem &item)
|
||||
{
|
||||
m_pendingVisibleTriggeredReminders.append(item);
|
||||
showNextTriggeredReminder();
|
||||
}
|
||||
|
||||
void PetWindow::showNextTriggeredReminder()
|
||||
{
|
||||
if (m_hasActiveTriggeredReminder || m_dragging || m_pendingVisibleTriggeredReminders.isEmpty())
|
||||
{
|
||||
m_deferredTriggeredReminders.append(item);
|
||||
return;
|
||||
}
|
||||
|
||||
const ReminderItem item = m_pendingVisibleTriggeredReminders.takeFirst();
|
||||
showTriggeredReminder(item);
|
||||
}
|
||||
|
||||
void PetWindow::finishActiveTriggeredReminder(bool hideBubble)
|
||||
{
|
||||
hideReminderActions();
|
||||
m_hasActiveTriggeredReminder = false;
|
||||
if (hideBubble && m_chatBubble)
|
||||
{
|
||||
m_chatBubble->hideBubble();
|
||||
}
|
||||
|
||||
showNextTriggeredReminder();
|
||||
}
|
||||
|
||||
void PetWindow::showTriggeredReminder(const ReminderItem &item)
|
||||
{
|
||||
const QString reminderState = m_clips.contains(QStringLiteral("happy"))
|
||||
? QStringLiteral("happy")
|
||||
: QStringLiteral("talk");
|
||||
playState(reminderState, false);
|
||||
showBubbleMessage(QStringLiteral("到时间啦:%1").arg(item.title));
|
||||
hideReminderActions();
|
||||
m_chatBubble->showMessage(QStringLiteral("到时间啦:%1").arg(item.title), bubbleAnchorPosition(), 0);
|
||||
showReminderActions(item);
|
||||
}
|
||||
|
||||
void PetWindow::ensureReminderActionPanel()
|
||||
{
|
||||
if (m_reminderActionPanel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto *panel = new QFrame();
|
||||
panel->setObjectName(QStringLiteral("ReminderActionPanel"));
|
||||
panel->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||
panel->setAttribute(Qt::WA_TranslucentBackground);
|
||||
panel->setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
panel->setStyleSheet(QStringLiteral(
|
||||
"QFrame#ReminderActionPanel {"
|
||||
"background: #ffffff;"
|
||||
"border: 1px solid #c7cdd4;"
|
||||
"border-radius: 8px;"
|
||||
"}"
|
||||
"QPushButton {"
|
||||
"background: #f1f4f7;"
|
||||
"border: 1px solid #aeb6bf;"
|
||||
"border-radius: 6px;"
|
||||
"padding: 7px 12px;"
|
||||
"color: #202124;"
|
||||
"}"
|
||||
"QPushButton:hover {"
|
||||
"background: #e4e9ee;"
|
||||
"}"
|
||||
"QPushButton:pressed {"
|
||||
"background: #d5dce3;"
|
||||
"}"));
|
||||
|
||||
auto *layout = new QVBoxLayout(panel);
|
||||
layout->setContentsMargins(8, 8, 8, 8);
|
||||
layout->setSpacing(8);
|
||||
|
||||
auto *dismissButton = new QPushButton(QStringLiteral("知道了"), panel);
|
||||
auto *snoozeButton = new QPushButton(QStringLiteral("5分钟后再提醒"), panel);
|
||||
layout->addWidget(dismissButton);
|
||||
layout->addWidget(snoozeButton);
|
||||
|
||||
connect(dismissButton, &QPushButton::clicked, this, [this]() {
|
||||
finishActiveTriggeredReminder(true);
|
||||
});
|
||||
|
||||
connect(snoozeButton, &QPushButton::clicked, this, [this]() {
|
||||
if (!m_hasActiveTriggeredReminder)
|
||||
{
|
||||
finishActiveTriggeredReminder(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const ReminderItem item = m_activeTriggeredReminder;
|
||||
hideReminderActions();
|
||||
snoozeTriggeredReminder(item);
|
||||
});
|
||||
|
||||
m_reminderActionPanel.reset(panel);
|
||||
}
|
||||
|
||||
void PetWindow::showReminderActions(const ReminderItem &item)
|
||||
{
|
||||
m_activeTriggeredReminder = item;
|
||||
m_hasActiveTriggeredReminder = true;
|
||||
ensureReminderActionPanel();
|
||||
if (!m_reminderActionPanel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_reminderActionPanel->adjustSize();
|
||||
updateReminderActionPosition();
|
||||
m_reminderActionPanel->show();
|
||||
m_reminderActionPanel->raise();
|
||||
}
|
||||
|
||||
void PetWindow::hideReminderActions()
|
||||
{
|
||||
m_hasActiveTriggeredReminder = false;
|
||||
if (m_reminderActionPanel)
|
||||
{
|
||||
m_reminderActionPanel->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void PetWindow::updateReminderActionPosition()
|
||||
{
|
||||
if (!m_reminderActionPanel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_reminderActionPanel->adjustSize();
|
||||
const QSize panelSize = m_reminderActionPanel->sizeHint();
|
||||
const QRect petGeometry = frameGeometry();
|
||||
constexpr int PanelSideSpacing = 8;
|
||||
|
||||
QPoint position(
|
||||
petGeometry.right() + PanelSideSpacing,
|
||||
petGeometry.center().y() - panelSize.height() / 2);
|
||||
|
||||
if (QScreen *screen = screenForPopup(this))
|
||||
{
|
||||
const QRect availableGeometry = screen->availableGeometry();
|
||||
const int maxX = qMax(availableGeometry.left(), availableGeometry.right() - panelSize.width());
|
||||
const int maxY = qMax(availableGeometry.top(), availableGeometry.bottom() - panelSize.height());
|
||||
if (position.x() > maxX)
|
||||
{
|
||||
position.setX(petGeometry.left() - panelSize.width() - PanelSideSpacing);
|
||||
}
|
||||
|
||||
position.setX(qBound(
|
||||
availableGeometry.left(),
|
||||
position.x(),
|
||||
maxX));
|
||||
position.setY(qBound(
|
||||
availableGeometry.top(),
|
||||
position.y(),
|
||||
maxY));
|
||||
}
|
||||
|
||||
m_reminderActionPanel->move(position);
|
||||
}
|
||||
|
||||
void PetWindow::snoozeTriggeredReminder(const ReminderItem &item)
|
||||
{
|
||||
m_hasActiveTriggeredReminder = false;
|
||||
if (!m_reminderManager)
|
||||
{
|
||||
playState(QStringLiteral("error"), false);
|
||||
showBubbleMessage(QStringLiteral("提醒功能初始化失败。"));
|
||||
QTimer::singleShot(1200, this, [this]() {
|
||||
showNextTriggeredReminder();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ReminderItem snoozedItem;
|
||||
QString errorMessage;
|
||||
if (!m_reminderManager->snoozeReminder(item, 5, &snoozedItem, &errorMessage))
|
||||
{
|
||||
playState(QStringLiteral("error"), false);
|
||||
showBubbleMessage(errorMessage.isEmpty() ? QStringLiteral("创建稍后提醒失败。") : errorMessage);
|
||||
QTimer::singleShot(1200, this, [this]() {
|
||||
showNextTriggeredReminder();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
playState(QStringLiteral("talk"), false);
|
||||
showBubbleMessage(QStringLiteral("已延后提醒:%1,时间:%2").arg(snoozedItem.title, reminderDisplayTime(snoozedItem.remindAt)));
|
||||
QTimer::singleShot(1200, this, [this]() {
|
||||
showNextTriggeredReminder();
|
||||
});
|
||||
}
|
||||
|
||||
bool PetWindow::submitAiChatMessage(const QString &message)
|
||||
@@ -620,6 +869,7 @@ bool PetWindow::submitAiChatMessage(const QString &message)
|
||||
|
||||
stopAnimationPrewarm();
|
||||
playState(QStringLiteral("think"), false);
|
||||
hideReminderActions();
|
||||
m_streamingAssistantText.clear();
|
||||
m_streamBubbleUpdateTimer.stop();
|
||||
m_streamingChatActive = true;
|
||||
@@ -844,6 +1094,7 @@ void PetWindow::flushStreamingBubble(bool finalUpdate)
|
||||
return;
|
||||
}
|
||||
|
||||
hideReminderActions();
|
||||
m_chatBubble->showMessage(
|
||||
m_streamingAssistantText,
|
||||
bubbleAnchorPosition(),
|
||||
@@ -888,6 +1139,7 @@ void PetWindow::hideEvent(QHideEvent *event)
|
||||
{
|
||||
m_chatBubble->hideBubble();
|
||||
}
|
||||
hideReminderActions();
|
||||
if (m_chatInputDialog)
|
||||
{
|
||||
m_chatInputDialog->hide();
|
||||
@@ -911,6 +1163,7 @@ void PetWindow::showEvent(QShowEvent *event)
|
||||
if (m_reminderManager)
|
||||
{
|
||||
m_reminderManager->start();
|
||||
m_reminderManager->checkDueRemindersNow();
|
||||
}
|
||||
scheduleAnimationPrewarm();
|
||||
}
|
||||
@@ -976,12 +1229,7 @@ void PetWindow::mouseReleaseEvent(QMouseEvent *event)
|
||||
m_dragging = false;
|
||||
playResolvedState(m_stateMachine.endDrag(), false);
|
||||
scheduleAnimationPrewarm();
|
||||
const QVector<ReminderItem> deferredReminders = m_deferredTriggeredReminders;
|
||||
m_deferredTriggeredReminders.clear();
|
||||
for (const ReminderItem &item : deferredReminders)
|
||||
{
|
||||
showTriggeredReminder(item);
|
||||
}
|
||||
showNextTriggeredReminder();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
@@ -1087,6 +1335,7 @@ void PetWindow::addStateTestActions(QMenu *menu)
|
||||
void PetWindow::updateBubblePosition()
|
||||
{
|
||||
m_chatBubble->updateAnchorPosition(bubbleAnchorPosition());
|
||||
updateReminderActionPosition();
|
||||
}
|
||||
|
||||
QPoint PetWindow::bubbleAnchorPosition() const
|
||||
|
||||
Reference in New Issue
Block a user