~aleteoryx/muditaos

ref: sign_test muditaos/module-db/Interface/ContactRecord.hpp -rw-r--r-- 13.2 KiB
a217eeb3 — Dawid Wojtas [BH-2024] Fix lack of alarm directory after updating software 1 year, 5 months ago
                                                                                
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Common/Query.hpp>
#include <Common/Logging.hpp>
#include "module-db/databases/ContactsDB.hpp"
#include <Tables/ContactsGroups.hpp>

#include <i18n/i18n.hpp>
#include "Record.hpp"
#include "utf8/UTF8.hpp"

#include <PhoneNumber.hpp>
#include <NumberHolderMatcher.hpp>
#include "module-gui/gui/widgets/text/TextConstants.hpp"
#include <module-apps/application-phonebook/data/ContactsMap.hpp>

#include <memory>
#include <optional>
#include <set>
#include <sstream>

struct ContactRecord : public Record
{
    UTF8 primaryName          = "";
    UTF8 alternativeName      = "";
    uint32_t contactPosOnList = 0;

    struct Number
    {
        utils::PhoneNumber::View number;
        ContactNumberType numberType = ContactNumberType::OTHER;
        std::uint64_t numberId       = 0;
        Number();
        explicit Number(const utils::PhoneNumber::View &number,
                        ContactNumberType      = ContactNumberType::CELL,
                        const std::uint64_t id = 0);
        explicit Number(const std::string &entered,
                        const std::string &e164,
                        ContactNumberType n_type = ContactNumberType::CELL,
                        const std::uint64_t id   = 0);
    };
    std::vector<Number> numbers = {};

    UTF8 address                   = "";
    UTF8 note                      = "";
    UTF8 mail                      = "";
    ContactAddressType addressType = ContactAddressType::OTHER;

    UTF8 assetPath = "";

    UTF8 speeddial                          = "";
    std::set<ContactsGroupsTableRow> groups = {};

    enum class NameFormatType
    {
        Default,
        List,
        NotUseNumber,
        Title,
    };

    inline auto getNumberAsName() const -> UTF8
    {
        if (!numbers.empty() && !numbers[0].number.getEntered().empty()) {
            return numbers[0].number.getFormatted();
        }
        if (numbers.size() > 1 && !numbers[1].number.getEntered().empty()) {
            return numbers[1].number.getFormatted();
        }
        return "";
    }

    inline auto getFormattedName(const NameFormatType type = NameFormatType::Default) const -> UTF8
    {
        if (isTemporary()) {
            debug_db_data("temporary contact, number as name: '%s'", getNumberAsName().c_str());
            return getNumberAsName();
        }
        if (primaryName.length() > 0) {
            return alternativeName.length() > 0 ? primaryName + " " + alternativeName : primaryName;
        }
        if (alternativeName.length() > 0) {
            return alternativeName;
        }
        if (type == NameFormatType::NotUseNumber) {
            return "";
        }
        if ((type == NameFormatType::Default || type == NameFormatType::List) && getNumberAsName().length() > 0) {
            return getNumberAsName();
        }
        if (type == NameFormatType::List || type == NameFormatType::Title) {
            return utils::translate("app_phonebook_contact_no_name");
        }
        return "";
    }

    auto getAsString() const -> std::string
    {
        std::stringstream contactData;

        if (getFormattedName(NameFormatType::NotUseNumber).length() > 0) {
            contactData << getFormattedName(NameFormatType::NotUseNumber) << gui::text::newline;
        }
        if (!numbers.empty() && !numbers[0].number.getEntered().empty()) {
            contactData << numbers[0].number.getFormatted() << gui::text::newline;
        }
        if (numbers.size() > 1 && !numbers[1].number.getEntered().empty()) {
            contactData << numbers[1].number.getFormatted() << gui::text::newline;
        }
        if (mail.length() > 0) {
            contactData << mail << gui::text::newline;
        }
        if (address.length() > 0) {
            contactData << address << gui::text::newline;
        }
        if (note.length() > 0) {
            contactData << note.c_str();
        }

        return contactData.str();
    }

    void addToFavourites(bool add);
    void addToIce(bool add);
    void addToBlocked(bool add);
    void addToGroup(uint32_t groupId);
    void removeFromGroup(uint32_t groupId);
    [[nodiscard]] auto isOnFavourites() const -> bool;
    [[nodiscard]] auto isOnIce() const -> bool;
    [[nodiscard]] auto isOnBlocked() const -> bool;
    [[nodiscard]] auto isOnGroup(uint32_t groupId) const -> bool;
    [[nodiscard]] auto isTemporary() const -> bool;
};

enum class ContactRecordField
{
    PrimaryName,
    NumberE164,
    NumberUser,
    SpeedDial,
    Groups,
};

class ContactNumberHolder
{
  private:
    ContactsNumberTableRow numberRow;

  public:
    ContactNumberHolder(ContactsNumberTableRow &&numberRow);

    auto getNumber() const noexcept -> utils::PhoneNumber;
    auto getContactID() const noexcept -> std::uint32_t;
    auto getNumberID() const noexcept -> std::uint32_t;
};

class ContactRecordInterface : public RecordInterface<ContactRecord, ContactRecordField>
{
  public:
    struct ContactNumberMatch
    {
        ContactRecord contact;
        std::uint32_t contactId = DB_ID_NONE;
        std::uint32_t numberId  = DB_ID_NONE;

        ContactNumberMatch(ContactRecord rec, std::uint32_t contactId, std::uint32_t numberId);
    };

    explicit ContactRecordInterface(ContactsDB *db);
    ~ContactRecordInterface() override = default;

    auto Add(ContactRecord &rec) -> bool final;

    auto RemoveByID(uint32_t id) -> bool final;

    auto Update(const ContactRecord &rec) -> bool final;

    auto BlockByID(uint32_t id, bool shouldBeBlocked = true) -> bool;

    auto GetByID(uint32_t id) -> ContactRecord final;
    auto GetByIdWithTemporary(uint32_t id) -> ContactRecord;

    auto GetCount() -> uint32_t final;

    auto GetCountFavourites() -> uint32_t;

    auto GetLimitOffset(uint32_t offset, uint32_t limit) -> std::unique_ptr<std::vector<ContactRecord>> final;

    auto GetLimitOffsetByField(uint32_t offset, uint32_t limit, ContactRecordField field, const char *str)
        -> std::unique_ptr<std::vector<ContactRecord>> final;

    auto GetByName(const UTF8 &primaryName, const UTF8 &alternativeName) -> std::unique_ptr<std::vector<ContactRecord>>;

    enum class CreateTempContact : bool
    {
        False,
        True
    };

    auto GetByNumber(const UTF8 &number, CreateTempContact createTempContact = CreateTempContact::False)
        -> std::unique_ptr<std::vector<ContactRecord>>;

    auto GetByNumber(const utils::PhoneNumber::View &numberView,
                     CreateTempContact createTempContact = CreateTempContact::False)
        -> std::unique_ptr<std::vector<ContactRecord>>;

    auto GetByNumberID(std::uint32_t numberId) -> std::optional<ContactRecord>;

    auto MatchByNumber(const utils::PhoneNumber::View &numberView,
                       CreateTempContact createTempContact   = CreateTempContact::False,
                       utils::PhoneNumber::Match matchLevel  = utils::PhoneNumber::Match::POSSIBLE,
                       const std::uint32_t contactIDToIgnore = 0u) -> std::optional<ContactNumberMatch>;

    auto GetBySpeedDial(const UTF8 &speedDial) -> std::unique_ptr<std::vector<ContactRecord>>;

    auto Search(const char *primaryName, const char *alternativeName, const char *number)
        -> std::unique_ptr<std::vector<ContactRecord>>;

    auto runQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult> override;

    auto GetNumberById(std::uint32_t numberId) -> utils::PhoneNumber::View;

    auto GetNumbersIdsByContact(std::uint32_t contactId) -> std::vector<std::uint32_t>;

    auto hasContactRecordSameNumbers(const ContactRecord &rec) -> bool;

    /**
     * @brief Merge contacts list with overriding the duplicates in contacts DB
     *
     * @param contacts vector of contacts with single number
     * @return boolean status
     */
    auto MergeContactsList(std::vector<ContactRecord> &contacts) -> std::vector<std::pair<db::Query::Type, uint32_t>>;

    /**
     * @brief Check which contacts in vector are duplicating contacts in DB
     *
     * @param contacts vector of contacts with single number
     * @return first vector of contacts with unique numbers and second with duplicates appearing in contacts DB
     */
    auto CheckContactsListDuplicates(std::vector<ContactRecord> &contacts)
        -> std::pair<std::vector<ContactRecord>, std::vector<ContactRecord>>;

    /**
     * @brief Verify if single contact record can be considered as a duplicate in DB
     *
     * @param record single contact record to be verified
     * @return true if contact can be considered as a duplicate in DB
     */
    auto verifyDuplicate(ContactRecord &record, const std::uint32_t contactIDToIgnore = 0u)
        -> std::vector<utils::PhoneNumber::View>;

    /**
     * @brief Verify if single contact record can be considered as an existing temporary contact in DB
     *
     * @param record single contact record to be verified
     * @return true if contact can be considered as already existing in DB, as a temporary contact
     */
    auto verifyTemporary(ContactRecord &record) -> bool;

  private:
    ContactsDB *contactDB = nullptr;

    /// get multiple numbers by split numbersId
    auto getNumbers(const std::string &numbersId) -> std::vector<ContactRecord::Number>;
    auto getByIdCommon(ContactsTableRow &contact) -> ContactRecord;
    auto getContactByNumber(const UTF8 &number) -> const std::unique_ptr<std::vector<ContactRecord>>;
    auto getAllNumbers() -> const std::vector<ContactsNumberTableRow>;
    auto buildNumberMatcher(unsigned int maxPageSize = std::numeric_limits<unsigned int>::max())
        -> utils::NumberHolderMatcher<std::vector, ContactNumberHolder>;
    auto splitNumberIDs(const std::string &numberIDs) -> const std::vector<std::uint32_t>;
    auto joinNumberIDs(const std::vector<std::uint32_t> &numberIDs) -> std::string;
    auto unbindNumber(std::uint32_t contactId, std::uint32_t numberId) -> bool;
    void unbindSpeedDialKeyFromOtherContacts(const UTF8 &key);

    const std::uint32_t favouritesGroupId;
    auto getQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto getQueryRecords(const std::shared_ptr<db::Query> &query) -> std::vector<ContactRecord>;
    auto getQueryWithTotalCount(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto getForListQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto getLetterMapQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;

    auto getByIDQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto getByNumberIDQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto getContactsSize(const std::shared_ptr<db::Query> &query) -> std::size_t;
    auto getSizeQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto addQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto updateQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto removeQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto numberGetByIdQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto mergeContactsListQuery(const std::shared_ptr<db::Query> &query) -> const std::unique_ptr<db::QueryResult>;
    auto checkContactsListDuplicatesQuery(const std::shared_ptr<db::Query> &query)
        -> const std::unique_ptr<db::QueryResult>;

    auto addTemporaryContactForNumber(const ContactRecord::Number &number) -> std::optional<ContactRecord>;
    auto getNumbersIDs(std::uint32_t contactID,
                       const ContactRecord &contact,
                       utils::PhoneNumber::Match matchLevel = utils::PhoneNumber::Match::POSSIBLE)
        -> std::vector<std::uint32_t>;
    auto addNumbers(std::uint32_t contactID, const std::vector<ContactRecord::Number> &numbers)
        -> std::optional<std::string>;
    auto addOrUpdateName(std::uint32_t contactID, std::uint32_t nameID, const ContactRecord &contact)
        -> std::optional<std::uint32_t>;
    auto addOrUpdateAddress(std::uint32_t contactID, std::uint32_t addressID, const ContactRecord &contact)
        -> std::optional<std::uint32_t>;
    auto addOrUpdateRingtone(std::uint32_t contactID, std::uint32_t ringtoneID, const ContactRecord &contact)
        -> std::optional<std::uint32_t>;

    auto matchedNumberRefersToTemporary(const ContactNumberHolder &matchedNumber) -> bool;

    /**
     * @brief Changing number table record in place if new number is same as old number but with/without country
     * code
     *
     * @param oldNumberIDs vector of old number IDs in contact_number table which can be changed in place
     *                     only if in  newNumbers  is same number but with/without country code
     * @param newNumbers  vector of numbers to which we want to change the old ones
     * @return true if operation have no error
     */
    auto changeNumberRecordInPlaceIfCountryCodeIsOnlyDifferent(const std::vector<std::uint32_t> &oldNumberIDs,
                                                               std::vector<ContactRecord::Number> &newNumbers) -> bool;
};