diff options
author | Caroline Chao <caroline.chao@digia.com> | 2014-09-17 00:52:43 +0200 |
---|---|---|
committer | Caroline Chao <caroline.chao@digia.com> | 2014-09-17 14:38:10 +0200 |
commit | 85be990470e5fcd6baa973d83cf40f8ae8999814 (patch) | |
tree | deddf8660455add2d16d643d9de06b09c5ad15ae | |
parent | ccff7bb3878d24cccee00df04824cc0da4bccce4 (diff) |
Refactor application to enable proper authentication
Use a single EnginioClient for all the application.
Remove rssFeed not needed anymore.
Change-Id: I0f5760abee0098aa0c99e4a65ccee716dca7a3fb
Reviewed-by: Niels Weber <niels.weber@digia.com>
-rw-r--r-- | qml/components/ConferenceHeader.qml | 18 | ||||
-rw-r--r-- | qml/components/DaySwitcher.qml | 2 | ||||
-rw-r--r-- | qml/components/DayTracksModel.qml | 13 | ||||
-rw-r--r-- | qml/components/HomeScreen.qml | 20 | ||||
-rw-r--r-- | qml/components/ModelsSingleton.qml | 162 | ||||
-rw-r--r-- | qml/components/Track.qml | 8 | ||||
-rw-r--r-- | qml/components/TrackHeader.qml | 8 | ||||
-rw-r--r-- | qml/components/TweetModel.qml | 2 | ||||
-rw-r--r-- | qml/main.qml | 10 | ||||
-rw-r--r-- | src/applicationclient.cpp | 174 | ||||
-rw-r--r-- | src/applicationclient.h | 105 | ||||
-rw-r--r-- | src/fileio.cpp | 10 | ||||
-rw-r--r-- | src/fileio.h | 1 | ||||
-rw-r--r-- | src/main.cpp | 6 | ||||
-rw-r--r-- | src/model.cpp | 45 | ||||
-rw-r--r-- | src/model.h | 16 | ||||
-rw-r--r-- | talk-schedule.pro | 6 |
17 files changed, 409 insertions, 197 deletions
diff --git a/qml/components/ConferenceHeader.qml b/qml/components/ConferenceHeader.qml index 0a454a3..914ee04 100644 --- a/qml/components/ConferenceHeader.qml +++ b/qml/components/ConferenceHeader.qml @@ -107,23 +107,17 @@ Item { id: locationMenu Instantiator { id: menuConferenceRepeater - model: ModelsSingleton.conferencesModel.rowCount() + model: applicationClient.conferencesModel.rowCount() MenuItem { - text: ModelsSingleton.conferencesModel.data(index, "title") - onTriggered: { - ModelsSingleton.conferenceId = ModelsSingleton.conferencesModel.data(index, "id") - ModelsSingleton.conferenceLocation = ModelsSingleton.conferencesModel.data(index, "location") - ModelsSingleton.conferenceTitle = ModelsSingleton.conferencesModel.data(index, "title") - ModelsSingleton.conferenceTwitterTag = ModelsSingleton.conferencesModel.data(index, "TwitterTag") - ModelsSingleton.rssFeed = ModelsSingleton.conferencesModel.data(index, "rssFeed") - } + text: applicationClient.conferencesModel.data(index, "title") + onTriggered: applicationClient.setCurrentConferenceIndex(index) } onObjectAdded: locationMenu.insertItem(locationMenu.items.length, object) } Connections { - target: ModelsSingleton.conferencesModel - onDataReady: menuConferenceRepeater.model = ModelsSingleton.conferencesModel.rowCount() + target: applicationClient.conferencesModel + onDataReady: menuConferenceRepeater.model = applicationClient.conferencesModel.rowCount() } } @@ -149,7 +143,7 @@ Item { anchors.left: locationImage.right anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: Theme.margins.ten - text: ModelsSingleton.conferenceLocation + text: applicationClient.currentConferenceDetails.location font.pointSize: Theme.fonts.ten_pt font.capitalization: Font.AllUppercase font.weight: Font.DemiBold diff --git a/qml/components/DaySwitcher.qml b/qml/components/DaySwitcher.qml index 84ce6e7..2c485a0 100644 --- a/qml/components/DaySwitcher.qml +++ b/qml/components/DaySwitcher.qml @@ -61,7 +61,7 @@ Rectangle { Item { Layout.fillWidth: true; width:Theme.margins.five; Layout.preferredHeight: 1 } Label { id: locationLabel - text: ModelsSingleton.conferenceLocation + text: applicationClient.currentConferenceDetails.location verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: Theme.fonts.seven_pt diff --git a/qml/components/DayTracksModel.qml b/qml/components/DayTracksModel.qml index 8b095ef..df28c55 100644 --- a/qml/components/DayTracksModel.qml +++ b/qml/components/DayTracksModel.qml @@ -53,16 +53,21 @@ QtObject { property bool isEmpty: false property var modelTracks: SortFilterModel { + property bool ready + + function init() + { + isEmpty = modelTracks.rowCount() === 0 + ready = Qt.binding(function() {return modelTracks.rowCount() > 0}) + } + sortRole: "start" filterRole: "track" filterRegExp: new RegExp(dayId) model: ModelsSingleton.eventModel hide: true - Component.onCompleted: { - isEmpty = modelTracks.rowCount() === 0 - ready = Qt.binding(function() {return modelTracks.rowCount() > 0}) - } + Component.onCompleted: init() onReadyChanged: { // calculate number of rows needed for the current track numberCollidingEvents = 0 diff --git a/qml/components/HomeScreen.qml b/qml/components/HomeScreen.qml index 857b539..42988af 100644 --- a/qml/components/HomeScreen.qml +++ b/qml/components/HomeScreen.qml @@ -117,19 +117,31 @@ Rectangle { SortFilterModel { id: sortModelNextEvents + + function init() + { + if (sortModelNextEvents.rowCount() > 0) + upcomingItem.visibleDate = Qt.formatDate(sortModelNextEvents.get(0, "start"), upcomingItem.formatDate) + } + sortRole: "start" filterRole: "fromNow" maximumCount: 7 model: ModelsSingleton.eventModel - Component.onCompleted: { - if (sortModelNextEvents.rowCount() > 0) - upcomingItem.visibleDate = Qt.formatDate(sortModelNextEvents.get(0, "start"), upcomingItem.formatDate) + Component.onCompleted: init() + } + + Connections { + target: ModelsSingleton.eventModel + onDataReady: { + sortModelNextEvents.model = ModelsSingleton.eventModel + sortModelNextEvents.init() } } Text { id: labelUpcoming - text: Theme.text.upcoming.arg(ModelsSingleton.conferenceTitle).arg(upcomingItem.visibleDate) + text: Theme.text.upcoming.arg(applicationClient.currentConferenceDetails.title).arg(upcomingItem.visibleDate) width: parent.width height: Theme.sizes.homeTitleHeight z: 1 diff --git a/qml/components/ModelsSingleton.qml b/qml/components/ModelsSingleton.qml index cc7e1c6..e5f6e18 100644 --- a/qml/components/ModelsSingleton.qml +++ b/qml/components/ModelsSingleton.qml @@ -46,45 +46,14 @@ import TalkSchedule 1.0 QtObject { id: object property string conferenceId: "" - property string currentUserId - property string conferenceLocation - property string conferenceTitle - property string conferenceTwitterTag - property string rssFeed property var currentConferenceTracks: [] property var currentConferenceEvents: [] property var currentConferenceDays: [] property bool busy: false property string errorMessage property int conferenceIndex: 0 - property var conference - property var client: EnginioClient { - backendId: backId - onError: { - errorMessage = reply.errorString - console.log("Enginio error " + reply.errorCode + ": " + reply.errorString) - } - Component.onCompleted: conferencesModel.query({"objectType": "objects.Conference"}) - } - - signal writeUserIdToFile(string userId) - - property var conferencesModel: Model { - backendId: backId - fileNameTag: "ConferencesObject" - onDataReady: { - if (conferencesModel.rowCount() > 0) { - object.conferenceId = conferencesModel.data(0, "id") - object.conferenceLocation = conferencesModel.data(0, "location") - object.conferenceTitle = conferencesModel.data(0, "title") - object.conferenceTwitterTag = conferencesModel.data(0, "TwitterTag") - object.rssFeed = conferencesModel.data(0, "rssFeed") - } - } - } property var day: Model { - backendId: backId fileNameTag: "DayObject" onDataReady: { currentConferenceDays = [] @@ -95,7 +64,6 @@ QtObject { } property var trackModel: Model { - backendId: backId fileNameTag: "TrackObject" onDataReady: { currentConferenceTracks = [] @@ -107,7 +75,6 @@ QtObject { property var eventModel: Model { id: eventModel - backendId: backId fileNameTag: "EventObject" onDataReady: queryFavorites() function queryFavorites() @@ -121,37 +88,39 @@ QtObject { property var favoriteModel: Model { // do not save favorite - backendId: backId onDataReady: getFavoriteIds() } property var breakModel: Model { fileNameTag: "BreakObject" - backendId: backId } property var timeListModel: Model { // do not save time list - backendId: backId property var tracksTodayModel onTracksTodayModelChanged: { if (!!tracksTodayModel) { var todaysTracks = [] for (var i = 0; i < tracksTodayModel.rowCount(); i++) todaysTracks.push(tracksTodayModel.data(i, "id")) - timeListModel.query({ "objectType": "objects.Event", + var timeQuery = applicationClient.client.query({ "objectType": "objects.Event", "sort" : [{"sortBy": "start", "direction": "asc"}], "query": { "track.id" : { "$in" : todaysTracks } } }) + timeQuery.finished.connect(function() { + timeListModel.onFinished(timeQuery) + }) } } } function queryConferenceEvents() { - eventModel.query({ + var eventQuery = applicationClient.client.query({ "objectType": "objects.Event", "query": { "track.id" : { "$in" : currentConferenceTracks } }, + "sort" : [{"sortBy": "start", "direction": "asc"}], + "limit": 200, "include": { "tracks": { "objectType": "objects.Track", @@ -160,14 +129,20 @@ QtObject { } } }) + eventQuery.finished.connect(function() { + eventModel.onFinished(eventQuery) + }) } function queryConferenceBreaks() { - breakModel.query({ + var breakQuery = applicationClient.client.query({ "objectType": "objects.Break", "query": { "day.id" : { "$in" : currentConferenceDays } }, }) + breakQuery.finished.connect(function() { + breakModel.onFinished(breakQuery) + }) } function getFavoriteIds() @@ -178,9 +153,8 @@ QtObject { function queryUserConferenceFavorites() { - var favQuery = favoriteModel.query({ "objectType": "objects.Favorite", + var favQuery = applicationClient.client.query({ "objectType": "objects.Favorite", "query": { - "user.id": currentUserId, "favoriteEvent.id" : { "$in" : currentConferenceEvents } }, "include": { @@ -198,58 +172,22 @@ QtObject { } } }) - } - - function retrieveUser(userId) - { - currentUserId = userId - if (currentUserId.length === 0) - createUser() - else - getUser() - } - - function createUser() - { - console.log("createUser") - var reply = client.create({"objectType":"objects.User"}) - var userId = 0 - reply.finished.connect(function() { - if (reply.errorType !== EnginioReply.NoError) { - console.log("Failed to create an user:\n" + JSON.stringify(reply.data, undefined, 2) + "\n\n") - } else { - console.log("Account Created.") - userId = reply.data.id - writeUserIdToFile(userId) - currentUserId = userId - } - }) - } - - function getUser() - { - console.log("get user") - var queryUser = client.query({"objectType":"objects.User", "query" : { "id" : currentUserId }}) - queryUser.finished.connect(function() { - if (queryUser.errorType !== EnginioReply.NoError || queryUser.data.results[0] === undefined) { - // User not found. Create new one - createUser() - } + favQuery.finished.connect(function() { + favoriteModel.onFinished(favQuery) }) } function saveFeedback(fbtext, eventId, rating) { console.log("saveFeedback") - var reply = client.create({ + var reply = applicationClient.client.create({ "objectType": "objects.Feedback", "event": { "id": eventId, "objectType": "objects.Event" }, "rating": rating, - "feedbackText": fbtext, - "userId": currentUserId + "feedbackText": fbtext }) reply.finished.connect(function() { if (reply.errorType !== EnginioReply.NoError) { @@ -268,15 +206,13 @@ QtObject { } busy = true console.log("start saving favorite") - var reply = client.create({ + var reply = applicationClient.client.create({ "objectType": "objects.Favorite", "favoriteEvent": { "id": saveEventId, "objectType": "objects.Event" - }, - "user": { - "id": currentUserId, - "objectType": "objects.User"}}) + } + }) reply.finished.connect(function() { if (reply.errorType !== EnginioReply.NoError) @@ -296,15 +232,13 @@ QtObject { } busy = true console.log("start removing favorite. First get the favorite id which should be removed") - var favoriteQuery = client.query({ + var favoriteQuery = applicationClient.client.query({ "objectType": "objects.Favorite", "query":{ "favoriteEvent": { "id": removeEventId, "objectType": "objects.Event"}, - "user": { - "id": currentUserId , - "objectType": "objects.User"}} + } }) favoriteQuery.finished.connect(function() { @@ -314,8 +248,9 @@ QtObject { else { if (favoriteQuery.data.results.length > 0) { // Now do the actual removal - var reply = client.remove({ "objectType": "objects.Favorite", - "id": favoriteQuery.data.results[0].id }) + var reply = applicationClient.client.remove({ "objectType": "objects.Favorite", + "id": favoriteQuery.data.results[0].id + }) reply.finished.connect(function() { if (favoriteQuery.errorType !== EnginioReply.NoError) console.log("Failed to remove an Favorite:\n" + JSON.stringify(reply.data, undefined, 2) + "\n\n") @@ -323,15 +258,15 @@ QtObject { eventModel.removeFavorite(removeEventId) }) } + console.log("favorite remove done") } - console.log("favorite remove done") busy = false }) } onConferenceIdChanged: { if (object.conferenceId === "") - return + return object.day.conferenceId = conferenceId object.trackModel.conferenceId = conferenceId @@ -340,21 +275,28 @@ QtObject { object.breakModel.conferenceId = conferenceId object.timeListModel.conferenceId = conferenceId - day.query({ "objectType": "objects.Day", - "query": { - "conference": { - "id": object.conferenceId, - "objectType": "objects.Conference" - } - } - }) - trackModel.query({"objectType": "objects.Track", - "query": { - "conference": { - "id": object.conferenceId, - "objectType": "objects.Conference" - } - } - }); + var dayQuery = applicationClient.client.query({ "objectType": "objects.Day", + "query": { + "conference": { + "id": object.conferenceId, + "objectType": "objects.Conference" + } + } + }) + dayQuery.finished.connect(function() { + day.onFinished(dayQuery) + }) + + var tracksQuery = applicationClient.client.query({"objectType": "objects.Track", + "query": { + "conference": { + "id": object.conferenceId, + "objectType": "objects.Conference" + } + } + }); + tracksQuery.finished.connect(function() { + trackModel.onFinished(tracksQuery) + }) } } diff --git a/qml/components/Track.qml b/qml/components/Track.qml index fea4035..275f586 100644 --- a/qml/components/Track.qml +++ b/qml/components/Track.qml @@ -58,6 +58,14 @@ Row { onIsReady: repeater1.model = rowsArray.length } + Connections { + target: ModelsSingleton.eventModel + onDataReady: { + dayTracksModel.modelTracks.model = ModelsSingleton.eventModel + dayTracksModel.modelTracks.init() + } + } + Column { id: column width: parent.width diff --git a/qml/components/TrackHeader.qml b/qml/components/TrackHeader.qml index 65b7ec5..1e7fe13 100644 --- a/qml/components/TrackHeader.qml +++ b/qml/components/TrackHeader.qml @@ -68,6 +68,14 @@ ListView { dayId: id } + Connections { + target: ModelsSingleton.eventModel + onDataReady: { + dayTracksModel.modelTracks.model = ModelsSingleton.eventModel + dayTracksModel.modelTracks.init() + } + } + height: !dayTracksModel.isEmpty ? trackHeight * ( dayTracksModel.numberCollidingEvents + 1 ): 0 width: Theme.sizes.trackHeaderWidth visible: !dayTracksModel.isEmpty diff --git a/qml/components/TweetModel.qml b/qml/components/TweetModel.qml index 78720ea..e9aec27 100644 --- a/qml/components/TweetModel.qml +++ b/qml/components/TweetModel.qml @@ -61,7 +61,7 @@ Item { var req = new XMLHttpRequest; req.open("GET", "https://api.twitter.com/1.1/search/tweets.json?" + - "count=10&q=" + encodePhrase(ModelsSingleton.conferenceTwitterTag) + "-filter:retweets"); + "count=10&q=" + encodePhrase(applicationClient.currentConferenceDetails.TwitterTag) + "-filter:retweets"); req.setRequestHeader("Authorization", "Bearer " + bearerToken); req.onreadystatechange = function() { status = req.readyState; diff --git a/qml/main.qml b/qml/main.qml index 6997bf7..b2245e6 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -53,14 +53,10 @@ ApplicationWindow { color: Theme.colors.white - FileIO { - id: userIdFile - Component.onCompleted: ModelsSingleton.retrieveUser(read()) - } - Connections { - target: ModelsSingleton - onWriteUserIdToFile: userIdFile.write(userId) + target: applicationClient + onCurrentConferenceIdChanged: ModelsSingleton.conferenceId = applicationClient.currentConferenceId + onError: ModelsSingleton.errorMessage = errorMessage } ConferenceHeader { diff --git a/src/applicationclient.cpp b/src/applicationclient.cpp new file mode 100644 index 0000000..3a2444d --- /dev/null +++ b/src/applicationclient.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "applicationclient.h" +#include <Enginio/enginioclient.h> +#include <Enginio/enginiomodel.h> +#include <Enginio/enginioreply.h> +#include <Enginio/enginiooauth2authentication.h> +#include <QJsonObject> +#include "fileio.h" +#include "model.h" +#include <QStringList> +#include <QDebug> +#include <QTimer> + +#define QUOTE_(x) #x +#define QUOTE(x) QUOTE_(x) +#define BACKEND_ID QUOTE(TALK_SCHEDULE_BACKEND_ID) + +ApplicationClient::ApplicationClient() +{ + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(authenticate())); + + m_userData = new FileIO(this); + authenticator = new EnginioOAuth2Authentication(this); + m_client = new EnginioClient(this); + const QByteArray backId = QByteArray(BACKEND_ID); + m_client->setBackendId(backId); + + m_conferenceModel = new Model(this); + m_conferenceModel->setFileNameTag("ConferencesObject"); + + connect(m_client, SIGNAL(sessionAuthenticated(EnginioReply*)), this, SLOT(authenticationSuccess(EnginioReply*))); + connect(m_client, SIGNAL(sessionAuthenticationError(EnginioReply*)), this, SLOT(errorClient(EnginioReply*))); + connect(m_client, SIGNAL(error(EnginioReply*)), this, SLOT(errorClient(EnginioReply*))); + getUserCredentials(); + + m_details = new QQmlPropertyMap(this); + m_details->insert(QLatin1String("location"), QVariant("")); + m_details->insert(QLatin1String("title"), QVariant("")); + m_details->insert(QLatin1String("TwitterTag"), QVariant("")); +} + +void ApplicationClient::errorClient(EnginioReply *reply) +{ + qDebug() << "Error" << reply->errorString() << m_client->authenticationState(); + emit error(reply->errorString()); + reply->deleteLater(); +} + +void ApplicationClient::getUserCredentials() +{ + qDebug() << "Get user credentials"; + QString cachedUserData = m_userData->read(); + QStringList splitData = cachedUserData.split(" "); + if (splitData.length() != 2) { + createUser(); + } else { + currentUsername = splitData.at(0); + currentPassword = splitData.at(1); + authenticate(); + } +} + +void ApplicationClient::createUser() +{ + qDebug() << "Create User"; + currentUsername = m_userData->createUUID(); + currentPassword = m_userData->createUUID(); + QJsonObject query; + query["objectType"] = QString::fromUtf8("users"); + query["username"] = currentUsername; + query["password"] = currentPassword; + const EnginioReply *reply = m_client->create(query); + connect(reply, SIGNAL(finished(EnginioReply*)), this, SLOT(userCreationReply(EnginioReply*))); +} + +void ApplicationClient::userCreationReply(EnginioReply *reply) +{ + if (reply->errorType() != Enginio::NoError) { + qDebug() << "Failed to create an user" << reply->errorString(); + emit error(reply->errorString()); + } else { + qDebug() << "User Created"; + m_userData->write(QString("%1 %2").arg(currentUsername).arg(currentPassword)); + authenticate(); + } + reply->deleteLater(); +} + +void ApplicationClient::authenticate() +{ + qDebug() << "Authenticate" << currentUsername; + m_client->setIdentity(0); + authenticator->setUser(currentUsername); + authenticator->setPassword(currentPassword); + m_client->setIdentity(authenticator); +} + +void ApplicationClient::authenticationSuccess(EnginioReply *reply) +{ + qDebug() << "Query the conference"; + int timeout = (reply->data().value("expires_in").toInt() - 20*60)*1000; + timer->setSingleShot(true); + timer->start(timeout); + QJsonObject query; + query["objectType"] = QString::fromUtf8("objects.Conference"); + const EnginioReply *replyConf = m_client->query(query); + connect(replyConf, SIGNAL(finished(EnginioReply*)), this, SLOT(queryConferenceReply(EnginioReply*))); +} + +void ApplicationClient::setCurrentConferenceId(const QString &newConfId) +{ + if (m_currentConferenceId != newConfId) { + m_currentConferenceId = newConfId; + emit currentConferenceIdChanged(); + } +} + +void ApplicationClient::queryConferenceReply(EnginioReply *reply) +{ + m_conferenceModel->onFinished(reply); + emit conferencesModelChanged(); + setCurrentConferenceIndex(0); +} + +void ApplicationClient::setCurrentConferenceIndex(const int index) +{ + if (index > m_conferenceModel->rowCount() - 1) + return; + setCurrentConferenceId(m_conferenceModel->data(index, "id").toString()); + m_details->insert(QLatin1String("location"),m_conferenceModel->data(index, "location")); + m_details->insert(QLatin1String("title"), m_conferenceModel->data(index, "title")); + m_details->insert(QLatin1String("TwitterTag"), m_conferenceModel->data(index, "TwitterTag")); + emit currentConferenceDetailsChanged(); +} diff --git a/src/applicationclient.h b/src/applicationclient.h new file mode 100644 index 0000000..a11bed3 --- /dev/null +++ b/src/applicationclient.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APPLICATIONCLIENT_H +#define APPLICATIONCLIENT_H + +#include <QObject> +#include <QString> +#include <QtQml/QQmlPropertyMap> + +class EnginioClient; +class EnginioModel; +class EnginioOAuth2Authentication; +class EnginioReply; +class FileIO; +class Model; +class QTimer; + +class ApplicationClient: public QObject +{ + Q_OBJECT + Q_PROPERTY(EnginioClient *client READ client) + Q_PROPERTY(QString currentConferenceId READ currentConferenceId WRITE setCurrentConferenceId NOTIFY currentConferenceIdChanged) + Q_PROPERTY(Model *conferencesModel READ conferencesModel NOTIFY conferencesModelChanged()) + Q_PROPERTY(QObject *currentConferenceDetails READ currentConferenceDetails NOTIFY currentConferenceDetailsChanged) +public: + explicit ApplicationClient(); + Model *conferencesModel() const { return m_conferenceModel; } + EnginioClient *client() { return m_client; } + + QString currentConferenceId() const { return m_currentConferenceId; } + void setCurrentConferenceId(const QString &newConfId); + + Q_INVOKABLE void setCurrentConferenceIndex(const int index); + + QQmlPropertyMap *currentConferenceDetails() const { return m_details; } + +protected: + void getUserCredentials(); + void createUser(); + +signals: + void error(QString errorMessage); + void askQueryConferences(); + void currentConferenceIdChanged(); + void currentConferenceDetailsChanged(); + void conferencesModelChanged(); + +public slots: + void authenticationSuccess(EnginioReply *reply); + void errorClient(EnginioReply *reply); + void userCreationReply(EnginioReply *reply); + void queryConferenceReply(EnginioReply *reply); + void authenticate(); + +private: + EnginioClient *m_client; + Model *m_conferenceModel; + FileIO *m_userData; + QString currentUsername; + QString currentPassword; + EnginioOAuth2Authentication *authenticator; + QString m_currentConferenceId; + QQmlPropertyMap *m_details; + QTimer *timer; +}; + +#endif // APPLICATIONCLIENT_H diff --git a/src/fileio.cpp b/src/fileio.cpp index 87fd174..2f18557 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -42,6 +42,7 @@ #include <QTextStream> #include <QDir> #include <QtCore/QStandardPaths> +#include <QUuid> #include <QDebug> FileIO::FileIO(QObject *parent) : @@ -97,3 +98,12 @@ bool FileIO::write(const QString &data) return true; } + +QString FileIO::createUUID() +{ + QString uuid = QUuid::createUuid().toString(); + // Remove curly brackets + uuid.remove(0,1); + uuid.remove(uuid.length() - 1,1); + return uuid; +} diff --git a/src/fileio.h b/src/fileio.h index e05bcb8..1af916b 100644 --- a/src/fileio.h +++ b/src/fileio.h @@ -52,6 +52,7 @@ public: Q_INVOKABLE QString read(); Q_INVOKABLE bool write(const QString &data); + Q_INVOKABLE QString createUUID(); QString source() { return mSource; } diff --git a/src/main.cpp b/src/main.cpp index 8345eda..2f325ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -48,6 +48,7 @@ #include "model.h" #include "sortfiltermodel.h" #include "fileio.h" +#include "applicationclient.h" #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) @@ -78,12 +79,13 @@ int main(int argc, char *argv[]) } else { qWarning("Error: fail to load Open Sans font"); } - QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty("backId", QString(BACKEND_ID)); engine.rootContext()->setContextProperty("consumerKey", QString(CONSUMER_KEY)); engine.rootContext()->setContextProperty("consumerSecret", QString(CONSUMER_SECRET)); + ApplicationClient *client = new ApplicationClient(); + engine.rootContext()->setContextProperty("applicationClient", client); + const char *uri = "TalkSchedule"; // @uri TalkSchedule qmlRegisterSingletonType<Theme>(uri, 1, 0, "Theme", systeminfo_provider); diff --git a/src/model.cpp b/src/model.cpp index 2ff33c9..70637b6 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -40,7 +40,6 @@ #include "model.h" -#include <Enginio/enginioclient.h> #include <Enginio/enginioreply.h> #include <QtCore/QDebug> #include <QtCore/QJsonValue> @@ -54,8 +53,6 @@ Model::Model(QObject *parent) : QAbstractListModel(parent) { - m_client = new EnginioClient(this); - connect(m_client, SIGNAL(finished(EnginioReply*)), this, SLOT(onFinished(EnginioReply*))); } @@ -144,24 +141,12 @@ QHash<int, QByteArray> Model::roleNames() const return m_roleNames; } -QString Model::backendId() const -{ - return m_client ? m_client->backendId() : ""; -} - -void Model::setBackendId(const QString &id) -{ - if (m_client && m_client->backendId() == id.toLatin1()) - return; - - m_client->setBackendId(id.toLatin1()); - Q_EMIT backendIdChanged(); -} - void Model::setConferenceId(const QString &id) { if (id != m_conferenceId) { m_conferenceId = id; + // The file name tag has been declared before + load(); Q_EMIT conferenceIdChanged(); } } @@ -174,30 +159,6 @@ void Model::setFileNameTag(const QString &newTag) } } -void Model::query(const QJSValue &query) -{ - if (!query.hasProperty("objectType")) - return; - - // Load data from file if available - load(); - - // Check for updates - QJsonObject queryObject; - queryObject["objectType"] = query.property("objectType").toString(); - - if (query.hasProperty("query")) - queryObject["query"] = QJsonObject::fromVariantMap(query.property("query").toVariant().toMap()); - - if (query.hasProperty("sort")) - queryObject["sort"] = QJsonObject::fromVariantMap(query.property("sort").toVariant().toMap()); - - if (query.hasProperty("include")) - queryObject["include"] = QJsonObject::fromVariantMap(query.property("include").toVariant().toMap()); - - m_client->query(queryObject); -} - QVariant Model::data(int index, const QString &role) const { return data(this->index(index, 0), m_roleNames.key(role.toLatin1())); @@ -269,7 +230,7 @@ bool Model::parse(const QJsonObject &object) { bool dataHasChanged = true; - dataHasChanged = fileNameTag().isEmpty() || object != currentModelObject[fileNameTag()] ; + dataHasChanged = fileNameTag().isEmpty() || object != currentModelObject[fileNameTag()]; if (dataHasChanged) { beginResetModel(); diff --git a/src/model.h b/src/model.h index 3936456..60b968a 100644 --- a/src/model.h +++ b/src/model.h @@ -45,18 +45,14 @@ #include <QtCore/QMap> #include <QtCore/QList> #include <QtQml/QJSValue> -//#include <QtCore/QJsonDocument> -#include <Enginio/enginioclient.h> -//#include <Enginio/enginioreply.h> +#include <QtCore/QJsonObject> -QT_FORWARD_DECLARE_CLASS(EnginioClient) -QT_FORWARD_DECLARE_CLASS(EnginioReply) +class EnginioReply; class Model : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QString backendId READ backendId WRITE setBackendId NOTIFY backendIdChanged) Q_PROPERTY(QString conferenceId READ conferenceId WRITE setConferenceId NOTIFY conferenceIdChanged) Q_PROPERTY(QString fileNameTag READ fileNameTag WRITE setFileNameTag NOTIFY fileNameTagChanged) @@ -68,15 +64,12 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QHash<int, QByteArray> roleNames() const; - QString backendId() const; - void setBackendId(const QString &id); QString conferenceId() const { return m_conferenceId; } void setConferenceId(const QString &id); QString fileNameTag() const { return m_fileNameTag; } void setFileNameTag(const QString &newTag); Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const; - Q_INVOKABLE void query(const QJSValue &query); Q_INVOKABLE QVariant data(int index, const QString &role) const; Q_INVOKABLE void addFavorite(const QString &data); Q_INVOKABLE void removeFavorite(const QString &data); @@ -85,12 +78,12 @@ public: Q_INVOKABLE QVariant indexOf(const QString &role, QVariant value); Q_SIGNALS: - void backendIdChanged(); + void clientChanged(); void conferenceIdChanged(); void fileNameTagChanged(); void dataReady(); -private Q_SLOTS: +public Q_SLOTS: void onFinished(EnginioReply *reply); private: @@ -100,7 +93,6 @@ private: QHash<int, QByteArray> m_roleNames; QList<QMap<QString, QVariant> > m_data; - EnginioClient *m_client; QMap<QString, QJsonObject> currentModelObject; QString m_conferenceId; QString m_fileNameTag; diff --git a/talk-schedule.pro b/talk-schedule.pro index 563369b..ef2275e 100644 --- a/talk-schedule.pro +++ b/talk-schedule.pro @@ -14,7 +14,8 @@ SOURCES += src/main.cpp \ src/theme.cpp \ src/model.cpp \ src/sortfiltermodel.cpp \ - src/fileio.cpp + src/fileio.cpp \ + src/applicationclient.cpp OTHER_FILES += \ qml/main.qml \ @@ -45,7 +46,8 @@ HEADERS += \ src/theme.h \ src/model.h \ src/sortfiltermodel.h \ - src/fileio.h + src/fileio.h \ + src/applicationclient.h DEFINES += \ TALK_SCHEDULE_BACKEND_ID=$$TALK_SCHEDULE_BACKEND_ID \ |