Skip to content
Snippets Groups Projects
Commit dd1cdd30 authored by Stepan Salenikovich's avatar Stepan Salenikovich Committed by Edric Milaret
Browse files

Load saved TextRecordings from .json files.

The main goal of this patch is to be able to recover ContactMethods
and conversations which have not been added to the users's contacts
and/or which have no call history. This happens in the case that the
users only sends chat messages to a ContactMethod.

Note that there is a possibility for more than one file to be
associated with the same CM, in the case the sha1 of that CM has
changed (eg: a contact UID has been associated with the CM at some
point). In this case, the most recent recording is used. However,
this is a problem which already exists and is unrelated to this patch.

Tuleap: #270
Change-Id: I5c67be83028af3fc3e1950909574ec947c55c50c
parent 8b68c493
Branches
No related tags found
No related merge requests found
...@@ -171,9 +171,57 @@ bool LocalTextRecordingCollection::isEnabled() const ...@@ -171,9 +171,57 @@ bool LocalTextRecordingCollection::isEnabled() const
bool LocalTextRecordingCollection::load() bool LocalTextRecordingCollection::load()
{ {
//This collection is special as it use the history collection // load all text recordings so we can recover CMs that are not in the call history
//as its source, there is no loading QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/text/");
return true; if (dir.exists()) {
// get .json files, sorted by time, latest first
QStringList filters;
filters << "*.json";
auto list = dir.entryInfoList(filters, QDir::Files | QDir::NoSymLinks | QDir::Readable, QDir::Time);
for (int i = 0; i < list.size(); ++i) {
QFileInfo fileInfo = list.at(i);
QString content;
QFile file(fileInfo.absoluteFilePath());
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
content = QString::fromUtf8(file.readAll());
} else {
qWarning() << "Could not open text recording json file";
}
if (!content.isEmpty()) {
QJsonParseError err;
QJsonDocument loadDoc = QJsonDocument::fromJson(content.toUtf8(), &err);
if (err.error == QJsonParseError::ParseError::NoError) {
Media::TextRecording* r = Media::TextRecording::fromJson({loadDoc.object()});
editor<Media::Recording>()->addExisting(r);
r->setCollection(this);
// get CMs from recording
for (ContactMethod *cm : r->peers()) {
// since we load the recordings in order from newest to oldest, if there is
// more than one found associated with a CM, we take the newest one
if (!cm->d_ptr->m_pTextRecording) {
cm->d_ptr->setTextRecording(r);
} else {
qWarning() << "CM already has text recording" << cm;
}
}
} else {
qWarning() << "Error Decoding Text Message History Json" << err.errorString();
}
} else {
qWarning() << "Text recording file is empty";
}
}
}
// always return true, even if noting was loaded, since the collection can still be used to
// save files
return true;
} }
bool LocalTextRecordingCollection::reload() bool LocalTextRecordingCollection::reload()
......
...@@ -208,6 +208,19 @@ void Media::TextRecording::setAllRead() ...@@ -208,6 +208,19 @@ void Media::TextRecording::setAllRead()
} }
} }
QVector<ContactMethod*> Media::TextRecording::peers() const
{
QVector<ContactMethod*> cms;
for (const Serializable::Peers* peers : d_ptr->m_lAssociatedPeers) {
for (const Serializable::Peer* peer : peers->peers) {
cms << peer->m_pContactMethod;
}
}
return cms;
}
/** /**
* I (Emmanuel Lepage) is in the process of writing a better one for this that * I (Emmanuel Lepage) is in the process of writing a better one for this that
* can be upstreamed into Qt (there is interest in merging a generic QVariant * can be upstreamed into Qt (there is interest in merging a generic QVariant
...@@ -304,40 +317,61 @@ QHash<QByteArray,QByteArray> Media::TextRecordingPrivate::toJsons() const ...@@ -304,40 +317,61 @@ QHash<QByteArray,QByteArray> Media::TextRecordingPrivate::toJsons() const
Media::TextRecording* Media::TextRecording::fromJson(const QList<QJsonObject>& items, const ContactMethod* cm) Media::TextRecording* Media::TextRecording::fromJson(const QList<QJsonObject>& items, const ContactMethod* cm)
{ {
TextRecording* t = new TextRecording(); TextRecording* t = new TextRecording();
//Load the history data //Load the history data
for (const QJsonObject& obj : items) { for (const QJsonObject& obj : items) {
Serializable::Peers* p = SerializableEntityManager::fromJson(obj,cm); Serializable::Peers* p = SerializableEntityManager::fromJson(obj,cm);
t->d_ptr->m_lAssociatedPeers << p; t->d_ptr->m_lAssociatedPeers << p;
} }
//Create the model //Create the model
t->instantMessagingModel(); t->instantMessagingModel();
//Reconstruct the conversation //Reconstruct the conversation
//TODO do it right, right now it flatten the graph //TODO do it right, right now it flatten the graph
for (const Serializable::Peers* p : t->d_ptr->m_lAssociatedPeers) { for (const Serializable::Peers* p : t->d_ptr->m_lAssociatedPeers) {
for (const Serializable::Group* g : p->groups) { // TODO: for now assume the convo is with only 1 CM at a time
for (Serializable::Message* m : g->messages) { auto peerCM = p->peers.at(0)->m_pContactMethod;
::TextMessageNode* n = new ::TextMessageNode();
n->m_pMessage = m ; // get the latest timestamp to set last used
if (!n->m_pMessage->contactMethod) { time_t lastUsed = 0;
n->m_pMessage->contactMethod = const_cast<ContactMethod*>(cm); //TODO remove in 2016 for (const Serializable::Group* g : p->groups) {
n->m_pMessage->authorSha1 = cm->sha1(); for (Serializable::Message* m : g->messages) {
::TextMessageNode* n = new ::TextMessageNode();
if (p->peers.isEmpty()) n->m_pMessage = m ;
addPeer(const_cast<Serializable::Peers*>(p), cm); if (!n->m_pMessage->contactMethod) {
if (cm) {
n->m_pMessage->contactMethod = const_cast<ContactMethod*>(cm); //TODO remove in 2016
n->m_pMessage->authorSha1 = cm->sha1();
if (p->peers.isEmpty())
addPeer(const_cast<Serializable::Peers*>(p), cm);
} else {
if (p->m_hSha1.contains(n->m_pMessage->authorSha1)) {
n->m_pMessage->contactMethod = p->m_hSha1[n->m_pMessage->authorSha1];
} else {
// message was outgoing and author sha1 was set to that of the sending account
n->m_pMessage->contactMethod = peerCM;
n->m_pMessage->authorSha1 = peerCM->sha1();
}
}
}
n->m_pContactMethod = m->contactMethod;
t->d_ptr->m_pImModel->addRowBegin();
t->d_ptr->m_lNodes << n;
t->d_ptr->m_pImModel->addRowEnd();
if (lastUsed < n->m_pMessage->timestamp)
lastUsed = n->m_pMessage->timestamp;
} }
n->m_pContactMethod = m->contactMethod; }
t->d_ptr->m_pImModel->addRowBegin();
t->d_ptr->m_lNodes << n; // update the timestamp of the CM
t->d_ptr->m_pImModel->addRowEnd(); peerCM->setLastUsed(lastUsed);
} }
}
}
return t; return t;
} }
void Media::TextRecordingPrivate::insertNewMessage(const QMap<QString,QString>& message, ContactMethod* cm, Media::Media::Direction direction) void Media::TextRecordingPrivate::insertNewMessage(const QMap<QString,QString>& message, ContactMethod* cm, Media::Media::Direction direction)
......
...@@ -75,6 +75,7 @@ public: ...@@ -75,6 +75,7 @@ public:
bool isEmpty ( ) const; bool isEmpty ( ) const;
bool hasMimeType ( const QString& mimeType ) const; bool hasMimeType ( const QString& mimeType ) const;
QStringList mimeTypes ( ) const; QStringList mimeTypes ( ) const;
QVector<ContactMethod*> peers ( ) const;
//Helper //Helper
void setAllRead(); void setAllRead();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment