mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-03 11:28:48 -05:00
Fixed no response on Center side when editing a contact to have the same number as another contact in phonebook. Added a duplicate number to the response body.
326 lines
13 KiB
C++
326 lines
13 KiB
C++
// 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;
|
|
};
|