ORE Studio 0.0.4
Loading...
Searching...
No Matches
ImageCache.hpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2025 Marco Craveiro <marco.craveiro@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20#ifndef ORES_QT_IMAGE_CACHE_HPP
21#define ORES_QT_IMAGE_CACHE_HPP
22
23#include <string>
24#include <unordered_map>
25#include <unordered_set>
26#include <QObject>
27#include <QIcon>
28#include <QFutureWatcher>
29#include "ores.qt/ClientManager.hpp"
30#include "ores.logging/make_logger.hpp"
31#include "ores.assets.api/domain/image.hpp"
32#include "ores.assets.api/messaging/assets_protocol.hpp"
33#include "ores.qt/export.hpp"
34
35namespace ores::qt {
36
54class ORES_QT_API ImageCache final : public QObject {
55 Q_OBJECT
56
57private:
58 inline static std::string_view logger_name = "ores.qt.image_cache";
59
60 [[nodiscard]] static auto& lg() {
61 using namespace ores::logging;
62 static auto instance = make_logger(logger_name);
63 return instance;
64 }
65
66public:
67 explicit ImageCache(ClientManager* clientManager, QObject* parent = nullptr);
68 ~ImageCache() override = default;
69
77 void loadAll();
78
85 void reload();
86
97 QIcon getIcon(const std::string& image_id);
98
105 bool hasIcon(const std::string& image_id) const;
106
110 std::size_t cachedIconCount() const { return image_icons_.size(); }
111
115 bool isLoading() const { return is_loading_images_; }
116
125 void clear();
126
133 void loadImageList();
134
140 const std::vector<assets::messaging::image_info>& availableImages() const {
141 return available_images_;
142 }
143
147 bool hasImageList() const { return !available_images_.empty(); }
148
155 void loadAllAvailableImages();
156
164 void setCurrencyImage(const std::string& iso_code, const std::string& image_id,
165 const std::string& assigned_by);
166
174 void setCountryImage(const std::string& alpha2_code, const std::string& image_id,
175 const std::string& assigned_by);
176
182 std::string getNoFlagImageId() const;
183
189 QIcon getNoFlagIcon() const;
190
197 QIcon getCurrencyFlagIcon(const std::string& iso_code);
198
205 QIcon getCountryFlagIcon(const std::string& alpha2_code);
206
213 QIcon getBusinessCentreFlagIcon(const std::string& bc_code);
214
215signals:
220
224 void allLoaded();
225
229 void loadError(const QString& error_message);
230
235
241 void imageLoaded(const QString& image_id);
242
247
251 void currencyImageSet(const QString& iso_code, bool success, const QString& message);
252
256 void countryImageSet(const QString& alpha2_code, bool success, const QString& message);
257
258private slots:
259 void onCurrencyImageIdsLoaded();
260 void onCountryImageIdsLoaded();
261 void onBusinessCentreMappingLoaded();
262 void onImagesLoaded();
263 void onImageListLoaded();
264 void onSingleImageLoaded();
265 void onCurrencyImageSet();
266 void onCountryImageSet();
267 void onAllAvailableImagesLoaded();
268 void onIncrementalChangesLoaded();
269
270private:
277 static QIcon svgToIcon(const std::string& svg_data);
278
284 void loadImageById(const std::string& image_id);
285
289 void loadCurrencyImageIds();
290
294 void loadCountryImageIds();
295
299 void loadImagesByIds(const std::vector<std::string>& image_ids);
300
304 void loadBusinessCentreMapping();
305
311 void loadIncrementalChanges();
312
313 struct ImageIdsResult {
314 bool success;
315 std::vector<std::string> image_ids;
316 // Code -> image_id mappings populated during fetch
317 std::unordered_map<std::string, std::string> code_to_image_id;
318 };
319
320 struct BusinessCentreMappingResult {
321 bool success;
322 std::unordered_map<std::string, std::string> bc_to_country;
323 };
324
325 struct ImagesResult {
326 bool success;
327 std::vector<assets::domain::image> images;
328 int failed_batches{0};
329 };
330
338 static ImagesResult fetchImagesInBatches(
339 ClientManager* clientManager,
340 const std::vector<std::string>& image_ids);
341
342 struct ImageListResult {
343 bool success;
344 std::vector<assets::messaging::image_info> images;
345 };
346
347 struct SingleImageResult {
348 bool success;
349 std::string image_id;
351 };
352
353 struct SetCurrencyImageResult {
354 bool success;
355 std::string iso_code;
356 std::string message;
357 };
358
359 struct SetCountryImageResult {
360 bool success;
361 std::string alpha2_code;
362 std::string message;
363 };
364
365 ClientManager* clientManager_;
366
367 // image_id -> cached SVG data
368 std::unordered_map<std::string, std::string> image_svg_cache_;
369
370 // image_id -> QIcon (rendered from SVG)
371 std::unordered_map<std::string, QIcon> image_icons_;
372
373 // Loading state
374 bool is_loading_images_{false};
375 bool is_loading_all_available_{false};
376 bool load_all_in_progress_{false};
377
378 // Image IDs collected during loadAll() for preloading
379 std::vector<std::string> pending_image_ids_;
380
381 QFutureWatcher<ImageIdsResult>* currency_ids_watcher_;
382 QFutureWatcher<ImageIdsResult>* country_ids_watcher_;
383 QFutureWatcher<ImageIdsResult>* incremental_changes_watcher_;
384 QFutureWatcher<ImagesResult>* images_watcher_;
385 QFutureWatcher<ImageListResult>* image_list_watcher_;
386 QFutureWatcher<SingleImageResult>* single_image_watcher_;
387 QFutureWatcher<SetCurrencyImageResult>* set_currency_image_watcher_;
388 QFutureWatcher<SetCountryImageResult>* set_country_image_watcher_;
389 QFutureWatcher<ImagesResult>* all_available_watcher_;
390
391 // List of all available images (metadata only)
392 std::vector<assets::messaging::image_info> available_images_;
393
394 // Track image IDs currently being loaded to prevent duplicate requests
395 std::unordered_set<std::string> pending_image_requests_;
396
397 // Timestamp of last successful load (for incremental loading)
398 std::optional<std::chrono::system_clock::time_point> last_load_time_;
399
400 // Code -> image_id mappings for flag icon lookups
401 std::unordered_map<std::string, std::string> currency_iso_to_image_id_;
402 std::unordered_map<std::string, std::string> country_alpha2_to_image_id_;
403 std::unordered_map<std::string, std::string> bc_code_to_country_alpha2_;
404
405 QFutureWatcher<BusinessCentreMappingResult>* bc_mapping_watcher_;
406};
407
408}
409
410#endif
Implements logging infrastructure for ORE Studio.
Definition boost_severity.hpp:28
Qt-based graphical user interface for ORE Studio.
Definition AccountController.hpp:32
Represents a dynamically loaded image (typically SVG).
Definition image.hpp:34
Manages the lifecycle of the NATS client and login state.
Definition ClientManager.hpp:123
Cache for dynamically loaded images (flags, icons) from the server.
Definition ImageCache.hpp:54
void imageLoaded(const QString &image_id)
Emitted when a single image has been loaded.
std::size_t cachedIconCount() const
Get the number of cached images.
Definition ImageCache.hpp:110
void countryImageSet(const QString &alpha2_code, bool success, const QString &message)
Emitted when country image assignment is complete.
void allLoaded()
Emitted when all data has been loaded (after loadAll()).
const std::vector< assets::messaging::image_info > & availableImages() const
Get the list of available images.
Definition ImageCache.hpp:140
bool isLoading() const
Check if images are currently being loaded.
Definition ImageCache.hpp:115
bool hasImageList() const
Check if image list has been loaded.
Definition ImageCache.hpp:147
void imageListLoaded()
Emitted when image list has been loaded.
void loadError(const QString &error_message)
Emitted when an error occurs during loading.
void imagesLoaded()
Emitted when images have been loaded.
void currencyImageSet(const QString &iso_code, bool success, const QString &message)
Emitted when currency image assignment is complete.
void allAvailableImagesLoaded()
Emitted when all available images have been loaded.