Skip to content

Commit

Permalink
Implement board persistence for example application "faketrello", per b…
Browse files Browse the repository at this point in the history
  • Loading branch information
NielsMayer committed Aug 2, 2017
1 parent 9407857 commit 06840d5
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 8 deletions.
72 changes: 70 additions & 2 deletions examples/faketrello/appdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,31 @@ AppDelegate::AppDelegate(QObject *parent) : QObject(parent)

int AppDelegate::run()
{
// Pretend to load from file, but now it just load dummy daa.
m_board.load();
// NPM: Find filesystem location for persistent storage
QString persistDir =
#ifdef Q_OS_ANDROID /* for android, store data in a location that doesn't get erased each app update */
QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/" //"/storage/emulated/0/"
+ "faketrello.example.qtproject.org";
#else
QStandardPaths::writableLocation(QStandardPaths::DataLocation);
#endif /* Q_OS_ANDROID */

// NPM: Create application-specific directory for persistent storage if it doesn't exist
QDir path;
path.setPath(persistDir);
if (!path.exists()) {
path.mkpath(persistDir);
}

QCoreApplication* app = QCoreApplication::instance();

// NPM: load board layout from persistence file,
// or load dummy data if no persistence file, or parse error.
QString persistFilePath = persistDir + "/persist.json";
m_board.load(persistFilePath);

m_engine.rootContext()->setContextProperty("PersistFilePath", persistFilePath);

m_engine.rootContext()->setContextProperty("App", this);

m_engine.rootContext()->setContextProperty("CardListStore", cardListStore);
Expand Down Expand Up @@ -71,3 +91,51 @@ void AppDelegate::sync()
runner.patch(cardListStore, patches);
}

// NPM -- see https://github.com/benlau/qsyncable/issues/2
QByteArray AppDelegate::stringifyModel()
{
QVariantList lists;
int count = cardListStore->storage().size();
for (int i = 0 ; i < count ; i++) {
lists << cardListStore->get(i);
}

QVariantMap map;
map["lists"] = lists;
QJsonObject object = QJsonObject::fromVariantMap(map);

QJsonDocument doc;
doc.setObject(object);
QByteArray bytes = doc.toJson(QJsonDocument::Indented);

return bytes;
}

// NPM -- see https://github.com/benlau/qsyncable/issues/2
void AppDelegate::persistModel(const QString& filePath) {
QFile file(filePath);
file.open(QIODevice::WriteOnly);
file.write(AppDelegate::stringifyModel());
file.close();
}

// NPM -- see https://github.com/benlau/qsyncable/issues/2
QByteArray AppDelegate::stringifyBoard()
{
QVariantMap map = m_board.toMap();
QJsonObject object = QJsonObject::fromVariantMap(map);

QJsonDocument doc;
doc.setObject(object);
QByteArray bytes = doc.toJson(QJsonDocument::Indented);

return bytes;
}

// NPM -- see https://github.com/benlau/qsyncable/issues/2
void AppDelegate::persistBoard(const QString& filePath) {
QFile file(filePath);
file.open(QIODevice::WriteOnly);
file.write(AppDelegate::stringifyBoard());
file.close();
}
5 changes: 5 additions & 0 deletions examples/faketrello/appdelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ class AppDelegate : public QObject

public slots:

QByteArray stringifyModel(); //NPM
void persistModel(const QString& filePath); //NPM
QByteArray stringifyBoard(); //NPM
void persistBoard(const QString& filePath); //NPM

void addList();

void addCard(const QString& listUuid);
Expand Down
54 changes: 52 additions & 2 deletions examples/faketrello/board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ void Board::removeCard(const QString& listUuid, const QString& cardUuid)
}
}

void Board::load()
{
// NPM: original version of load(), now invoked from load() if persistence file missing or malformed
void Board::failsafeLoad() {
List list1 = list();

list1 << card()
<< card()
<< card();
Expand All @@ -122,6 +123,55 @@ void Board::load()
d->lists << list1 << list2;
}

// NPM: load persisted JSON board, or call failsafeload() above if first run or malformed persistence file.
// see https://github.com/benlau/qsyncable/issues/2
void Board::load(const QString& persistFilePath)
{
QFile file(persistFilePath);
if(!file.open(QIODevice::ReadOnly)) {
qWarning() << "NOTE: using default board because no persistence file found: "<< persistFilePath;
Board::failsafeLoad();
}
else {
QTextStream stream (&file);
QString text = stream.readAll();
file.close();

QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(text.toUtf8(), &error);

if (error.error != QJsonParseError::NoError) {
qWarning() << "JSON::parse() error: "<< error.errorString();
qWarning() << "NOTE: using default board because persistence file is malformed: "<< persistFilePath;
Board::failsafeLoad();
}
else {
QVariantMap map = doc.object().toVariantMap();
QList<QVariant> lists = map.value("lists").toList();
int listsCount = lists.size();
for (int listsIdx = 0 ; listsIdx < listsCount ; listsIdx++) {
QMap<QString,QVariant> listsMap = lists[listsIdx].toMap();
QString listName = listsMap.value("title").toString();
QList<QVariant> listCards = listsMap.value("cards").toList();
int listNum = listName.mid(5).toInt(); //"List %1" --> "%1"
if (d->nextListId <= listNum)
d->nextListId = listNum + 1;
List cardsList(listName);
int cardsCount = listCards.size();
for (int cardIdx = 0 ; cardIdx < cardsCount ; cardIdx++) {
QString cardName = listCards[cardIdx].toMap().value("text").toString();
int cardNum = cardName.mid(5).toInt(); //"Card %1" --> "%1"
if (d->nextCardId <= cardNum)
d->nextCardId = cardNum + 1;
Card card(cardName);
cardsList << card;
}
d->lists << cardsList;
}
}
}
}

QVariantMap Board::toMap() const
{
QVariantMap res;
Expand Down
7 changes: 5 additions & 2 deletions examples/faketrello/board.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ class Board

int indexOfList(const QString& listUuid);

// Load from file, but now it just load dummy data for demo
void load();
// NPM: Load persisted board from file.
void load(const QString &persistFilePath);

// NPM: Load initial board if persistence file missing or malformed.
void failsafeLoad();

QVariantMap toMap() const;

Expand Down
16 changes: 14 additions & 2 deletions examples/faketrello/main.qml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.1
import "./views"

Window {
ApplicationWindow {
id: window
width: 640
height: 480
Expand All @@ -23,5 +24,16 @@ Window {

}

// NPM: Persist the model to file on application close.
// see https://github.com/benlau/qsyncable/issues/2
// BUG: when running on Android, putting application in background and closing
// (via swipe or close window button) doesn't call this -- you just get
// '"org.qtproject.example.faketrello" died.' message before anything can be done.
// However, closing android app with back button works and persists the board.
onClosing: {
//console.log("Model=" + App.stringifyModel());
console.log("Exiting application... Persisting model to file " + PersistFilePath);
App.persistModel(PersistFilePath);
close.accepted = true;
}
}

0 comments on commit 06840d5

Please sign in to comment.