setup electron app with boilerplate

This commit is contained in:
talksik
2026-02-19 19:41:26 -08:00
parent fc6a5e43db
commit 1f249c47a6
5174 changed files with 8743 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
#include "authdialog.h"
#include "ui_authdialog.h"
#include "../authmanager.h"
AuthDialog::AuthDialog(QWidget *parent)
: QDialog{parent}, ui(new Ui::Dialog)
{
ui->setupUi(this);
// Connect button signals
connect(ui->sendCodeButton, &QPushButton::clicked, this, &AuthDialog::onSendCodeClicked);
connect(ui->signInButton, &QPushButton::clicked, this, &AuthDialog::onSignInClicked);
connect(ui->goBackButton, &QPushButton::clicked, this, &AuthDialog::showEmailPage);
// Start on email page
showEmailPage();
}
AuthDialog::~AuthDialog()
{
delete ui;
}
void AuthDialog::reset()
{
ui->emailLineEdit->clear();
ui->codeLineEdit->clear();
clearError();
showEmailPage();
}
void AuthDialog::showEmailPage()
{
ui->stackedWidget->setCurrentIndex(0);
ui->emailLineEdit->setFocus();
clearError();
}
void AuthDialog::showCodePage()
{
ui->stackedWidget->setCurrentIndex(1);
ui->codeLineEdit->setFocus();
clearError();
}
void AuthDialog::clearError()
{
ui->errorMessage->setText("");
ui->errorMessage->setVisible(false);
ui->codeErrorMessage->setText("");
ui->codeErrorMessage->setVisible(false);
}
void AuthDialog::showError(const QString &message)
{
if (ui->stackedWidget->currentIndex() == 0)
{
// Update error labels on both pages
ui->errorMessage->setText(message);
ui->errorMessage->setVisible(true);
}
else
{
ui->codeErrorMessage->setText(message);
ui->codeErrorMessage->setVisible(true);
}
}
void AuthDialog::onSendCodeClicked()
{
QString email = ui->emailLineEdit->text().trimmed();
if (email.isEmpty())
{
showError("Please enter your email");
return;
}
clearError();
emit codeRequested(email);
}
void AuthDialog::onCodeSentToEmail(const QString &email)
{
showCodePage();
}
void AuthDialog::onSignInClicked()
{
QString code = ui->codeLineEdit->text().trimmed();
if (code.isEmpty())
{
showError("Please enter the code");
return;
}
clearError();
emit signInRequested(code);
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef AUTHDIALOG_H
#define AUTHDIALOG_H
#include <QDialog>
namespace Ui {
class Dialog;
}
class AuthDialog : public QDialog
{
Q_OBJECT
public:
explicit AuthDialog(QWidget *parent = nullptr);
~AuthDialog();
public slots:
void reset();
void showError(const QString &message);
void onCodeSentToEmail(const QString &email);
private slots:
void onSendCodeClicked();
void onSignInClicked();
signals:
void codeRequested(const QString &email);
void signInRequested(const QString &code);
private:
Ui::Dialog *ui;
void showEmailPage();
void showCodePage();
void clearError();
};
#endif // AUTHDIALOG_H
+154
View File
@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QStackedWidget" name="stackedWidget">
<property name="geometry">
<rect>
<x>40</x>
<y>20</y>
<width>321</width>
<height>241</height>
</rect>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="enterEmailPage">
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>50</x>
<y>20</y>
<width>231</width>
<height>183</height>
</rect>
</property>
<property name="title">
<string>Sign in</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="emailLabel">
<property name="text">
<string>Email</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="emailLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="errorMessage">
<property name="text">
<string>Error</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="sendCodeButton">
<property name="text">
<string>Send code</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="enterCodePage">
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>50</x>
<y>20</y>
<width>231</width>
<height>201</height>
</rect>
</property>
<property name="title">
<string>Verify code</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="codeLabel">
<property name="text">
<string>Enter the code we sent to your email</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="codeLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="codeErrorMessage">
<property name="text">
<string>Error</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="goBackButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="signInButton">
<property name="text">
<string>Sign In</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>
+80
View File
@@ -0,0 +1,80 @@
#include "createstreamdialog.h"
#include <QComboBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QVBoxLayout>
CreateStreamDialog::CreateStreamDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle("New Stream");
setMinimumWidth(320);
auto *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("Stream name:"));
m_nameEdit = new QLineEdit(this);
m_nameEdit->setPlaceholderText("e.g. design-review");
layout->addWidget(m_nameEdit);
layout->addWidget(new QLabel("Visibility:"));
m_visibilityCombo = new QComboBox(this);
m_visibilityCombo->addItem("Everyone in network", "network_all");
m_visibilityCombo->addItem("Specific members", "custom");
layout->addWidget(m_visibilityCombo);
m_membersLabel = new QLabel("Select members:");
m_membersList = new QListWidget(this);
m_membersLabel->setVisible(false);
m_membersList->setVisible(false);
layout->addWidget(m_membersLabel);
layout->addWidget(m_membersList);
connect(m_visibilityCombo, &QComboBox::currentIndexChanged, this, [this](int) {
bool specific = (m_visibilityCombo->currentData().toString() == "custom");
m_membersLabel->setVisible(specific);
m_membersList->setVisible(specific);
});
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
layout->addWidget(buttons);
connect(buttons, &QDialogButtonBox::accepted, this, [this]() {
QString name = m_nameEdit->text().trimmed();
if (name.isEmpty())
return;
QString visibility = m_visibilityCombo->currentData().toString();
QStringList members;
if (visibility == "custom")
{
for (int i = 0; i < m_membersList->count(); ++i)
{
auto *item = m_membersList->item(i);
if (item->checkState() == Qt::Checked)
members.append(item->data(Qt::UserRole).toString());
}
}
emit streamRequested(name, visibility, members);
m_nameEdit->clear();
m_visibilityCombo->setCurrentIndex(0);
accept();
});
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
void CreateStreamDialog::setNetworkMembers(const std::vector<Human> &members)
{
m_membersList->clear();
for (const auto &human : members)
{
auto *item = new QListWidgetItem(human.email, m_membersList);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
item->setData(Qt::UserRole, human.email);
}
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef CREATESTREAMDIALOG_H
#define CREATESTREAMDIALOG_H
#include "../models.h"
#include <QDialog>
QT_BEGIN_NAMESPACE
class QLineEdit;
class QComboBox;
class QLabel;
class QListWidget;
QT_END_NAMESPACE
class CreateStreamDialog : public QDialog
{
Q_OBJECT
public:
explicit CreateStreamDialog(QWidget *parent = nullptr);
/// Call before showing to populate the member checklist.
void setNetworkMembers(const std::vector<Human> &members);
signals:
void streamRequested(const QString &name, const QString &visibility, const QStringList &memberEmails);
private:
QLineEdit *m_nameEdit;
QComboBox *m_visibilityCombo;
QLabel *m_membersLabel;
QListWidget *m_membersList;
};
#endif // CREATESTREAMDIALOG_H
+85
View File
@@ -0,0 +1,85 @@
#include "mediapreviewdialog.h"
#include <QAudioOutput>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMediaPlayer>
#include <QPushButton>
#include <QVBoxLayout>
#include <QVideoWidget>
MediaPreviewDialog::MediaPreviewDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle("Media Preview");
resize(480, 360);
setModal(true);
m_videoWidget = new QVideoWidget(this);
m_player = new QMediaPlayer(this);
m_audioOutput = new QAudioOutput(this);
m_player->setAudioOutput(m_audioOutput);
m_cancelButton = new QPushButton("Cancel", this);
m_sendButton = new QPushButton("Send", this);
m_sendButton->setDefault(true);
auto *buttonLayout = new QHBoxLayout();
buttonLayout->addWidget(m_cancelButton);
buttonLayout->addStretch();
buttonLayout->addWidget(m_sendButton);
auto *layout = new QVBoxLayout(this);
layout->addWidget(m_videoWidget, 1);
layout->addLayout(buttonLayout);
m_cancelButton->hide();
m_sendButton->hide();
connect(m_sendButton, &QPushButton::clicked, this, &QDialog::accept);
connect(m_cancelButton, &QPushButton::clicked, this, &QDialog::reject);
}
QVideoSink *MediaPreviewDialog::videoSink() const
{
return m_videoWidget->videoSink();
}
void MediaPreviewDialog::showRecording()
{
m_player->stop();
m_player->setVideoOutput(nullptr);
m_player->setSource(QUrl());
m_cancelButton->hide();
m_sendButton->hide();
show();
}
void MediaPreviewDialog::showPreview(const QUrl &mediaUrl)
{
m_cancelButton->show();
m_sendButton->show();
m_player->setVideoOutput(m_videoWidget);
m_player->setSource(mediaUrl);
m_player->setLoops(QMediaPlayer::Infinite);
m_player->play();
}
void MediaPreviewDialog::reset()
{
m_player->stop();
m_player->setVideoOutput(nullptr);
m_player->setSource(QUrl());
m_cancelButton->hide();
m_sendButton->hide();
hide();
}
void MediaPreviewDialog::keyReleaseEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Space && !event->isAutoRepeat())
{
emit spaceReleased();
return;
}
QDialog::keyReleaseEvent(event);
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef MEDIAPREVIEWDIALOG_H
#define MEDIAPREVIEWDIALOG_H
#include <QDialog>
class QKeyEvent;
QT_BEGIN_NAMESPACE
class QVideoWidget;
class QVideoSink;
class QMediaPlayer;
class QAudioOutput;
class QPushButton;
QT_END_NAMESPACE
class MediaPreviewDialog : public QDialog
{
Q_OBJECT
public:
explicit MediaPreviewDialog(QWidget *parent = nullptr);
/// Video sink for live camera feed during recording
QVideoSink *videoSink() const;
/// Show dialog in recording mode (live camera preview, buttons hidden)
void showRecording();
/// Transition to preview mode: loop playback, show Cancel/Send buttons
void showPreview(const QUrl &mediaUrl);
/// Stop playback and reset state
void reset();
signals:
void spaceReleased();
protected:
void keyReleaseEvent(QKeyEvent *event) override;
private:
QVideoWidget *m_videoWidget;
QMediaPlayer *m_player;
QAudioOutput *m_audioOutput;
QPushButton *m_cancelButton;
QPushButton *m_sendButton;
};
#endif // MEDIAPREVIEWDIALOG_H
+55
View File
@@ -0,0 +1,55 @@
#include "memberpickerdialog.h"
#include <QDialogButtonBox>
#include <QLabel>
#include <QListWidget>
#include <QSet>
#include <QVBoxLayout>
MemberPickerDialog::MemberPickerDialog(const QString &title, QWidget *parent)
: QDialog(parent)
{
setWindowTitle(title);
setMinimumWidth(300);
auto *layout = new QVBoxLayout(this);
m_list = new QListWidget(this);
layout->addWidget(m_list);
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
layout->addWidget(buttons);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
void MemberPickerDialog::setMembers(const std::vector<Human> &members, const std::vector<QString> &exclude)
{
m_list->clear();
QSet<QString> excludeSet;
for (const auto &e : exclude)
excludeSet.insert(e);
for (const auto &human : members)
{
if (excludeSet.contains(human.email))
continue;
auto *item = new QListWidgetItem(human.email, m_list);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
item->setData(Qt::UserRole, human.email);
}
}
QStringList MemberPickerDialog::selectedEmails() const
{
QStringList result;
for (int i = 0; i < m_list->count(); ++i)
{
auto *item = m_list->item(i);
if (item->checkState() == Qt::Checked)
result.append(item->data(Qt::UserRole).toString());
}
return result;
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef MEMBERPICKERDIALOG_H
#define MEMBERPICKERDIALOG_H
#include "../models.h"
#include <QDialog>
QT_BEGIN_NAMESPACE
class QListWidget;
QT_END_NAMESPACE
/// A simple dialog that shows a checklist of humans and returns the selected emails.
class MemberPickerDialog : public QDialog
{
Q_OBJECT
public:
explicit MemberPickerDialog(const QString &title, QWidget *parent = nullptr);
/// Populate the checklist. Members whose emails appear in `exclude` are hidden.
void setMembers(const std::vector<Human> &members, const std::vector<QString> &exclude = {});
/// Returns the emails of all checked items.
QStringList selectedEmails() const;
private:
QListWidget *m_list;
};
#endif // MEMBERPICKERDIALOG_H
+95
View File
@@ -0,0 +1,95 @@
#include "settingsdialog.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QStackedWidget>
#include <QVBoxLayout>
SettingsDialog::SettingsDialog(QWidget *parent)
: QDialog(parent)
{
buildUi();
setModal(true);
m_listWidget->addItem("Account");
m_listWidget->addItem("Create Network");
connect(m_listWidget, &QListWidget::currentRowChanged,
m_stackedWidget, &QStackedWidget::setCurrentIndex);
m_listWidget->setCurrentRow(0);
connect(m_signOutButton, &QPushButton::clicked, this, [this]() {
emit signOutRequested();
close();
});
connect(m_createNetworkButton, &QPushButton::clicked, this, [this]() {
QString name = m_networkNameEdit->text().trimmed();
if (name.isEmpty())
return;
emit createNetworkRequested(name);
m_networkNameEdit->clear();
});
}
void SettingsDialog::setAuthLabel(const QString &text)
{
m_authLabel->setText(text);
}
void SettingsDialog::buildUi()
{
resize(600, 500);
setWindowTitle("Settings");
auto *mainLayout = new QHBoxLayout(this);
m_listWidget = new QListWidget();
m_listWidget->setMaximumWidth(220);
mainLayout->addWidget(m_listWidget);
m_stackedWidget = new QStackedWidget();
mainLayout->addWidget(m_stackedWidget);
// ── Account page ──
auto *accountPage = new QWidget();
auto *accountLayout = new QVBoxLayout(accountPage);
accountLayout->addStretch();
m_authLabel = new QLabel("Hello, ");
m_authLabel->setAlignment(Qt::AlignCenter);
accountLayout->addWidget(m_authLabel);
m_signOutButton = new QPushButton("Sign Out");
accountLayout->addWidget(m_signOutButton);
accountLayout->addStretch();
m_stackedWidget->addWidget(accountPage);
// ── Create Network page ──
auto *createNetworkPage = new QWidget();
auto *createNetworkLayout = new QVBoxLayout(createNetworkPage);
auto *createNetworkLabel = new QLabel("Create a new network");
QFont labelFont;
labelFont.setPointSize(16);
createNetworkLabel->setFont(labelFont);
createNetworkLayout->addWidget(createNetworkLabel);
m_networkNameEdit = new QLineEdit();
m_networkNameEdit->setPlaceholderText("Network name");
createNetworkLayout->addWidget(m_networkNameEdit);
m_createNetworkButton = new QPushButton("Create");
createNetworkLayout->addWidget(m_createNetworkButton);
createNetworkLayout->addStretch();
m_stackedWidget->addWidget(createNetworkPage);
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
class QLabel;
class QLineEdit;
class QListWidget;
class QPushButton;
class QStackedWidget;
QT_END_NAMESPACE
class SettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit SettingsDialog(QWidget *parent = nullptr);
void setAuthLabel(const QString &text);
signals:
void signOutRequested();
void createNetworkRequested(const QString &name);
private:
void buildUi();
QListWidget *m_listWidget;
QStackedWidget *m_stackedWidget;
QLabel *m_authLabel;
QPushButton *m_signOutButton;
QLineEdit *m_networkNameEdit;
QPushButton *m_createNetworkButton;
};
#endif // SETTINGSDIALOG_H
+30
View File
@@ -0,0 +1,30 @@
#include "textinputdialog.h"
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
TextInputDialog::TextInputDialog(QWidget *parent)
: QDialog(parent), m_lineEdit(new QLineEdit(this)), m_sendButton(new QPushButton("Send", this))
{
setWindowTitle("Send Text Message");
auto *layout = new QVBoxLayout(this);
layout->addWidget(m_lineEdit);
layout->addWidget(m_sendButton);
connect(m_lineEdit, &QLineEdit::returnPressed, this, &TextInputDialog::onSubmit);
connect(m_sendButton, &QPushButton::clicked, this, &TextInputDialog::onSubmit);
}
void TextInputDialog::onSubmit()
{
QString text = m_lineEdit->text().trimmed();
if (text.isEmpty())
{
return;
}
emit textSubmitted(text);
m_lineEdit->clear();
hide();
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef TEXTINPUTDIALOG_H
#define TEXTINPUTDIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE
class TextInputDialog : public QDialog
{
Q_OBJECT
public:
explicit TextInputDialog(QWidget *parent = nullptr);
signals:
void textSubmitted(const QString &text);
private slots:
void onSubmit();
private:
QLineEdit *m_lineEdit;
QPushButton *m_sendButton;
};
#endif // TEXTINPUTDIALOG_H