diff options
| author | Aldrik Ramaekers <aldrik@mailbox.org> | 2026-01-11 20:09:37 +0100 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrik@mailbox.org> | 2026-01-11 20:09:37 +0100 |
| commit | 74748ae725ca3cdcc450473ea0ccd245ab36533d (patch) | |
| tree | 4cf69b3d2cb99219651c38683d1c1c6cddbcc7a4 | |
| parent | c011cfe0cb4b2b29c2e1d48b5bfd6db1d7fdc6d8 (diff) | |
ai import ui improvements
| -rw-r--r-- | TODO | 2 | ||||
| -rw-r--r-- | include/config.hpp | 5 | ||||
| -rw-r--r-- | libs/imgui/imgui.cpp | 4 | ||||
| -rw-r--r-- | libs/imgui/imgui.h | 2 | ||||
| -rw-r--r-- | src/importer.cpp | 10 | ||||
| -rw-r--r-- | src/locales/en.cpp | 2 | ||||
| -rw-r--r-- | src/main_linux.cpp | 4 | ||||
| -rw-r--r-- | src/providers/MailerSend.cpp | 11 | ||||
| -rw-r--r-- | src/ui/imgui_extensions.cpp | 14 | ||||
| -rw-r--r-- | src/ui/ui_expenses.cpp | 20 | ||||
| -rw-r--r-- | src/ui/ui_invoices.cpp | 13 |
11 files changed, 60 insertions, 27 deletions
@@ -18,6 +18,8 @@ Testing: - write tests for document saving Features: +- status window for ongoing requests, used can click on item when result is ready. Should be usefull for batch imports. +- implement gemini and perplexity api backends - Refactor contact and project UI to be like invoice & expenses - Handle invalid api key response from AI backends and display in settings UI - error log for tax report to display invoices not yet supported for tax generation or invoices with invalid tax rates diff --git a/include/config.hpp b/include/config.hpp index 84773d1..ed1feed 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -30,8 +30,9 @@ #define u32 uint32_t #define u64 uint64_t -#define SIMULATE_EMAIL_FAILURE 1 -#define SIMULATE_EMAIL 1 +#define SIMULATE_AI_IMPORT 1 +#define SIMULATE_EMAIL_FAILURE 0 +#define SIMULATE_EMAIL 0 #define SIMULATE_SLOW_DISK 0 #define SIMULATE_WRITE_FAILURE 0 diff --git a/libs/imgui/imgui.cpp b/libs/imgui/imgui.cpp index e42993c..a4c7e56 100644 --- a/libs/imgui/imgui.cpp +++ b/libs/imgui/imgui.cpp @@ -16680,7 +16680,7 @@ void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_ // 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, - const int circle_count, const float speed) { + const int circle_count, const float speed, float circle_radius = 0.0f) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) { return; @@ -16690,7 +16690,7 @@ void ImGui::LoadingIndicatorCircle(const float indicator_radius, //const ImGuiID id = window->GetID(label); const ImVec2 pos = window->DC.CursorPos; - const float circle_radius = indicator_radius / 12.0f; + if (circle_radius == 0.0f) circle_radius = indicator_radius / 12.0f; const float updated_indicator_radius = indicator_radius - 4.0f * circle_radius; const ImRect bb(pos, ImVec2(pos.x + indicator_radius * 2.0f, pos.y + indicator_radius * 2.0f)); //ItemSize(bb); diff --git a/libs/imgui/imgui.h b/libs/imgui/imgui.h index bfe7b0f..3f4ee8c 100644 --- a/libs/imgui/imgui.h +++ b/libs/imgui/imgui.h @@ -380,7 +380,7 @@ namespace ImGui 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); + const int circle_count, const float speed, float circle_radius); // Context creation and access // - Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between contexts. diff --git a/src/importer.cpp b/src/importer.cpp index 23bdcbe..0027da9 100644 --- a/src/importer.cpp +++ b/src/importer.cpp @@ -210,8 +210,8 @@ static int _ai_document_to_invoice_t(void *arg) char* file_path = request->file_path; importer::ai_provider_impl impl = importer::get_ai_provider_implementation(administration::get_active_ai_service().provider); + #if !SIMULATE_AI_IMPORT request->status = importer::import_status::IMPORT_UPLOADING_FILE; - char file_id[100]; if (!impl.upload_file(file_path, file_id, 100)) { request->status = importer::import_status::IMPORT_DONE; @@ -256,6 +256,14 @@ static int _ai_document_to_invoice_t(void *arg) request->error = I_ERR_FAILED_QUERY; return 0; } + #else + invoice inv = administration::invoice_create_empty(); + usleep(5000 * 1000); + request->status = importer::import_status::IMPORT_UPLOADING_FILE; + usleep(5000 * 1000); + request->status = importer::import_status::IMPORT_WAITING_FOR_RESPONSE; + usleep(5000 * 1000); + #endif inv.extras.status = invoice_status::INVOICE_RECEIVED; diff --git a/src/locales/en.cpp b/src/locales/en.cpp index c03fcc3..673c3b7 100644 --- a/src/locales/en.cpp +++ b/src/locales/en.cpp @@ -343,7 +343,7 @@ locale_entry en_locales[] = { {"settings.services.ai_service.model", "Model"}, {"settings.services.ai_service.apikey", "API key"}, - {"settings.services.ai_service", "Email Service"}, + {"settings.services.email_service", "Email Service"}, {"settings.services.email_service.provider", "Provider"}, {"settings.services.email_service.apikey", "API key"}, diff --git a/src/main_linux.cpp b/src/main_linux.cpp index 95177ef..092fd80 100644 --- a/src/main_linux.cpp +++ b/src/main_linux.cpp @@ -69,13 +69,13 @@ static void _create_window(bool is_setup_window) else { glfwWindowHint(GLFW_RESIZABLE, true); } - + + glfwWindowHint(GLFW_SAMPLES, 4); window = glfwCreateWindow(windowWidth, windowHeight, "OpenBooks", nullptr, nullptr); if (window == nullptr) return; glfwMakeContextCurrent(window); glfwSwapInterval(1); - glfwWindowHint(GLFW_SAMPLES, 4); IMGUI_CHECKVERSION(); ImGui::CreateContext(); diff --git a/src/providers/MailerSend.cpp b/src/providers/MailerSend.cpp index dbae4c6..fe83aea 100644 --- a/src/providers/MailerSend.cpp +++ b/src/providers/MailerSend.cpp @@ -31,7 +31,6 @@ bool _MailerSend_send_email(const char* sender, const char* recipient, const cha const char *api_key = administration::get_email_service().api_key; httplib::SSLClient cli("api.mailersend.com", 443); - cli.enable_server_certificate_verification(false); cli.set_connection_timeout(15, 0); size_t body_size = 10000; @@ -60,9 +59,15 @@ bool _MailerSend_send_email(const char* sender, const char* recipient, const cha httplib::Result res = cli.Post("/v1/email", headers, body, "application/json"); memops::unalloc(body); + + if (!res) { + logger::error("Failed to send email. Reason: Timeout"); + return E_ERR_FAILED_REQUEST; + } - if (!res || (res->status != 200 && res->status != 202)) { - logger::error("Failed to send email."); + if (res->status != 200 && res->status != 202) { + int status = res->status; + logger::error("Failed to send email. Status code: '%d'", status); return E_ERR_FAILED_REQUEST; } diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp index 496bbe2..dee36a7 100644 --- a/src/ui/imgui_extensions.cpp +++ b/src/ui/imgui_extensions.cpp @@ -41,7 +41,7 @@ namespace ImGui float radius = 10.0f; const ImVec4 col = ImGui::GetStyleColorVec4(ImGuiCol_LoadingIndicatorFg); const ImVec4 bg = ImGui::GetStyleColorVec4(ImGuiCol_LoadingIndicatorBg); - ImGui::LoadingIndicatorCircle(radius, bg, col, 6, 4.0f); + ImGui::LoadingIndicatorCircle(radius, bg, col, 6, 4.0f, 0.0f); } bool CheckboxX(const char* label, bool* v, bool disabled, bool show_loading_indicator_while_disabled) @@ -85,14 +85,14 @@ namespace ImGui static void DrawSuccessMark(int bWidth = 0, bool isButton = true) { ImGui::PushStyleColor(ImGuiCol_Text, config::colors::COLOR_SUCCESS); - if (isButton) ImGui::Button(ICON_FA_CHECK_SQUARE, ImVec2(bWidth, 0)); else ImGui::Text("√"); + if (isButton) ImGui::Button(ICON_FA_CHECK_SQUARE, ImVec2(bWidth, 0)); else ImGui::Text(ICON_FA_CHECK_SQUARE); ImGui::PopStyleColor(); } static void DrawFailureMark(int bWidth = 0, bool isButton = true) { ImGui::PushStyleColor(ImGuiCol_Text, config::colors::COLOR_ERROR); - if (isButton) ImGui::Button(ICON_FA_BAN, ImVec2(bWidth, 0)); else ImGui::Text("√"); + if (isButton) ImGui::Button(ICON_FA_BAN, ImVec2(bWidth, 0)); else ImGui::Text(ICON_FA_BAN); ImGui::PopStyleColor(); } @@ -796,7 +796,7 @@ namespace ImGui { static bool is_new_request = false; static bool show_status_change = false; - static time_t status_changed_at = 0; + static double status_changed_at = 0; static e_err last_err; if (active_request && active_request->status == exporter::export_status::EXPORT_DONE && is_new_request) @@ -804,7 +804,7 @@ namespace ImGui is_new_request = false; show_status_change = true; last_err = active_request->error; - status_changed_at = time(NULL); + status_changed_at = ImGui::GetTime(); } if (active_request && active_request->status != exporter::export_status::EXPORT_DONE) { @@ -834,14 +834,14 @@ namespace ImGui } else { DrawFailureMark(0, false); - ImGui::Text(locale::get("form.failure")); + ImGui::Text(locale::get("form.failed")); } ImGui::EndComboPreview(); } } ImGui::EndDisabled(); - if (difftime(time(NULL), status_changed_at) > 0.5f) { + if (ImGui::GetTime() - status_changed_at >= 1.0f) { show_status_change = false; } } diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp index 6ac043f..64dbc18 100644 --- a/src/ui/ui_expenses.cpp +++ b/src/ui/ui_expenses.cpp @@ -20,6 +20,7 @@ #include "ui.hpp" #include "memops.hpp" +#include "assets.hpp" #include "strops.hpp" #include "locales.hpp" #include "importer.hpp" @@ -431,6 +432,9 @@ static void draw_import_request() } } + ai_service ai = administration::get_active_ai_service(); + importer::ai_provider_impl ai_impl = importer::get_ai_provider_implementation(ai.provider); + ImGui::PushFont(ui::fontBig); ImVec2 windowSize = ImGui::GetWindowSize(); @@ -439,17 +443,25 @@ static void draw_import_request() const char* text = importer::status_to_string(active_import_request->status); if (active_import_request->error != I_ERR_SUCCESS) text = importer::error_to_string(active_import_request->error); ImVec2 textSize = ImGui::CalcTextSize(text); - ImGui::SetCursorPos(ImVec2((windowSize.x - textSize.x) * 0.5f, + + ImGui::SetCursorPos(ImVec2((windowSize.x - textSize.x) * 0.5f - 24, (windowSize.y) * 0.5f - radius - 40.0f)); + ImGui::Image(assets::load_image(ai_impl.icon_name), ImVec2(24, 24)); + + + ImGui::SetCursorPos(ImVec2((windowSize.x - textSize.x) * 0.5f + 10, + (windowSize.y) * 0.5f - radius - 40.0f - 2)); ImGui::Text(text); if (active_import_request->error == I_ERR_SUCCESS) { ImGui::SetCursorPos(ImVec2((windowSize.x - radius*2) * 0.5f, (windowSize.y - radius*2) * 0.5f)); - const ImVec4 col = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); - const ImVec4 bg = ImGui::GetStyleColorVec4(ImGuiCol_Button); - ImGui::LoadingIndicatorCircle(radius, bg, col, 10, 4.0f); + const ImVec4 col = ImGui::GetStyleColorVec4(ImGuiCol_LoadingIndicatorFg); + const ImVec4 bg = ImGui::GetStyleColorVec4(ImGuiCol_LoadingIndicatorBg); + ImGui::LoadingIndicatorCircle(radius, bg, col, 10, 4.0f, 0.0f); + + ImGui::Dummy(ImVec2(200,200)); } ImGui::PopFont(); diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp index ffdfb9b..e7872d1 100644 --- a/src/ui/ui_invoices.cpp +++ b/src/ui/ui_invoices.cpp @@ -49,11 +49,13 @@ static void _set_active_invoice(invoice inv) void ui::destroy_invoices() { - memops::unalloc(invoice_items_buffer); - memops::unalloc(activity_buffer); + // Currently causes crash because of multithreaded imports. + // Main thread needs to be locked. + //memops::unalloc(invoice_items_buffer); + //memops::unalloc(activity_buffer); - invoice_items_buffer = 0; - activity_buffer = 0; + //invoice_items_buffer = 0; + //activity_buffer = 0; } void ui::setup_invoices() @@ -497,6 +499,9 @@ static void draw_send_options() _reload_activities(); } + else { + // @TODO show failure reason + } }); } // if (ImGui::Selectable(locale::get("ui.sendAs.einvoice"), false)) { |
