1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
#pragma once
#include <optional>
#include <QDateTime>
#include <QDebug>
#include <QDomDocument>
#include <QList>
#include <QString>
#include <QUrl>
#include "atomchannelimage.h"
#include "atomitem.h"
class QXmlStreamReader;
// TODO: mark items as read in all user channels
#define GADGET_PROPERTY(type, name) \
protected: \
Q_PROPERTY(const type name READ get_##name WRITE set_##name) \
public: \
const type &get_##name() const \
{ \
return m_##name; \
} \
void set_##name(const type &name) \
{ \
m_##name = name; \
} \
\
protected: \
type m_##name
class DbItem : public QObject
{
// Q_GADGET
Q_OBJECT
public:
using DbId = std::optional<qlonglong>;
GADGET_PROPERTY(DbId, id);
static constexpr auto dateFormat = Qt::DateFormat::RFC2822Date;
public:
explicit DbItem(const QDomNode &node);
public:
virtual bool isValid() const = 0;
virtual void init(const QDomNode &node);
// TODO: sync list of items, thus minimizing amount of db queries
virtual DbId syncWithDb();
virtual QStringList propertyNames() const;
virtual QList<QMetaProperty> properties() const;
virtual const QStringList &uniqueFields() const = 0;
};
class Feed : public DbItem
{
Q_OBJECT
GADGET_PROPERTY(QString, title);
GADGET_PROPERTY(QUrl, link);
GADGET_PROPERTY(QUrl, image_url);
public:
explicit Feed(const QDomNode &node);
public:
bool isValid() const override;
const QStringList &uniqueFields() const override;
private:
static inline QStringList m_uniqueFields{{"link"}};
};
// TODO: <image>
class Item : public DbItem
{
Q_OBJECT
GADGET_PROPERTY(QString, title);
GADGET_PROPERTY(QUrl, link);
GADGET_PROPERTY(QStringList, category);
GADGET_PROPERTY(QString, guid);
GADGET_PROPERTY(QDateTime, pubDate);
GADGET_PROPERTY(QString, description);
GADGET_PROPERTY(QString, content);
public:
explicit Item(const QDomNode &node);
public:
bool isValid() const override;
const QStringList &uniqueFields() const override;
private:
static inline QStringList m_uniqueFields{{"link"}};
};
class AtomChannel
{
public:
// TODO: move to interface
static inline const QString xmlTag{"channel"};
public:
explicit AtomChannel(QXmlStreamReader *xmlReader);
public:
// TODO: update db data if differs. here and everywhere (item, user, etc)
/*!
* \brief getDbId - check if channel with corresponding `link` exists in db
* and return db id if any
* \return id on success, 0 otherwise
*/
std::optional<int> getDbId();
/*!
* \brief createInDb - create channel in db
* \return new channel id on success, 0 otherwise
*/
std::optional<int> createInDb();
/*!
* \brief getOrInsertDbId - get existing channel id or try to create a new
* channel and get its id
* \return existing or new channel id on success, 0 otherwise
*/
std::optional<int> getOrInsertDbId();
/*!
* \brief syncDbItems - create items in db if not exist
* \return list of new items ids
*/
QList<int> syncDbItems();
public:
/*!
* \brief m_id - cache db m_id
*/
std::optional<int> m_id{0};
QString title;
/*!
* \brief link - field called `link` in atom xml. Points to feed itself
*/
QUrl link;
QString description;
QDateTime lastBuildDate;
QLocale::Language language;
// ...
// TODO: shared_ptr
AtomChannelImage image;
QList<AtomItem> items;
};
QDebug operator<<(QDebug debug, const AtomChannel &channel);
QDebug operator<<(QDebug debug, const Feed &feed);
QDebug operator<<(QDebug debug, const Item &item);
|