presentation/QuickContactVcf.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QObject> | ||
| 4 | #include <QStringList> | ||
| 5 | #include <QUrl> | ||
| 6 | #include <QVariantList> | ||
| 7 | #include <functional> | ||
| 8 | #include <optional> | ||
| 9 | |||
| 10 | #include "data/MailRequest.h" | ||
| 11 | #include "service/MailPayloadExportService.h" | ||
| 12 | |||
| 13 | class ContactSyncCoordinator; | ||
| 14 | class MailSessionCore; | ||
| 15 | |||
| 16 | // vCard files (Sprint 97 S97.5.8, CJ10; QML `backend.vcf`). | ||
| 17 | // | ||
| 18 | // Import: openFile() (file dialog) or openAttachment() (a .vcf in the | ||
| 19 | // displayed mail) read the cards and fill a preview — name, address and | ||
| 20 | // whether the card already exists in the target book (same UID or same | ||
| 21 | // address). importAll(bookId, duplicates) writes them through the contact | ||
| 22 | // outbox: "skip" leaves existing cards, "update" replaces the card with the | ||
| 23 | // same UID (a card that only shares an address is left alone), "both" adds a | ||
| 24 | // second card. 3.0/4.0 cards are stored as written. | ||
| 25 | // | ||
| 26 | // Export: exportKeys() / exportBook() / exportGroup() write the stored raw | ||
| 27 | // cards unchanged into one .vcf in the download folder. | ||
| 28 | // Sprint 103 S103.3.9 (D-22, interaction.md §11): when a card holds | ||
| 29 | // personal details (VCardExportFilter), the export waits for an answer: | ||
| 30 | // `question` describes it ({title, action, count}), answerQuestion(go, | ||
| 31 | // include) runs or drops it; without the checkbox the details are left out. | ||
| 32 | // "Send as vCard" then reports its file through attachmentReady(). | ||
| 33 | class QuickContactVcf final : public QObject { | ||
| 34 | 37 | Q_OBJECT | |
| 35 | Q_PROPERTY(bool active READ active NOTIFY changed) | ||
| 36 | Q_PROPERTY(QString sourceName READ sourceName NOTIFY changed) | ||
| 37 | // [{name, address, duplicate}] — duplicate is "", "uid" or "address". | ||
| 38 | Q_PROPERTY(QVariantList items READ items NOTIFY changed) | ||
| 39 | Q_PROPERTY(int duplicateCount READ duplicateCount NOTIFY changed) | ||
| 40 | // {title, action, count} while an export waits for the personal-details answer; {} otherwise. | ||
| 41 | Q_PROPERTY(QVariantMap question READ question NOTIFY questionChanged) | ||
| 42 | public: | ||
| 43 | struct Deps { | ||
| 44 | std::function<MailId()> currentMail; | ||
| 45 | std::function<void(const QString &)> setStatus; | ||
| 46 | QString downloadDirectory; // "" = the platform download folder | ||
| 47 | }; | ||
| 48 | static constexpr qint64 MaxBytes = 16 * 1024 * 1024; | ||
| 49 | // S106-286 (INV-14): the preview parses every card and checks it for | ||
| 50 | // duplicates on the GUI thread; past this many cards the rest of the | ||
| 51 | // file is left out (the ICS import's IcsFile::MaxEntries rule). | ||
| 52 | static constexpr int MaxCards = 5000; | ||
| 53 | // A mail attachment is read in one payload window, which the export | ||
| 54 | // service limits to 8 MiB (a larger request is refused outright). | ||
| 55 | static constexpr qint64 AttachmentMaxBytes = MailPayloadExportService::MaximumWindowBytes; | ||
| 56 | QuickContactVcf(MailSessionCore &session, Deps deps, QObject *parent = nullptr); | ||
| 57 | |||
| 58 | 26 | bool active() const { return !m_cards.isEmpty(); } | |
| 59 | 775 | QString sourceName() const { return m_sourceName; } | |
| 60 | 2327 | QVariantList items() const { return m_items; } | |
| 61 | int duplicateCount() const; | ||
| 62 | QVariantMap question() const; | ||
| 63 | |||
| 64 | Q_INVOKABLE bool openFile(const QUrl &url); | ||
| 65 | Q_INVOKABLE bool openAttachment(qint64 attachmentId, const QString &name); | ||
| 66 | bool openText(const QString &text, const QString &name); | ||
| 67 | // Recomputes the duplicate column for the chosen target book. | ||
| 68 | Q_INVOKABLE void setTargetBook(qint64 bookId); | ||
| 69 | // {ok, imported, updated, skipped} | ||
| 70 | Q_INVOKABLE QVariantMap importAll(qint64 bookId, const QString &duplicates); | ||
| 71 | Q_INVOKABLE void close(); | ||
| 72 | |||
| 73 | Q_INVOKABLE bool exportKeys(const QStringList &keys, const QString &title); | ||
| 74 | Q_INVOKABLE bool exportBook(qint64 bookId); | ||
| 75 | Q_INVOKABLE bool exportGroup(qint64 bookId, const QString &group); | ||
| 76 | // S97.5.8 "Send as vCard": the cards as "<title>.vcf" in a fresh folder | ||
| 77 | // under the cache directory (owner-only, removed after 7 days); the path | ||
| 78 | // for the composer's attachment list, "" on failure. | ||
| 79 | Q_INVOKABLE QString attachmentFile(const QStringList &keys, const QString &title); | ||
| 80 | // The answer to `question`: `go` false drops the export. | ||
| 81 | Q_INVOKABLE void answerQuestion(bool go, bool includePersonal); | ||
| 82 | // Worker-safe file name: "Mara Klein.vcf", controls and separators removed. | ||
| 83 | static QString fileName(const QString &title); | ||
| 84 | |||
| 85 | signals: | ||
| 86 | void changed(); | ||
| 87 | // The cards that were added or updated (the workspace selects the first). | ||
| 88 | void imported(const QStringList &keys); | ||
| 89 | void questionChanged(); | ||
| 90 | // "Send as vCard" after the question: the file for the composer. | ||
| 91 | void attachmentReady(const QString &path); | ||
| 92 | |||
| 93 | private: | ||
| 94 | // An export waiting for the personal-details answer. | ||
| 95 | struct Pending { | ||
| 96 | QStringList raws; | ||
| 97 | QString title; | ||
| 98 | QString sheetTitle; | ||
| 99 | bool attachment = false; | ||
| 100 | }; | ||
| 101 | // Runs the export now, or holds it and asks when a card has personal details. | ||
| 102 | bool startExport(const QStringList &raws, const QString &title, const QString &sheetTitle, bool attachment); | ||
| 103 | bool writeExport(const QStringList &raws, const QString &title, const QString &status = {}); | ||
| 104 | QString writeAttachment(const QStringList &raws, const QString &title); | ||
| 105 | QStringList rawCards(const QStringList &keys) const; | ||
| 106 | ContactSyncCoordinator *coordinator() const; | ||
| 107 | |||
| 108 | MailSessionCore &m_session; | ||
| 109 | Deps m_deps; | ||
| 110 | QStringList m_cards; | ||
| 111 | QVariantList m_items; | ||
| 112 | QString m_sourceName; | ||
| 113 | qint64 m_targetBook = 0; | ||
| 114 | quint64 m_readGeneration = 0; | ||
| 115 | std::optional<Pending> m_pending; | ||
| 116 | }; | ||
| 117 |