Files
panopainter/src/app_core/document_cloud.h
2026-06-05 08:14:11 +02:00

316 lines
8.5 KiB
C++

#pragma once
#include "app_core/app_dialog.h"
#include "foundation/result.h"
#include <cstdio>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <string>
#include <string_view>
namespace pp::app {
enum class CloudUploadAction {
unavailable_no_canvas,
show_save_required_warning,
prompt_publish,
};
enum class CloudBrowseAction {
unavailable_no_canvas,
show_browser,
};
enum class CloudDownloadSelectionAction {
wait_for_selection,
start_download,
};
enum class CloudTransferDirection {
download,
upload,
};
enum class CloudTransferAction {
reject_missing_source,
reject_missing_destination,
start_transfer,
};
struct CloudUploadPlan {
CloudUploadAction action = CloudUploadAction::unavailable_no_canvas;
bool save_before_upload = false;
};
struct CloudBulkUploadPlan {
std::size_t file_count = 0;
int progress_total = 0;
bool show_progress = false;
};
struct CloudDownloadRequest {
std::string selected_file;
std::string selected_path;
std::string selected_name;
};
struct CloudTransferPlan {
CloudTransferDirection direction = CloudTransferDirection::download;
CloudTransferAction action = CloudTransferAction::reject_missing_source;
bool enable_progress = false;
bool disable_tls_verification = false;
};
struct CloudTransferProgressPlan {
bool notify = false;
float fraction = 0.0F;
};
[[nodiscard]] inline AppMessageDialogPlan plan_cloud_save_required_prompt()
{
return plan_app_message_dialog(
"Warning",
"This document needs to be saved before upload.",
false);
}
[[nodiscard]] inline AppMessageDialogPlan plan_cloud_publish_prompt()
{
return plan_app_message_dialog(
"Publish document",
"Would you like to upload to the public domain?",
true,
"Yes",
"No");
}
[[nodiscard]] inline AppMessageDialogPlan plan_cloud_upload_success_prompt()
{
return plan_app_message_dialog(
"Success",
"This document has been succesfully uploaded.",
false);
}
[[nodiscard]] inline AppProgressDialogPlan plan_cloud_upload_progress_dialog()
{
return plan_app_progress_dialog("Uploading", 0);
}
[[nodiscard]] inline AppProgressDialogPlan plan_cloud_bulk_upload_progress_dialog(int progress_total)
{
return plan_app_progress_dialog("Export Pano Image", progress_total);
}
[[nodiscard]] inline AppMessageDialogPlan plan_cloud_download_progress_prompt()
{
return plan_app_message_dialog(
"Downloading",
"Download in progress",
true);
}
[[nodiscard]] inline std::string format_cloud_download_progress_message(float progress_fraction)
{
char buffer[64] {};
std::snprintf(
buffer,
sizeof(buffer),
"Download in progress %.2f%%",
progress_fraction * 100.0F);
return buffer;
}
class CloudServices {
public:
virtual ~CloudServices() = default;
virtual void show_save_required_warning() = 0;
virtual void prompt_publish(bool save_before_upload) = 0;
virtual void begin_bulk_upload(int progress_total, bool show_progress) = 0;
virtual void upload_all_bulk_files() = 0;
virtual void end_bulk_upload() = 0;
virtual void show_browser() = 0;
virtual void start_download(const CloudDownloadRequest& request) = 0;
};
[[nodiscard]] constexpr CloudUploadPlan plan_cloud_upload(
bool has_canvas,
bool is_new_document,
bool has_unsaved_changes) noexcept
{
if (!has_canvas) {
return { CloudUploadAction::unavailable_no_canvas, false };
}
if (is_new_document) {
return { CloudUploadAction::show_save_required_warning, false };
}
return { CloudUploadAction::prompt_publish, has_unsaved_changes };
}
[[nodiscard]] constexpr CloudBrowseAction plan_cloud_browse(bool has_canvas) noexcept
{
return has_canvas
? CloudBrowseAction::show_browser
: CloudBrowseAction::unavailable_no_canvas;
}
[[nodiscard]] constexpr CloudDownloadSelectionAction plan_cloud_download_selection(
std::string_view selected_file) noexcept
{
return selected_file.empty()
? CloudDownloadSelectionAction::wait_for_selection
: CloudDownloadSelectionAction::start_download;
}
[[nodiscard]] constexpr CloudBulkUploadPlan plan_cloud_bulk_upload(
std::size_t file_count,
bool progress_ui_available) noexcept
{
const auto max_progress_total = static_cast<std::size_t>(std::numeric_limits<int>::max());
return {
file_count,
file_count > max_progress_total ? std::numeric_limits<int>::max() : static_cast<int>(file_count),
progress_ui_available,
};
}
[[nodiscard]] constexpr CloudTransferPlan plan_cloud_download_transfer(
std::string_view url,
std::string_view destination_path,
bool has_progress_callback,
bool disables_tls_verification) noexcept
{
if (url.empty()) {
return {
CloudTransferDirection::download,
CloudTransferAction::reject_missing_source,
false,
false,
};
}
if (destination_path.empty()) {
return {
CloudTransferDirection::download,
CloudTransferAction::reject_missing_destination,
false,
false,
};
}
return {
CloudTransferDirection::download,
CloudTransferAction::start_transfer,
has_progress_callback,
disables_tls_verification,
};
}
[[nodiscard]] constexpr CloudTransferPlan plan_cloud_upload_transfer(
std::string_view filename,
bool has_progress_callback,
bool disables_tls_verification) noexcept
{
if (filename.empty()) {
return {
CloudTransferDirection::upload,
CloudTransferAction::reject_missing_source,
false,
false,
};
}
return {
CloudTransferDirection::upload,
CloudTransferAction::start_transfer,
has_progress_callback,
disables_tls_verification,
};
}
[[nodiscard]] constexpr CloudTransferProgressPlan plan_cloud_transfer_progress(
std::int64_t total,
std::int64_t current) noexcept
{
if (total <= 0) {
return {};
}
const auto clamped_current = current < 0
? std::int64_t { 0 }
: (current > total ? total : current);
return {
true,
static_cast<float>(static_cast<double>(clamped_current) / static_cast<double>(total)),
};
}
[[nodiscard]] inline pp::foundation::Status execute_cloud_upload_plan(
const CloudUploadPlan& plan,
CloudServices& services)
{
switch (plan.action) {
case CloudUploadAction::unavailable_no_canvas:
return pp::foundation::Status::success();
case CloudUploadAction::show_save_required_warning:
services.show_save_required_warning();
return pp::foundation::Status::success();
case CloudUploadAction::prompt_publish:
services.prompt_publish(plan.save_before_upload);
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("unknown cloud upload action");
}
[[nodiscard]] inline pp::foundation::Status execute_cloud_bulk_upload_plan(
const CloudBulkUploadPlan& plan,
CloudServices& services)
{
services.begin_bulk_upload(plan.progress_total, plan.show_progress);
services.upload_all_bulk_files();
services.end_bulk_upload();
return pp::foundation::Status::success();
}
[[nodiscard]] inline pp::foundation::Status execute_cloud_browse_action(
CloudBrowseAction action,
CloudServices& services)
{
switch (action) {
case CloudBrowseAction::unavailable_no_canvas:
return pp::foundation::Status::success();
case CloudBrowseAction::show_browser:
services.show_browser();
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("unknown cloud browse action");
}
[[nodiscard]] inline pp::foundation::Status execute_cloud_download_selection_action(
CloudDownloadSelectionAction action,
CloudServices& services,
const CloudDownloadRequest& request)
{
switch (action) {
case CloudDownloadSelectionAction::wait_for_selection:
return pp::foundation::Status::success();
case CloudDownloadSelectionAction::start_download:
if (request.selected_file.empty()) {
return pp::foundation::Status::invalid_argument("cloud download requires a selected file");
}
services.start_download(request);
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("unknown cloud download selection action");
}
}