#pragma once #include #include #include // Persists the non-secret settings (base URL, username, last post) in a // QSettings INI under ~/.config, and the password in the system keychain // (libsecret / kwallet via QtKeychain). The password never lands on disk in the // clear, mirroring the Android app's Keystore sealing. class Settings { public: struct LastPost { QString slug; QString url; QString caption; QDateTime postedAt; bool valid() const { return !slug.isEmpty(); } }; Settings(); QString baseUrl() const; void setBaseUrl(const QString &value); QString username() const; void setUsername(const QString &value); // Max photos uploaded at once in a bulk post, clamped to [1, kMaxConcurrency]. static constexpr int kMaxConcurrency = 8; static constexpr int kDefaultConcurrency = 3; int concurrency() const; void setConcurrency(int value); // Keychain access is synchronous here (a local event loop): the app does // nothing until the credential is in hand, and there is no background retry // worker as on Android that would need it off the UI thread. QString readPassword() const; void writePassword(const QString &value) const; bool isConfigured() const; LastPost lastPost() const; void setLastPost(const LastPost &value); // Returns an error message, or an empty string when the URL is usable. // https is required: Basic auth would otherwise send the password in the // clear. static QString validateBaseUrl(const QString &raw); // Trims trailing slashes and a trailing "/upload" (the endpoint path a user // pasting the upload URL would leave on — the client appends it itself). static QString normalizeBaseUrl(const QString &raw); private: mutable QSettings m_settings; };