presentation/QuickSourceViewer.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QObject> | ||
| 4 | #include <QVariantList> | ||
| 5 | #include <QByteArray> | ||
| 6 | #include <QString> | ||
| 7 | #include <QUrl> | ||
| 8 | #include <functional> | ||
| 9 | #include <memory> | ||
| 10 | |||
| 11 | class MailPayloadPreview; | ||
| 12 | class MailSessionCore; | ||
| 13 | class WorkspaceState; | ||
| 14 | |||
| 15 | // Raw-source view, inline PDF preview and attachment export of the selected | ||
| 16 | // mail (Sprint 91 S91.1.2, extracted from QuickMailBackend). QML reaches it | ||
| 17 | // as `backend.source`. | ||
| 18 | // | ||
| 19 | // Owner: QuickMailBackend (value member). GUI thread only. Payload reads, | ||
| 20 | // saves and previews run on the shared MailPayloadExportService worker; | ||
| 21 | // callbacks are owned by this object (saves/source reads) or by m_pdfOwner | ||
| 22 | // (previews), so a selection change cancels only previews. connectSignals() | ||
| 23 | // is called by the owner where wireSourceView() was: its | ||
| 24 | // WorkspaceState::selectionChanged handler stays the 6th receiver. | ||
| 25 | class QuickSourceViewer final : public QObject { | ||
| 26 | 8 | Q_OBJECT | |
| 27 | Q_PROPERTY(bool sourceActive READ sourceActive NOTIFY sourceChanged) | ||
| 28 | Q_PROPERTY(QString sourceText READ sourceText NOTIFY sourceChanged) | ||
| 29 | Q_PROPERTY(bool sourceFullyLoaded READ sourceFullyLoaded NOTIFY sourceChanged) | ||
| 30 | Q_PROPERTY(bool sourcePending READ sourcePending NOTIFY sourceChanged) | ||
| 31 | Q_PROPERTY(qint64 sourceTotalBytes READ sourceTotalBytes NOTIFY sourceChanged) | ||
| 32 | Q_PROPERTY(bool pdfActive READ pdfActive NOTIFY pdfChanged) | ||
| 33 | Q_PROPERTY(QUrl pdfSource READ pdfSource NOTIFY pdfChanged) | ||
| 34 | Q_PROPERTY(QString pdfName READ pdfName NOTIFY pdfChanged) | ||
| 35 | Q_PROPERTY(QVariantList pdfRejectedAttachments READ pdfRejectedAttachments NOTIFY pdfChanged) | ||
| 36 | public: | ||
| 37 | struct Deps { | ||
| 38 | std::function<bool()> configured; | ||
| 39 | std::function<void(const QString &)> setStatus; | ||
| 40 | }; | ||
| 41 | QuickSourceViewer(MailSessionCore &session, WorkspaceState &workspace, | ||
| 42 | const QString &profileDirectory, Deps deps, QObject *parent = nullptr); | ||
| 43 | void connectSignals(); | ||
| 44 | |||
| 45 | // Exports one attachment of the selected mail to the user's download | ||
| 46 | // directory. The attachment must belong to that mail's cached metadata; | ||
| 47 | // completion and failure surface as status-line feedback. | ||
| 48 | Q_INVOKABLE bool saveAttachment(qint64 attachmentId); | ||
| 49 | // Bounded raw-source view (T-87.G1 streaming): windows are read from the | ||
| 50 | // cache by the shared payload worker; identity/epoch are fenced per | ||
| 51 | // request and the view closes when the selected mail changes. | ||
| 52 | Q_INVOKABLE void openSource(); | ||
| 53 | Q_INVOKABLE void loadMoreSource(); | ||
| 54 | Q_INVOKABLE void closeSource(); | ||
| 55 | Q_INVOKABLE bool saveSource(); | ||
| 56 | 795 | bool sourceActive() const { return m_sourceActive; } | |
| 57 | 802 | QString sourceText() const { return m_sourceText; } | |
| 58 | 799 | bool sourceFullyLoaded() const { return m_sourceFullyLoaded; } | |
| 59 | 1577 | bool sourcePending() const { return m_sourcePending; } | |
| 60 | 1574 | qint64 sourceTotalBytes() const { return m_sourceTotal; } | |
| 61 | // Inline PDF viewing: prepares a complete private snapshot file through the | ||
| 62 | // shared payload worker and keeps its lease while the overlay shows it. | ||
| 63 | Q_INVOKABLE void viewAttachmentPdf(qint64 attachmentId); | ||
| 64 | Q_INVOKABLE void closePdf(); | ||
| 65 | 780 | bool pdfActive() const { return m_pdfActive; } | |
| 66 | 6 | QUrl pdfSource() const { return m_pdfSource; } | |
| 67 | 3 | QString pdfName() const { return m_pdfName; } | |
| 68 | 20 | QVariantList pdfRejectedAttachments() const { return m_pdfRejectedAttachments; } | |
| 69 | |||
| 70 | signals: | ||
| 71 | void sourceChanged(); | ||
| 72 | void pdfChanged(); | ||
| 73 | |||
| 74 | private: | ||
| 75 | void requestSourceWindow(); | ||
| 76 | void closePdfInternal(); | ||
| 77 | MailSessionCore &m_session; | ||
| 78 | WorkspaceState &m_workspace; | ||
| 79 | const QString &m_profileDirectory; // owner's profile directory (outlives this) | ||
| 80 | Deps m_deps; | ||
| 81 | // Bounded raw-source view state (see openSource()). | ||
| 82 | bool m_sourceActive = false; | ||
| 83 | bool m_sourceFullyLoaded = false; | ||
| 84 | bool m_sourcePending = false; | ||
| 85 | QString m_sourceText; | ||
| 86 | qint64 m_sourceSkip = 0; | ||
| 87 | qint64 m_sourceTotal = 0; | ||
| 88 | // F-70 (SPRINT-104): view lifetime — open, close and selection change | ||
| 89 | // start a new one; completions of an older lifetime are dropped. | ||
| 90 | quint64 m_sourceGeneration = 0; | ||
| 91 | // F-71 (SPRINT-104): a trailing partial UTF-8 sequence held back across | ||
| 92 | // source-window boundaries. | ||
| 93 | QByteArray m_sourcePendingBytes; | ||
| 94 | static constexpr qint64 SourceWindowBytes = 1024 * 1024; | ||
| 95 | static constexpr qint64 SourceMaximumLoadedBytes = 8 * 1024 * 1024; | ||
| 96 | // Inline PDF state: the preview lease keeps the snapshot file alive while | ||
| 97 | // the overlay shows it. m_pdfOwner isolates preview requests so cancelling | ||
| 98 | // them on selection change never aborts attachment saves or source reads, | ||
| 99 | // which are owned by this viewer itself. | ||
| 100 | QObject m_pdfOwner; | ||
| 101 | std::shared_ptr<const MailPayloadPreview> m_pdfPreview; | ||
| 102 | bool m_pdfActive = false; | ||
| 103 | QUrl m_pdfSource; | ||
| 104 | QString m_pdfName; | ||
| 105 | QVariantList m_pdfRejectedAttachments; | ||
| 106 | }; | ||
| 107 |