From b81d5bca3c4418fad601f103f070f309077c16ed Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 2 Nov 2025 06:50:41 +0100 Subject: [PATCH] ui: update discord references and add forum widget (#1440) * sunnylink: introduce community popup with QR code embedding - Added `SunnylinkCommunityPopup` widget to promote the sunnypilot Community Forum. - Integrated a QR code generator and display for quick access. - Updated `WiFiPromptWidget` to include a "Learn More" button triggering the community popup. * sunnylink: adjust community popup styling for better layout - Reduced font size of description text slightly for consistency. - Decreased QR code dimensions to improve visual balance. * Making more space out of thin air * sunnylink: update community references to use forum links - Replaced Discord links with Community Forum URLs for better alignment. - Improved clarity in sponsorship instructions. --- selfdrive/ui/qt/widgets/wifi.cc | 11 +- selfdrive/ui/qt/widgets/wifi.h | 5 + selfdrive/ui/sunnypilot/SConscript | 1 + .../settings/sunnylink/community_widget.cc | 139 ++++++++++++++++++ .../settings/sunnylink/community_widget.h | 40 +++++ .../settings/sunnylink/sponsor_widget.cc | 4 +- 6 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.h diff --git a/selfdrive/ui/qt/widgets/wifi.cc b/selfdrive/ui/qt/widgets/wifi.cc index d7eb8beeb..8958e48c9 100644 --- a/selfdrive/ui/qt/widgets/wifi.cc +++ b/selfdrive/ui/qt/widgets/wifi.cc @@ -11,17 +11,18 @@ WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) { main_layout->setContentsMargins(56, 40, 56, 40); main_layout->setSpacing(42); - QLabel *title = new QLabel(tr("🔥 Firehose Mode 🔥")); - title->setStyleSheet("font-size: 64px; font-weight: 500;"); + community_popup = new SunnylinkCommunityPopup(this); + QLabel *title = new QLabel(tr("sunnypilot Community")); + title->setStyleSheet("font-size: 56px; font-weight: 500;"); main_layout->addWidget(title); - QLabel *desc = new QLabel(tr("Maximize your training data uploads to improve openpilot's driving models.")); + QLabel *desc = new QLabel(tr("Need help or have ideas?
Join our community now!")); desc->setStyleSheet("font-size: 40px; font-weight: 400;"); desc->setWordWrap(true); main_layout->addWidget(desc); - QPushButton *settings_btn = new QPushButton(tr("Open")); - connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(1, "FirehosePanel"); }); + QPushButton *settings_btn = new QPushButton(tr("Learn More")); + connect(settings_btn, &QPushButton::clicked, [=]() { community_popup->exec(); }); settings_btn->setStyleSheet(R"( QPushButton { font-size: 48px; diff --git a/selfdrive/ui/qt/widgets/wifi.h b/selfdrive/ui/qt/widgets/wifi.h index 3e68a15b7..8ef9dca91 100644 --- a/selfdrive/ui/qt/widgets/wifi.h +++ b/selfdrive/ui/qt/widgets/wifi.h @@ -3,12 +3,17 @@ #include #include +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.h" + class WiFiPromptWidget : public QFrame { Q_OBJECT public: explicit WiFiPromptWidget(QWidget* parent = 0); +private: + SunnylinkCommunityPopup *community_popup; + signals: void openSettings(int index = 0, const QString ¶m = ""); }; diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index be92c1426..449093886 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -35,6 +35,7 @@ qt_src = [ "sunnypilot/qt/offroad/settings/software_panel.cc", "sunnypilot/qt/offroad/settings/sunnylink_panel.cc", "sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc", + "sunnypilot/qt/offroad/settings/sunnylink/community_widget.cc", "sunnypilot/qt/offroad/settings/trips_panel.cc", "sunnypilot/qt/offroad/settings/vehicle_panel.cc", "sunnypilot/qt/offroad/settings/visuals_panel.cc", diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.cc new file mode 100644 index 000000000..e29e89ee9 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.cc @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2025-, sunnypilot contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.h" + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/util.h" + +using qrcodegen::QrCode; + +// --- SunnylinkCommunityQRWidget --- + +SunnylinkCommunityQRWidget::SunnylinkCommunityQRWidget(QWidget* parent) + : QWidget(parent) {} + +void SunnylinkCommunityQRWidget::showEvent(QShowEvent *event) { + updateQrCode(SUNNYLINK_COMMUNITY_URL); + update(); +} + +void SunnylinkCommunityQRWidget::updateQrCode(const QString &text) { + QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW); + qint32 sz = qr.getSize(); + QImage im(sz, sz, QImage::Format_RGB32); + + QRgb black = qRgb(0, 0, 0); + QRgb white = qRgb(255, 255, 255); + for (int y = 0; y < sz; y++) { + for (int x = 0; x < sz; x++) { + im.setPixel(x, y, qr.getModule(x, y) ? black : white); + } + } + + int final_sz = ((width() / sz) - 1) * sz; + img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly); +} + +void SunnylinkCommunityQRWidget::paintEvent(QPaintEvent *e) { + QPainter p(this); + p.fillRect(rect(), Qt::white); + + if (!img.isNull()) { + QSize s = (size() - img.size()) / 2; + p.drawPixmap(s.width(), s.height(), img); + } +} + +// --- SunnylinkCommunityPopup --- + +QStringList SunnylinkCommunityPopup::getInstructions() { + QStringList instructions; + instructions << tr("Scan the QR code and join us!"); + return instructions; +} + +SunnylinkCommunityPopup::SunnylinkCommunityPopup(QWidget* parent) + : DialogBase(parent) { + auto *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + + // Solarized Light base3 background + setStyleSheet("SunnylinkCommunityPopup { background-color: #FDF6E3; }"); + + // Header spanning full width + auto headerWidget = new QWidget(this); + auto headerLayout = new QHBoxLayout(headerWidget); + headerLayout->setContentsMargins(85, 50, 85, 30); + headerLayout->setSpacing(30); + + auto close = new QPushButton(QIcon(":/icons/close.svg"), "", this); + close->setIconSize(QSize(80, 80)); + close->setStyleSheet("border: none;"); + connect(close, &QPushButton::clicked, this, &QDialog::reject); + headerLayout->addWidget(close, 0, Qt::AlignLeft | Qt::AlignVCenter); + + const auto title = new QLabel(tr("Join the sunnypilot Community Forum"), this); + // Solarized base02 for text + title->setStyleSheet("font-size: 65px; color: #073642;"); + title->setWordWrap(false); + title->setAlignment(Qt::AlignCenter); + headerLayout->addWidget(title, 1); + + // Spacer to balance the close button on the right + auto spacer = new QWidget(this); + spacer->setFixedSize(80, 80); + headerLayout->addWidget(spacer, 0); + + mainLayout->addWidget(headerWidget); + + // Two-column content layout + auto contentLayout = new QHBoxLayout(); + contentLayout->setContentsMargins(0, 0, 0, 0); + contentLayout->setSpacing(0); + mainLayout->addLayout(contentLayout, 66); + + // Left side: description + auto leftLayout = new QVBoxLayout(); + leftLayout->setContentsMargins(85, 40, 50, 70); + leftLayout->setSpacing(35); + contentLayout->addLayout(leftLayout, 40); + + // Hype / intro paragraph + const auto desc = new QLabel(tr( + "We're excited to announce our sunnypilot Community Forum

" + "Over the years, Discord just hasn't scaled well for our growing community.
" + "It's noisy, unsearchable, and great discussions disappear too easily.
" + "Our new community forum aims to fix that by making it easier to find answers, share ideas, track feedback, report bugs, help newcomers and more!

" + "Here's what's waiting for you:
" + "• Fully indexable and discoverable through search engines 🔎
" + "• AI-powered🤖 topic and chat summaries, spam detection, and more
" + "• A trust-level system✅ that rewards meaningful contributions
" + "• Designed to work on your own time.🧘

" + "Scan the QR code on the right and join the discussion!" + ), this); + // Solarized base01 for body text + desc->setStyleSheet("font-size: 40px; color: #586E75;"); + desc->setWordWrap(true); + leftLayout->addWidget(desc); + + leftLayout->addStretch(); + + // Right side: QR code and instructions + auto rightLayout = new QVBoxLayout(); + rightLayout->setContentsMargins(50, 40, 85, 70); + rightLayout->setSpacing(40); + contentLayout->addLayout(rightLayout, 1); + + // QR code (smaller, fixed size) + auto *qr = new SunnylinkCommunityQRWidget(this); + qr->setFixedSize(500, 500); + rightLayout->addStretch(); + rightLayout->addWidget(qr, 0, Qt::AlignCenter); + rightLayout->addStretch(); +} \ No newline at end of file diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.h new file mode 100644 index 000000000..613375d12 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/community_widget.h @@ -0,0 +1,40 @@ +/** +* Copyright (c) 2025-, sunnypilot contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include +#include + +#include "common/util.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +const QString SUNNYLINK_COMMUNITY_URL = "https://community.sunnypilot.ai/sp-qr"; + +class SunnylinkCommunityQRWidget : public QWidget { + Q_OBJECT + +public: + explicit SunnylinkCommunityQRWidget(QWidget* parent = nullptr); + void paintEvent(QPaintEvent*) override; + +private: + QPixmap img; + void updateQrCode(const QString &text); + void showEvent(QShowEvent *event) override; +}; + +// Popup widget +class SunnylinkCommunityPopup : public DialogBase { + Q_OBJECT + +public: + explicit SunnylinkCommunityPopup(QWidget* parent = nullptr); + +private: + static QStringList getInstructions(); +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc index 7b5ce4823..a014eddac 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc @@ -79,11 +79,11 @@ QStringList SunnylinkSponsorPopup::getInstructions(bool sponsor_pair) { instructions << tr("Scan the QR code to login to your GitHub account") << tr("Follow the prompts to complete the pairing process") << tr("Re-enter the \"sunnylink\" panel to verify sponsorship status") - << tr("If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot"); + << tr("If sponsorship status was not updated, please contact a moderator on our forum at https://community.sunnypilot.ai"); } else { instructions << tr("Scan the QR code to visit sunnyhaibin's GitHub Sponsors page") << tr("Choose your sponsorship tier and confirm your support") - << tr("Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status"); + << tr("Join our Community Forum at https://community.sunnypilot.ai and reach out to a moderator if you have issues"); } return instructions; }