summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrik@mailbox.org>2026-01-01 14:33:14 +0100
committerAldrik Ramaekers <aldrik@mailbox.org>2026-01-01 14:33:14 +0100
commita1d639e963eaad1f8d24d47cd004c22052166978 (patch)
tree473a9a1b8b9ed5bd0c24a81fa848c31d3db788d0
parentbe5c11029adb25c586c4fcde6fedfa01d1bdcd49 (diff)
export dropdown ui
-rw-r--r--include/config.hpp3
-rw-r--r--include/exporter.hpp7
-rw-r--r--include/ui.hpp3
-rw-r--r--libs/imgui-1.92.1/imgui.cpp8
-rw-r--r--libs/imgui-1.92.1/imgui.h1
-rw-r--r--src/exporter.cpp6
-rw-r--r--src/providers/MailerSend.cpp5
-rw-r--r--src/ui/imgui_extensions.cpp65
-rw-r--r--src/ui/ui_invoices.cpp36
-rw-r--r--src/ui/ui_settings.cpp6
10 files changed, 122 insertions, 18 deletions
diff --git a/include/config.hpp b/include/config.hpp
index ff36364..dcc476b 100644
--- a/include/config.hpp
+++ b/include/config.hpp
@@ -31,12 +31,15 @@
#define u32 uint32_t
#define u64 uint64_t
+#define SIMULATE_EMAIL 1
+
namespace config {
static const char* PROGRAM_VERSION = "0.1.0"; // major.minor.patch
namespace colors {
static const ImU32 COLOR_ERROR_OUTLINE = IM_COL32(255, 0, 0, 80);
static const ImU32 COLOR_ERROR = IM_COL32(235, 64, 52, 255);
+ static const ImU32 COLOR_SUCCESS = IM_COL32(125, 214, 66, 255);
static const ImU32 COLOR_DEFAULT = IM_COL32(235, 255, 255, 255);
}
}
diff --git a/include/exporter.hpp b/include/exporter.hpp
index 00eaf32..82c18b2 100644
--- a/include/exporter.hpp
+++ b/include/exporter.hpp
@@ -26,6 +26,7 @@ typedef uint32_t e_err;
namespace exporter {
+
typedef enum
{
EXPORT_STARTING,
@@ -33,15 +34,19 @@ namespace exporter {
EXPORT_DONE,
} status;
+ typedef void (*send_email_callback)(e_err err);
+
typedef struct
{
time_t started_at;
+ time_t done_at;
e_err error;
status status;
char* sender;
char* recipient;
const char* subject;
const char* text;
+ send_email_callback callback;
} export_request;
typedef struct
@@ -51,5 +56,5 @@ namespace exporter {
} email_provider_impl;
email_provider_impl get_email_provider_implementation(email_provider provider);
- exporter::export_request* send_email(char* sender, char* recipient, const char* subject, const char* text);
+ exporter::export_request* send_email(char* sender, char* recipient, const char* subject, const char* text, send_email_callback ev);
} \ No newline at end of file
diff --git a/include/ui.hpp b/include/ui.hpp
index 86778bc..2e85dc2 100644
--- a/include/ui.hpp
+++ b/include/ui.hpp
@@ -17,6 +17,7 @@
#pragma once
#include "imgui.h"
+#include "exporter.hpp"
#include "administration.hpp"
#define STATUS_TEXT_LEN 64
@@ -102,4 +103,6 @@ namespace ImGui
void TaxRateDropdown(char* tax_internal_code, bool outgoing, bool has_error);
bool CurrencyDropdown(char* currency);
void ToggleDropdown(bool *buffer, char* option1, char* option2);
+
+ bool BeginExportDropdown(const char* title, const char* text, exporter::export_request* active_request);
} \ No newline at end of file
diff --git a/libs/imgui-1.92.1/imgui.cpp b/libs/imgui-1.92.1/imgui.cpp
index d3d82d4..dd13cc1 100644
--- a/libs/imgui-1.92.1/imgui.cpp
+++ b/libs/imgui-1.92.1/imgui.cpp
@@ -16677,6 +16677,14 @@ void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_
EndTooltip();
}
+void ImGui::LoadingIndicatorCircleSmall()
+{
+ float radius = 10.0f;
+ const ImVec4 col = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
+ const ImVec4 bg = ImGui::GetStyleColorVec4(ImGuiCol_Button);
+ ImGui::LoadingIndicatorCircle(radius, bg, col, 6, 4.0f);
+}
+
// From https://github.com/ocornut/imgui/issues/1901#issuecomment-444929973
void ImGui::LoadingIndicatorCircle(const float indicator_radius,
const ImVec4& main_color, const ImVec4& backdrop_color,
diff --git a/libs/imgui-1.92.1/imgui.h b/libs/imgui-1.92.1/imgui.h
index 3b48ad8..ad874ab 100644
--- a/libs/imgui-1.92.1/imgui.h
+++ b/libs/imgui-1.92.1/imgui.h
@@ -377,6 +377,7 @@ namespace ImGui
{
IMGUI_API bool BeginComboPreview();
IMGUI_API void EndComboPreview();
+ IMGUI_API void LoadingIndicatorCircleSmall();
IMGUI_API void LoadingIndicatorCircle(const float indicator_radius,
const ImVec4& main_color, const ImVec4& backdrop_color,
const int circle_count, const float speed);
diff --git a/src/exporter.cpp b/src/exporter.cpp
index c53b70d..e296973 100644
--- a/src/exporter.cpp
+++ b/src/exporter.cpp
@@ -45,11 +45,14 @@ static int _send_email_t(void* arg) {
request->error = impl.send_email(request->sender, request->recipient, request->subject, request->text);
request->status = exporter::status::EXPORT_DONE;
+ request->done_at = time(NULL);
+
+ if (request->callback) request->callback(request->error);
return 0;
}
-exporter::export_request* exporter::send_email(char* sender, char* recipient, const char* subject, const char* text)
+exporter::export_request* exporter::send_email(char* sender, char* recipient, const char* subject, const char* text, send_email_callback ev)
{
exporter::export_request* result = (exporter::export_request*)memops::alloc(sizeof(exporter::export_request));
result->started_at = time(NULL);
@@ -59,6 +62,7 @@ exporter::export_request* exporter::send_email(char* sender, char* recipient, co
result->recipient = recipient;
result->subject = subject;
result->text = text;
+ result->callback = ev;
thrd_t thr;
if (thrd_create(&thr, _send_email_t, result) != thrd_success) {
diff --git a/src/providers/MailerSend.cpp b/src/providers/MailerSend.cpp
index 1a633d9..122b7b2 100644
--- a/src/providers/MailerSend.cpp
+++ b/src/providers/MailerSend.cpp
@@ -24,6 +24,11 @@
bool _MailerSend_send_email(char* sender, char* recipient, const char* subject, const char* text)
{
+ #if SIMULATE_EMAIL
+ usleep(1000 * 1000);
+ return E_ERR_SUCCESS;
+ #endif
+
const char *api_key = administration::get_email_service().api_key;
httplib::SSLClient cli("api.mailersend.com", 443);
cli.enable_server_certificate_verification(false);
diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp
index f8adb70..05eeb7b 100644
--- a/src/ui/imgui_extensions.cpp
+++ b/src/ui/imgui_extensions.cpp
@@ -1,3 +1,5 @@
+#include <time.h>
+
#include "ui.hpp"
#include "strops.hpp"
#include "memops.hpp"
@@ -707,4 +709,67 @@ namespace ImGui
ImGui::CountryDropdown(buffer->country_code, IM_ARRAYSIZE(buffer->country_code), active_countries_only);
}
+
+ bool BeginExportDropdown(const char* title, const char* text, exporter::export_request* active_request)
+ {
+ static bool is_new_request = false;
+ static bool show_status_change = false;
+ static time_t status_changed_at = 0;
+ static e_err last_err;
+
+ if (active_request && active_request->status == exporter::status::EXPORT_DONE && is_new_request)
+ {
+ is_new_request = false;
+ show_status_change = true;
+ last_err = active_request->error;
+ status_changed_at = time(NULL);
+ }
+
+ if (active_request && active_request->status != exporter::status::EXPORT_DONE) {
+ is_new_request = true;
+ ImGui::BeginDisabled();
+ ImGui::BeginCombo("##sendStatus", NULL, 1 << 20);
+ {
+ if (ImGui::BeginComboPreview())
+ {
+ ImGui::Text("Sending"); // @Localize
+ ImGui::SameLine();
+ ImGui::LoadingIndicatorCircleSmall();
+ ImGui::EndComboPreview();
+ }
+ }
+ ImGui::EndDisabled();
+ }
+ else if (show_status_change) {
+ ImGui::BeginDisabled();
+ ImGui::BeginCombo("##sendStatus", NULL, 1 << 20);
+ {
+ if (ImGui::BeginComboPreview())
+ {
+ if (last_err == E_ERR_SUCCESS) {
+ ImGui::PushStyleColor(ImGuiCol_Text, config::colors::COLOR_SUCCESS);
+ ImGui::Text("√");
+ ImGui::PopStyleColor();
+ ImGui::Text("Success"); // @Localize
+ }
+ else {
+ ImGui::PushStyleColor(ImGuiCol_Text, config::colors::COLOR_ERROR);
+ ImGui::Text("X");
+ ImGui::PopStyleColor();
+ ImGui::Text("Failed"); // @Localize
+ }
+ ImGui::EndComboPreview();
+ }
+ }
+ ImGui::EndDisabled();
+
+ if (difftime(time(NULL), status_changed_at) > 1.0f) {
+ show_status_change = false;
+ }
+ }
+ else {
+ return ImGui::BeginCombo(title, text);
+ }
+ return false;
+ }
} \ No newline at end of file
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index 6a50c7f..eb04af4 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -20,6 +20,7 @@
#include "ui.hpp"
#include "imgui.h"
#include "memops.hpp"
+#include "config.hpp"
#include "strops.hpp"
#include "locales.hpp"
#include "importer.hpp"
@@ -484,6 +485,28 @@ static void draw_invoice_create()
ImGui::EndChild();
}
+static void draw_send_options()
+{
+ static exporter::export_request* active_request = NULL;
+
+ if (ImGui::BeginExportDropdown("##Send", locale::get("ui.sendAs"), active_request))
+ {
+ if (ImGui::Selectable(locale::get("ui.sendAs.email"), false)) {
+ active_request = exporter::send_email("test@test-vz9dlemj2564kj50.mlsender.net", "aldrikboy@gmail.com", "test", "test 123",
+ [](e_err status) {
+ if (status == E_ERR_SUCCESS) {
+ administration::activity_add(ACTIVITY_USER, active_invoice.id, "Sent email", 0);
+ _reload_activities();
+ }
+ });
+ }
+ // if (ImGui::Selectable(locale::get("ui.sendAs.einvoice"), false)) {
+
+ // }
+ ImGui::EndCombo();
+ }
+}
+
static void draw_invoice_view()
{
if (ImGui::Button(locale::get("form.back"), true, false)) {
@@ -499,17 +522,8 @@ static void draw_invoice_view()
ImGui::SameLine();
- ImGui::PushItemWidth(100.0f);
- if (ImGui::BeginCombo("##Send", locale::get("ui.sendAs")))
- {
- if (ImGui::Selectable(locale::get("ui.sendAs.email"), false)) {
- exporter::send_email("test@test-vz9dlemj2564kj50.mlsender.net", "aldrikboy@gmail.com", "test", "test 123");
- }
- // if (ImGui::Selectable(locale::get("ui.sendAs.einvoice"), false)) {
-
- // }
- ImGui::EndCombo();
- }
+ ImGui::PushItemWidth(180.0f);
+ draw_send_options();
ImGui::PushItemWidth(0.0f);
ImGui::SameLine();
diff --git a/src/ui/ui_settings.cpp b/src/ui/ui_settings.cpp
index fd0d5b9..fb02738 100644
--- a/src/ui/ui_settings.cpp
+++ b/src/ui/ui_settings.cpp
@@ -314,11 +314,7 @@ static void draw_ai_service_ui()
if (ImGui::BeginComboPreview())
{
if (model_request->status != importer::status::IMPORT_DONE) {
- float radius = 10.0f;
- const ImVec4 col = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
- const ImVec4 bg = ImGui::GetStyleColorVec4(ImGuiCol_Button);
- ImGui::LoadingIndicatorCircle(radius, bg, col, 6, 4.0f);
-
+ ImGui::LoadingIndicatorCircleSmall();
ImGui::SameLine();
}
ImGui::TextUnformatted(new_ai_service.model_name);