void EnginioBaseModelPrivate::fullQueryReset(const QJsonArray &data) { delete _replyConnectionConntext; _replyConnectionConntext = new QObject(); q->beginResetModel(); _data = data; _attachedData.initFromArray(_data); syncRoles(); _canFetchMore = _canFetchMore && _data.count() && (queryData(QtCloudServicesConstants::limit).toDouble() <= _data.count()); q->endResetModel(); }
void EnginioBaseModelPrivate::receivedUpdateNotification(const QJsonObject &object, const QString &idHint, int row) { // update an existing object if (row == NoHintRow) { QString id = idHint.isEmpty() ? object[QtCloudServicesConstants::id].toString() : idHint; Q_ASSERT(_attachedData.contains(id)); row = _attachedData.rowFromObjectId(id); } if (Q_UNLIKELY(row == DeletedRow)) { return; } // FIXME Sometimes it may happen that we get an update about an object that was created just after // the full query and before notifications are setup. For now we inore such situation in future // we should create a createNotification. if (Q_UNLIKELY(row < 0)) { return; } QJsonObject current = _data[row].toObject(); QDateTime currentUpdateAt = QDateTime::fromString(current[QtCloudServicesConstants::updatedAt].toString(), Qt::ISODate); QDateTime newUpdateAt = QDateTime::fromString(object[QtCloudServicesConstants::updatedAt].toString(), Qt::ISODate); if (newUpdateAt < currentUpdateAt) { // we already have a newer version return; } if (_data[row].toObject()[QtCloudServicesConstants::id].toString().isEmpty()) { // Create and update may go through the same code path because // the model already have a dummy item. No id means that it // is a dummy item. const QString newId = object[QtCloudServicesConstants::id].toString(); AttachedData newData(row, newId); _attachedData.insert(newData); } if (_data.count() == 1) { q->beginResetModel(); _data.replace(row, object); syncRoles(); q->endResetModel(); } else { _data.replace(row, object); emit q->dataChanged(q->index(row), q->index(row)); } }
EnginioReply *append(const QJsonObject &value) { QJsonObject object(value); object[EnginioString::objectType] = _query[EnginioString::objectType]; // TODO think about it, it means that not all queries are valid EnginioReply* id = _enginio->create(object, _operation); const int row = _data.count(); if (!row) { // the first item need to update roles q->beginResetModel(); _rowsToSync.insert(row); _data.append(value); syncRoles(); _dataChanged.insert(id, qMakePair(row, object)); q->endResetModel(); } else { q->beginInsertRows(QModelIndex(), _data.count(), _data.count()); _rowsToSync.insert(row); _data.append(value); _dataChanged.insert(id, qMakePair(row, object)); q->endInsertRows(); } return id; }
void finishedRequest(const EnginioReply *response) { // We get all finished requests, check if we started this one if (!_dataChanged.contains(response)) return; // ### TODO proper error handling // this kind of response happens when the backend id/secret is missing if (!response->data()[EnginioString::message].isNull()) qWarning() << "Enginio: " << response->data()[EnginioString::message].toString(); QPair<int, QJsonObject> requestInfo = _dataChanged.take(response); int row = requestInfo.first; if (row == FullModelReset) { q->beginResetModel(); _rowsToSync.clear(); _data = response->data()[EnginioString::results].toArray(); syncRoles(); _canFetchMore = _canFetchMore && _data.count() && (_query[EnginioString::limit].toDouble() <= _data.count()); q->endResetModel(); } else if (row == IncrementalModelUpdate) { Q_ASSERT(_canFetchMore); QJsonArray data(response->data()[EnginioString::results].toArray()); QJsonObject query(requestInfo.second); int offset = query[EnginioString::offset].toDouble(); int limit = query[EnginioString::limit].toDouble(); int dataCount = data.count(); int startingOffset = qMax(offset, _data.count()); q->beginInsertRows(QModelIndex(), startingOffset, startingOffset + dataCount -1); for (int i = 0; i < dataCount; ++i) { _data.append(data[i]); } _canFetchMore = limit <= dataCount; q->endInsertRows(); } else { _rowsToSync.remove(row); // FIXME the row is may not be right QJsonObject newValue(response->data()); QJsonObject oldValue = requestInfo.second; const bool removeOperation = newValue.isEmpty(); // update the row number row = findId(oldValue[EnginioString::id].toString(), row); if (row == -1) { // The object is not in the cache, which means that it was deleted already if (removeOperation) qWarning() << "The same row was removed twice, removed object was:" << QJsonDocument(oldValue).toJson(); else qWarning() << "Trying to update a removed object:\nOldValue: " << QJsonDocument(oldValue).toJson() << "\nNewValue: " << QJsonDocument(newValue).toJson(); return; // nothing to do } Q_ASSERT(row >= 0 && row < _data.count()); if (response->networkError() != QNetworkReply::NoError) { _data.replace(row, oldValue); // FIXME do we have to do more here? return; } if (removeOperation) { q->beginRemoveRows(QModelIndex(), row, row); _data.removeAt(row); q->endRemoveRows(); } else { QJsonObject current = _data[row].toObject(); QDateTime currentUpdateAt = QDateTime::fromString(current[EnginioString::updatedAt].toString(), Qt::ISODate); QDateTime newUpdateAt = QDateTime::fromString(newValue[EnginioString::updatedAt].toString(), Qt::ISODate); if (newUpdateAt < currentUpdateAt) { // we already have a newer version return; } if (_data.count() == 1) { q->beginResetModel(); _data.replace(row, newValue); syncRoles(); q->endResetModel(); } else { _data.replace(row, newValue); emit q->dataChanged(q->index(row), q->index(row)); } } } }