diff --git a/README.md b/README.md index 651edf8..31bb64a 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,20 @@ Currently supported: * Liking, disliking, favoriting * Update fetches new posts since last in local DB * More shows older posts from local DB -* New Message with images, also possible as DM, Contact selection +* New Message with images, also possible as DM, Contact/Group access rights + ToDo: -* Test new Aspargus features -* Nice symbols for liking, disliking and menu for deletion, reposting and answering (currently press on Contact picture, press on news would interfere with external link functionality) -* Videos as attachment (sending and receiving) +* Nice symbols for liking, disliking +* Videos and other binary data as attachment (sending and receiving) * Rich text editing in Send Dialog +* Attachments for Direct messages (currently not supported in API +* Better image selection (currently ugly file dialog) # Friends # Currently supported: +* Tabs for friends, other contacts and groups * Grid of all known contacts with locally downloaded pictures * Large friend item for addional information and functionality * Show news of contact from local database @@ -42,7 +45,6 @@ Currently supported: ToDo: * More information for contact from description page, possibly private information for friends -* Filter contacts for friends, forums,groups etc. * Groups: create, change, delete @@ -53,25 +55,24 @@ Currently supported: * Show albums and images of contacts ToDo: -* Better download mechanism for own images * Private images of friends * Support for all themes of friends * Delete downloaded own images +* Pinch to zoom, swipe to scroll # Config # Currently supported: * Multiple accounts * Maximum news (deleted after use of Quit button) +* View mode for news (tree or timeline) ToDo * OAuth? -* View mode for news (chronological or tree) # Other # ToDo -* Friendstab and Newstab buggy after account change, restart helps * Calendar tab, video tab * Photo upload to album (needs API change) * Translation @@ -81,5 +82,5 @@ ToDo ## License ## v0.001 for Friendica < 3.5 v0.002 for Friendica >= 3.5 -Pubished under the [GPL v3](http://gplv3.fsf.org). +Published under the [GPL v3](http://gplv3.fsf.org). diff --git a/Screenshots/ConfigTab.jpg b/Screenshots/ConfigTab.jpg index 77b8d21..c63a34d 100644 Binary files a/Screenshots/ConfigTab.jpg and b/Screenshots/ConfigTab.jpg differ diff --git a/Screenshots/FriendsTab.jpg b/Screenshots/FriendsTab.jpg index 609fad9..94d3fe4 100644 Binary files a/Screenshots/FriendsTab.jpg and b/Screenshots/FriendsTab.jpg differ diff --git a/Screenshots/NewsTab.jpg b/Screenshots/NewsTab.jpg index 6c1c2ce..ac283c4 100644 Binary files a/Screenshots/NewsTab.jpg and b/Screenshots/NewsTab.jpg differ diff --git a/Screenshots/PhotoTab.jpg b/Screenshots/PhotoTab.jpg index 5f98a9e..bb51e5f 100644 Binary files a/Screenshots/PhotoTab.jpg and b/Screenshots/PhotoTab.jpg differ diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 5fcdc04..0000000 --- a/TODO.txt +++ /dev/null @@ -1,35 +0,0 @@ -TODO -====== - - -Layout -____________ - - - -Config -____________ - -- Server Domain -- SSL Check -- OAuth? - -Fotos erhalten -_______________ - -- Liste der Fotos mit api/friendica/photos/list -- Daten der Fotos als JSON über api/friendica/photo.xml?photo_id=[ID], "album" als Feld vorhanden -- Raw data in File? -- Download/Sortieren der Fotos? - -Fotos senden -____________ - -- Upload über api/media/upload.xml?media= (raw data)? -- Berechtigungen? Album? -- Album erstellen/bearbeiten - - - - - diff --git a/v0.002/Release/Friendiqa_v0.002.apk b/v0.002/Release/Friendiqa_v0.002.apk new file mode 100644 index 0000000..26d5321 Binary files /dev/null and b/v0.002/Release/Friendiqa_v0.002.apk differ diff --git a/v0.002/Release/source-android/android/AndroidManifest.xml b/v0.002/Release/source-android/android/AndroidManifest.xml new file mode 100644 index 0000000..a22ecbc --- /dev/null +++ b/v0.002/Release/source-android/android/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v0.002/Release/source-android/android/res/drawable/friendiqa.png b/v0.002/Release/source-android/android/res/drawable/friendiqa.png new file mode 100644 index 0000000..d77cd59 Binary files /dev/null and b/v0.002/Release/source-android/android/res/drawable/friendiqa.png differ diff --git a/v0.002/Release/source-android/android/res/drawable/icon.png b/v0.002/Release/source-android/android/res/drawable/icon.png new file mode 100644 index 0000000..d77cd59 Binary files /dev/null and b/v0.002/Release/source-android/android/res/drawable/icon.png differ diff --git a/v0.002/Release/source-android/android/res/drawable/splash.png b/v0.002/Release/source-android/android/res/drawable/splash.png new file mode 100644 index 0000000..25a3580 Binary files /dev/null and b/v0.002/Release/source-android/android/res/drawable/splash.png differ diff --git a/v0.002/Release/source-android/android/res/values/libs.xml b/v0.002/Release/source-android/android/res/values/libs.xml new file mode 100644 index 0000000..4d68673 --- /dev/null +++ b/v0.002/Release/source-android/android/res/values/libs.xml @@ -0,0 +1,25 @@ + + + + https://download.qt-project.org/ministro/android/qt5/qt-5.4 + + + + + + + + + + + + + + + + + + + + diff --git a/v0.002/Release/source-android/application.qrc b/v0.002/Release/source-android/application.qrc new file mode 100644 index 0000000..ecabb77 --- /dev/null +++ b/v0.002/Release/source-android/application.qrc @@ -0,0 +1,27 @@ + + + qml/friendiqa.qml + qml/newsqml/NewsTab.qml + qml/newsqml/Newsitem.qml + qml/newsqml/MessageSend.qml + qml/newsqml/PermissionDialog.qml + qml/contactqml/FriendsTab.qml + qml/contactqml/GroupComponent.qml + qml/contactqml/ContactComponent.qml + qml/contactqml/ContactDetailsComponent.qml + qml/genericqml/BlueButton.qml + qml/photoqml/PhotoComponent.qml + qml/photoqml/PhotogroupComponent.qml + qml/photoqml/PhotoTab.qml + qml/configqml/InfoBox.qml + qml/configqml/ConfigTab.qml + js/layout.js + js/photoworker.js + js/service.js + js/news.js + js/newsworker.js + js/helper.js + images/defaultcontact.jpg + qml/newsqml/Conversation.qml + + diff --git a/v0.002/Release/source-android/common/filesystem.cpp b/v0.002/Release/source-android/common/filesystem.cpp new file mode 100644 index 0000000..758f7c8 --- /dev/null +++ b/v0.002/Release/source-android/common/filesystem.cpp @@ -0,0 +1,49 @@ +#include "filesystem.h" + +FILESYSTEM *FILESYSTEM::instance() +{ + static FILESYSTEM filesystem; + return &filesystem; +} + +FILESYSTEM::FILESYSTEM(QObject *parent) : QObject(parent){} + +void FILESYSTEM::setDirectory(QString Directory) +{ + if (Directory!=m_Directory) { + m_Directory = Directory; + emit directoryChanged(); + } +} + +QString FILESYSTEM::Directory() const +{ + return m_Directory; +} + +void FILESYSTEM::makeDir(QString name) +{ + QDir dir(m_Directory); + if (dir.mkdir(name)){ + emit success(name); + } + else {emit error(name,1);} +} + +void FILESYSTEM::rmDir() +{ + QDir dir(m_Directory); + if (dir.removeRecursively()){ + emit success(m_Directory); + } + else {emit error(m_Directory,1);} +} + +void FILESYSTEM::rmFile(QString name) +{ + QDir dir(m_Directory); + if(dir.remove(name)){ + emit success(name); + } + else {emit error(name,1);} +} diff --git a/v0.002/Release/source-android/common/filesystem.h b/v0.002/Release/source-android/common/filesystem.h new file mode 100644 index 0000000..53d2caf --- /dev/null +++ b/v0.002/Release/source-android/common/filesystem.h @@ -0,0 +1,33 @@ +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + +#include +#include + +class FILESYSTEM : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString Directory READ Directory WRITE setDirectory NOTIFY directoryChanged) + +public: + static FILESYSTEM *instance(); + explicit FILESYSTEM(QObject *parent = 0); + void setDirectory(QString Directory); + QString Directory() const; + +signals: + void directoryChanged(); + void success(QString data); + void error(QString data, int code); + +public slots: + //void setDirectory(QString Directory); + void makeDir(QString name); + void rmDir(); + void rmFile(QString name); + +private: + QString m_Directory; +}; + +#endif // FILSYSTEM_H diff --git a/v0.002/Release/source-android/common/friendiqa.cpp b/v0.002/Release/source-android/common/friendiqa.cpp new file mode 100644 index 0000000..de055f0 --- /dev/null +++ b/v0.002/Release/source-android/common/friendiqa.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include "xhr.h" +#include "filesystem.h" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + QQuickView view; + + + XHR* xhr = XHR::instance(); + view.rootContext()->setContextProperty("xhr", xhr); + FILESYSTEM* filesystem = FILESYSTEM::instance(); + view.rootContext()->setContextProperty("filesystem", filesystem); + view.setSource(QUrl("qrc:/qml/friendiqa.qml")); + view.show(); + view.connect(view.rootContext()->engine(), SIGNAL(quit()), &app, SLOT(quit())); + return app.exec(); + +} + diff --git a/v0.002/Release/source-android/common/uploadableimage.cpp b/v0.002/Release/source-android/common/uploadableimage.cpp new file mode 100644 index 0000000..81bea1a --- /dev/null +++ b/v0.002/Release/source-android/common/uploadableimage.cpp @@ -0,0 +1,90 @@ +#include "uploadableimage.h" + + +#include +#include +#include +#include + +void UploadableImage::setSource(const QString &a) { + if (a != m_source) { + m_source = a; + //m_base64 = ""; + m_mimetype = ""; + m_filename = ""; + + qDebug() << "UploadableImage::setSource : " << m_source; + + if (m_source=="") { + emit sourceChanged(); + //emit base64Changed(); + emit mimetypeChanged(); + emit filenameChanged(); + return; + } + + QImage fullimage = QImage(QUrl(m_source).toLocalFile()); + if (fullimage.width() > 800 || fullimage.height() > 800) { + if (fullimage.width() > fullimage.height()) { + m_image = fullimage.scaledToWidth(800); + } else { + m_image = fullimage.scaledToHeight(800); + } + } else { + m_image = fullimage; + } + qDebug() << "UploadableImage::setSource : " << m_image.width() << "x" << m_image.height(); + emit sourceChanged(); + + QFileInfo fi(m_source); + + m_filename = fi.fileName(); + emit filenameChanged(); + + QString filetype = fi.suffix().toUpper(); + if (filetype!="PNG" && filetype!="JPG") { + filetype = "JPG"; + } + qDebug() << "UploadableImage::setSource : " << "Saving as " << filetype; + + m_mimetype = "image/"+filetype.toLower(); + emit mimetypeChanged(); + + /* + QByteArray byteArray; + QBuffer buffer(&byteArray); + m_image.save(&buffer, filetype.toLatin1().constData()); + QString b64 = QString::fromLatin1(byteArray.toBase64().data()); + + for(int k=0; k +#include +#include + +class UploadableImage : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) + //Q_PROPERTY(QString base64 READ base64 NOTIFY base64Changed) + Q_PROPERTY(QString filename READ filename NOTIFY filenameChanged) + Q_PROPERTY(QString mimetype READ mimetype NOTIFY mimetypeChanged) + Q_PROPERTY(QByteArray bytes READ bytes) + +public: + void setSource(const QString &a); + QString source() const; + + //QString base64() const; + QString filename() const; + QString mimetype() const; + + QByteArray bytes(); +signals: + void sourceChanged(); + //void base64Changed(); + void filenameChanged(); + void mimetypeChanged(); + +private: + QString m_source; + QImage m_image; + //QString m_base64; + QString m_filename; + QString m_mimetype; +}; + +#endif // UPLOADABLEIMAGE_H diff --git a/v0.002/Release/source-android/common/xhr.cpp b/v0.002/Release/source-android/common/xhr.cpp new file mode 100644 index 0000000..9661273 --- /dev/null +++ b/v0.002/Release/source-android/common/xhr.cpp @@ -0,0 +1,235 @@ +#include "xhr.h" + +#include +#include +#include + +#include "uploadableimage.h" + +XHR *XHR::instance() +{ + static XHR xhr; + return &xhr; +} + +XHR::XHR(QObject *parent) : QObject(parent) +{ + request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); +} + +void XHR::setUrl(QString url) +{ + if (url!=m_url) { + m_url = url; + emit urlChanged(); + } +} + +void XHR::setLogin(QString login) +{ + if (login!=m_login) { + m_login = login; + emit loginChanged(); + } +} + +void XHR::setFilename(QString filename) +{ + if (filename!=m_filename) { + m_filename = filename; + emit filenameChanged(); + } +} + +void XHR::setDownloadtype(QString downloadtype) +{ + if (downloadtype!=m_downloadtype) { + m_downloadtype = downloadtype; + emit downloadtypeChanged(); + } +} + +QString XHR::url() const +{ + return m_url; +} + +QString XHR::login() const +{ + return m_login; +} + +QString XHR::filename() const +{ + return m_filename; +} + +QString XHR::downloadtype() const +{ + return m_downloadtype; +} + +void XHR::setParam(QString name, QString value) +{ + params.insert(name, value); +} + +void XHR::setImageFileParam(QString name, QString url) +{ + files.insert(name, url); +} + +void XHR::clearParams() +{ + files.clear(); + params.clear(); +} + +void XHR::download() +{ + QUrl requrl(m_url); + //qDebug()<< "replyerror"<error(); +// qDebug() << "start download of " << requrl; + request.setUrl(requrl); + reply = manager.get(request); +// qDebug() << "reply " << reply->header(QNetworkRequest::LocationHeader)<header(QNetworkRequest::LastModifiedHeader); + // qDebug() << "request " << request.url(); + // reply->ignoreSslErrors(); + connect(reply, &QNetworkReply::readyRead,this, &XHR::onReadyRead); + //connect(reply,SIGNAL(downloadProgress(qint64,qint64)), this,SLOT(updateDownloadProgress(qint64,qint64))); + connect(reply, &QNetworkReply::finished,this, &XHR::onRequestFinished); + //connect(reply, SIGNAL(finished()),this, SLOT(onRequestFinished())); + connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError))); +} + +void XHR::get() +{ + QUrlQuery query; + + QHashIterator i(params); + while(i.hasNext()) { + i.next(); + query.addQueryItem(i.key(), i.value()); + } + + QUrl requrl(m_url); + requrl.setQuery(query); + + QByteArray loginData = m_login.toLocal8Bit().toBase64(); + QString headerData = "Basic " + loginData; + request.setRawHeader("Authorization", headerData.toLocal8Bit()); + + + request.setUrl(requrl); + reply = manager.get(request); + + connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError))); + connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead); + connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError); +} + +void XHR::post() +{ + qDebug() << "start post to " << m_url; + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHashIterator iparams(params); + while(iparams.hasNext()) { + iparams.next(); + qDebug() << "\t add param " << iparams.key() << " : " << iparams.value(); + QHttpPart textPart; + textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + iparams.key() + "\"")); + + + textPart.setBody(iparams.value().toUtf8()); + multiPart->append(textPart); + } + + UploadableImage uimg; + QHashIterator ifiles(files); + while(ifiles.hasNext()) { + ifiles.next(); + + uimg.setSource(ifiles.value()); + qDebug() << "\t image: " << uimg.mimetype() << ", " << ifiles.key(); + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(uimg.mimetype())); + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + ifiles.key() + "\"; filename=\""+uimg.filename()+"\"")); + imagePart.setBody(uimg.bytes()); + multiPart->append(imagePart); + } + + QByteArray loginData = m_login.toLocal8Bit().toBase64(); + QString headerData = "Basic " + loginData; + request.setRawHeader(QByteArray("Authorization"), headerData.toLocal8Bit()); + + request.setUrl(m_url); + reply = manager.post(request, multiPart); + qDebug() << "\t request sent"; + connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError))); + connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead); + connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError); + qDebug() << "\t reply signals connected"; +} + +void XHR::onReplyError(QNetworkReply::NetworkError code) +{ + qDebug() << code; + emit this->error( bufferToString(), (int) code); + reply->deleteLater(); +} + +void XHR::onReplySuccess() +{ + qDebug() << "!"; + emit this->success( bufferToString() ); + reply->deleteLater(); +} + +void XHR::onRequestFinished() +{ + // Save the file here + //qDebug() << "buffer downloaded "<error(m_downloadtype,1);} + else {QFile file(m_filename); + file.open(QIODevice::WriteOnly); + file.write(buffer); + buffer.clear(); + file.close(); + //qDebug() << m_url << "File downloaded "<downloaded(m_downloadtype); + //reply->deleteLater(); + } +} + +void XHR::onReadyRead() +{ + qDebug() << "."; + buffer += reply->readAll(); +} + +//void XHR::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes) +//{ +// qDebug() << "Bytes: " << bytesRead<<" / "< &errors) +{ + qDebug() << "XHR::onSSLError :" ; + QListIterator ierrs(errors); + while(ierrs.hasNext()) { + qDebug() << "\t" << ierrs.next().errorString(); + } +} + +QString XHR::bufferToString() +{ + return QTextCodec::codecForName("utf-8")->toUnicode(buffer); +} + + diff --git a/v0.002/Release/source-android/common/xhr.h b/v0.002/Release/source-android/common/xhr.h new file mode 100644 index 0000000..6e93c6c --- /dev/null +++ b/v0.002/Release/source-android/common/xhr.h @@ -0,0 +1,73 @@ +#ifndef XHR_H +#define XHR_H + +#include +#include +#include +#include + +class XHR : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QString login READ login WRITE setLogin NOTIFY loginChanged) + Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged) + Q_PROPERTY(QString downloadtype READ downloadtype WRITE setDownloadtype NOTIFY downloadtypeChanged) + + +public: + static XHR *instance(); + + explicit XHR(QObject *parent = 0); + + QString url() const; + QString login() const; + QString filename() const; + QString downloadtype() const; + +signals: + void urlChanged(); + void loginChanged(); + void filenameChanged(); + void downloadtypeChanged(); + void downloaded(QString data); + void success(QString data); + void error(QString data, int code); + +public slots: + void setUrl(QString url); + void setLogin(QString login); + void setDownloadtype(QString downloadtype); + void setFilename(QString filename); + void setParam(QString name, QString value); + void setImageFileParam(QString name, QString url); + void clearParams(); + void post(); + void get(); + void download(); + +private slots: + void onReplyError(QNetworkReply::NetworkError code); + void onReplySuccess(); + void onRequestFinished(); + void onReadyRead(); + void onSSLError(const QList &errors); + //void updateDownloadProgress(qint64 bytesRead, qint64 totalBytes); + +private: + QByteArray buffer; + QString m_url; + QString m_login; + QString m_filename; + QString m_downloadtype; + QHash params; + QHash files; + + QNetworkAccessManager manager; + QNetworkRequest request; + QNetworkReply *reply; + + QString bufferToString(); +}; + +#endif // XHR_H diff --git a/v0.002/Release/source-android/friendiqa.pro b/v0.002/Release/source-android/friendiqa.pro new file mode 100644 index 0000000..484b220 --- /dev/null +++ b/v0.002/Release/source-android/friendiqa.pro @@ -0,0 +1,55 @@ +# NOTICE: +# +# Application name defined in TARGET has a corresponding QML filename. +# If name defined in TARGET is changed, the following needs to be done +# to match new name: +# - corresponding QML filename must be changed +# - desktop icon filename must be changed +# - desktop filename must be changed +# - icon definition filename in desktop file must be changed +# - translation filenames have to be changed + +# The name of your application +TARGET = friendiqa +CONFIG += debug +QT += qml quick gui widgets + +SOURCES += common/friendiqa.cpp \ + common/uploadableimage.cpp \ + common/xhr.cpp \ + common/filesystem.cpp + +RESOURCES = application.qrc + +OTHER_FILES += qml/friendiqa.qml \ + translations/*.ts \ + qml/*.qml + qml/newsqml/*.qml + qml/contactqml/*.qml + qml/photoqml/*.qml + qml/configqml/*.qml + js/*.js + +# German translation is enabled as an example. If you aren't +# planning to localize your app, remember to comment out the +# following TRANSLATIONS line. And also do not forget to +# modify the localized app name in the the .desktop file. +TRANSLATIONS += translations/friendiqa-de.ts + +HEADERS += \ + common/uploadableimage.h \ + common/xhr.h \ + common/filesystem.h + +DISTFILES += \ + android/AndroidManifest.xml \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/gradlew \ + android/res/values/libs.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew.bat + +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + + diff --git a/v0.002/Release/source-android/images/defaultcontact.jpg b/v0.002/Release/source-android/images/defaultcontact.jpg new file mode 100644 index 0000000..bb7bce2 Binary files /dev/null and b/v0.002/Release/source-android/images/defaultcontact.jpg differ diff --git a/v0.002/Release/source-android/js/friendworker.js b/v0.002/Release/source-android/js/friendworker.js new file mode 100644 index 0000000..6b6c5be --- /dev/null +++ b/v0.002/Release/source-android/js/friendworker.js @@ -0,0 +1,12 @@ +WorkerScript.onMessage = function(msg) { + msg.model.clear(); + for (var j=0;j 0 ) { + for (var i in list) { if (list[i][prop] === val) { + return i; + } + } + } return -1; +} +function cleanArray(array) { +var arraystring=JSON.stringify(array); + arraystring=arraystring.replace(/[\[\]]/g , ''); +return arraystring; +} diff --git a/v0.002/Release/source-android/js/layout.js b/v0.002/Release/source-android/js/layout.js new file mode 100644 index 0000000..23815fd --- /dev/null +++ b/v0.002/Release/source-android/js/layout.js @@ -0,0 +1,44 @@ +function showFriends(db) { + Service.readActiveConfig(db,function(login){ + Service.requestFriends(login.url,login.user,login.password,displayFriends); + }); +} +function displayFriends(obj){ + for (var i=0; i= c.x) + f.contentX = c.x; + else if (f.contentX+f.width <= c.x+c.width) + f.contentX = c.x+c.width-f.width; + if (f.contentY >= c.y) + f.contentY = c.y; + else if (f.contentY+f.height <= c.y+c.height) + f.contentY = c.y+c.height-f.height; +} + +function createObject(objectQml,qmlParameters,parentitem,callback) { + var component = Qt.createComponent(objectQml); + if (component.status === Component.Ready || component.status === Component.Error) + finishCreation(component,qmlParameters,parentitem,callback); + else + component.statusChanged.connect(finishCreation(qmlParameters)); +} + +function finishCreation(component,qmlParameters,parentitem,callback) { + if (component.status === Component.Ready) { + var createdObject = component.createObject(parentitem, qmlParameters); + if (createdObject === null) + print("Error creating image"); } + else if (component.status === Component.Error) + print("Error loading component:"+component.errorString()); + else {print("created")} + //callback(createdObject); +} + diff --git a/v0.002/Release/source-android/js/news.js b/v0.002/Release/source-android/js/news.js new file mode 100644 index 0000000..a23fb58 --- /dev/null +++ b/v0.002/Release/source-android/js/news.js @@ -0,0 +1,399 @@ +.pragma library +.import QtQuick.LocalStorage 2.0 as Sql +.import "qrc:/js/helper.js" as Helperjs + +function requestFriends(login,database,rootwindow,callback){ +// return array of friends + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + db.transaction( function(tx) { + var result = tx.executeSql('UPDATE contacts SET isFriend=0 where username="'+login.username+'"')}); // clean old friends + Helperjs.friendicaRequest(login,"/api/statuses/friends", rootwindow,function (obj){ + var friends=JSON.parse(obj); + for (var i=0;i'+lastDate); + var result2 = tx.executeSql('SELECT id from contacts WHERE username="'+login.username+'" AND isFriend=0 AND imageAge > '+lastDate); + for (var j=0;j0){ + for (var j=0;j0){ + for (var k=0;j1){ + var helpernews=newsrs.rows.item(0); + helpernews.newscount=newsrs.rows.length; + helpernews=fetchUsersForNews(database,user,helpernews) + newsArray.push(helpernews); + //} +} + callback(newsArray); + })} + +function inArray(list, prop, val) { + if (list.length > 0 ) { + for (var i in list) {if (list[i][prop] == val) { + return true; + } + } + } return false; +} + +function cleanDate(date){ +var cleanedDate= date.slice(0,3)+", "+date.slice(8,11)+date.slice(4,7)+date.slice(25,30)+date.slice(10,25); +return cleanedDate +} diff --git a/v0.002/Release/source-android/js/newsworker.js b/v0.002/Release/source-android/js/newsworker.js new file mode 100644 index 0000000..9ae91b6 --- /dev/null +++ b/v0.002/Release/source-android/js/newsworker.js @@ -0,0 +1,63 @@ +WorkerScript.onMessage = function(msg) { + if(msg.appendnews!==true){ msg.model.clear()}; + + for (var j=0;j0){ + if (newsitemobject.like.length==1){likeText= Qt.atob(newsitemobject.like[0].name)+" "+ qsTr("likes this.")} + else {likeText= newsitemobject.like.length+" "+ qsTr("like this.")} + } + if (newsitemobject.dislike.length>0){ + if (newsitemobject.dislike.length==1){dislikeText= QT.atob(newsitemobject.dislike[0].name)+" "+ qsTr("doesn't like this.")} + else {dislikeText= newsitemobject.dislike.length+" "+ qsTr("don't like this.")} + } + if (newsitemobject.attendyes.length>0){ + if (newsitemobject.attendyes.length==1){attendyesText= Qt.atob(newsitemobject.attendyes[0].name)+" "+ qsTr("will attend.")} + else {attendyesText= newsitemobject.attendyes.length+" "+ qsTr("persons will attend.")} + } + if (newsitemobject.attendno.length>0){ + if (newsitemobject.attendno.length==1){attendnoText= Qt.atob(newsitemobject.attendno[0].name)+" "+ qsTr("will not attend.")} + else {attendnoText= newsitemobject.attendno.length+" "+ qsTr("persons will not attend.")} + } + if (newsitemobject.attendmaybe.length>0){ + if (newsitemobject.attendmaybe.length==1){attendmaybeText= Qt.atob(newsitemobject.attendmaybe[0].name)+" "+ qsTr("may attend.")} + else {attendmaybeText= newsitemobject.attendmaybe.length+" "+ qsTr("persons may attend.")} + } + var friendica_activities_self=JSON.parse(newsitemobject.friendica_activities_self); + if (friendica_activities_self.indexOf(3)!=-1){self.attending=qsTr("yes")} + if (friendica_activities_self.indexOf(4)!=-1){self.attending=qsTr("no")} + if (friendica_activities_self.indexOf(5)!=-1){self.attending=qsTr("maybe")} + if (friendica_activities_self.indexOf(1)!=-1){self.liked=1} + if (friendica_activities_self.indexOf(2)!=-1){self.disliked=1} + } + var friendica_activities={likeText:likeText,dislikeText:dislikeText,attendyesText:attendyesText,attendnoText:attendnoText,attendmaybeText:attendmaybeText,self:self} + //print(JSON.stringify(friendica_activities) ) ; + var seconds=(msg.currentTime-newsitemobject.created_at)/1000; + var timestring=""; + if (seconds<60) {timestring=seconds+" "+qsTr("seconds") +" "+qsTr("ago");} + else if (seconds<90){timestring=Math.round(seconds/60)+" "+qsTr("minute") +" "+qsTr("ago");} + else if (seconds<3600){timestring=Math.round(seconds/60)+" "+qsTr("minutes") +" "+qsTr("ago");} + else if (seconds<5400){timestring=Math.round(seconds/3600)+" "+qsTr("hour") +" "+qsTr("ago");} + else if (seconds<86400){timestring=Math.round(seconds/3600)+" "+qsTr("hours") +" "+qsTr("ago");} + else if (seconds<129600){timestring=Math.round(seconds/86400)+" "+qsTr("day") +" "+qsTr("ago");} + else if (seconds<3888000){timestring=Math.round(seconds/86400)+" "+qsTr("days") +" "+qsTr("ago");} + else if (seconds<5832000){timestring=Math.round(seconds/3888000)+" "+qsTr("month") +" "+qsTr("ago");} + else if (seconds<69984000){timestring=Math.round(seconds/3888000)+" "+qsTr("months") +" "+qsTr("ago");} + else {timestring=Math.round(seconds/69984000)+" "+qsTr("years") +" "+qsTr("ago");} + var data=({"newsitemobject": newsitemobject,"dateDiff":timestring,"friendica_activities":friendica_activities})} + //print("News:"+j+msg.news.length+JSON.stringify(data)); + msg.model.append(data);} + if (j==msg.news.length){ + msg.model.sync() + }; +} diff --git a/v0.002/Release/source-android/js/photoworker.js b/v0.002/Release/source-android/js/photoworker.js new file mode 100644 index 0000000..36646c7 --- /dev/null +++ b/v0.002/Release/source-android/js/photoworker.js @@ -0,0 +1,15 @@ +WorkerScript.onMessage = function(msg) { + if (msg.firstalbum==0){msg.model.clear();} + var limit=0; if (msg.albums.length-msg.firstalbum<20){limit=msg.albums.length} else{limit=msg.firstalbum+20} + for (var j=msg.firstalbum;j0){ + for(var i=0;i< AllStoredImages.length;i++){ + var position=Helperjs.inArray(obj,"resourceID",AllStoredImages[i]); + if (position>-1){obj.splice(position,1)} + //obj.splice(obj.indexOf(AllStoredImages[i]),1);// list of objects instead of list!!! + }} + callback(obj) + }) +})} + +function dataRequest(login,photoID,database,rootwindow) { + // check if image exist and call download function + Helperjs.friendicaRequest(login,"/api/friendica/photo?photo_id="+photoID, rootwindow, function (image){ + try{ if(image==""){currentimageno=currentimageno+1}else{ + var obj = JSON.parse(image); + var helpfilename=obj.filename.substring(0,obj.filename.lastIndexOf(".")); + var filesuffix=""; + if (obj.type=="image/jpeg"){filesuffix=".jpg"} + else if (obj.type=="image/png"){filesuffix=".png"} + else {filesuffix=""} + + if (helpfilename==""){// check if file has any filename + obj.filename=obj["id"]+filesuffix; + } + else{obj.filename=helpfilename+filesuffix} + + var link=""; + if(obj["link"][0]){link=obj["link"][0]} else{link=obj["link"]["4"]} + xhr.setUrl(Qt.resolvedUrl(link)); + xhr.setFilename(login.imagestore+'albums/'+obj.album+"/"+obj["filename"]); + xhr.setDownloadtype("picture"); + xhr.download(); + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + db.transaction( function(tx) { + var result = tx.executeSql('SELECT * from imageData where id = "'+obj["id"]+'"'); + if(result.rows.length === 1) {// use update + result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+obj.id+'", created="'+obj.created+'", edited="'+obj.edited+'", profile="'+obj.profile+'", link="'+obj["link"]["4"]+'", filename="'+obj.filename+'",title="'+obj.title+'", desc="'+obj.desc+'", type="'+obj.type+'", width="'+obj.width+'", height="'+obj.height+'", album="'+obj.album+'", location="file://'+login.imagestore+'albums/'+obj.album+'/" where id="'+obj["id"]+'"'); + } else {// use insert print('... does not exists, create it') + result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,obj.id,obj.created,obj.edited, obj.title, obj.desc, obj.album, obj.filename, obj.type, obj.height, obj.width,obj. profile,obj["link"]["4"],'file://'+login.imagestore+'albums/'+obj.album+"/"]); + } + })}} + catch (e){print("Data retrieval failure! "+ e+obj);} +})} + + +function deleteImageData(database,user,field,selection,callback) { // does nothing useful at the moment + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + //print(' delete Image Data() for ' + field +"="+selection) + db.transaction( function(tx) { + result = tx.executeSql('UPDATE imageData SET data="" where '+ field +'="'+selection+'"'); + callback(result)}) +} + +function requestFriendsAlbumPictures(login,friend,rootwindow,callback){ +// screenscraping of albums page of contact without user and password + Helperjs.friendicaWebRequest(friend.url.replace("profile","photos"),rootwindow,function(photohtml){ + //print(photohtml); + var photoarray=[]; + var arr = photohtml.split("sidebar-photos-albums-li"); + for (var i=2;i')-1); + var album={'link':albumlink,'name':albumname} + photoarray.push(album); + } + callback(photoarray) + }) +} + +function requestFriendsPictures(link,rootwindow,callback){ +// screenscraping of pictures page for given album + Helperjs.friendicaWebRequest(link,rootwindow,function(photohtml){ + var photoarray=[]; + var basehtml=photohtml.substring(photohtml.indexOf('',photohtml.indexOf('-1){ //theme 1 + var arr = photohtml.split("photo-album-image-wrapper-end");} + +// other themes + if (photohtml.indexOf("photo-album-wrapper")>-1){ //theme 2 + var photoarea=photohtml.substring(photohtml.indexOf("photo-album-wrapper"),photohtml.indexOf("photo-album-end")) + var arr = photoarea.split("");} + + for (var i=0;i0){ + for(var i = 0; i < rs.rows.length; i++) { + rsArray.push(rs.rows.item(i)) + } + var rsObject={server:rsArray[0].server,username:rsArray[0].username, password:rsArray[0].password,imagestore:rsArray[0].imagestore,maxnews:rsArray[0].maxnews,isActive:rsArray[0].isActive,timerInterval:rsArray[0].timerInterval, newsViewType:rsArray[0].newsViewType,permissions:JSON.parse(rsArray[0].permissions),maxContactAge:rsArray[0].maxContactAge,APIVersion:rsArray[0].APIVersion}; + } else {var rsObject=""} + callback(rsObject)}} + ) +} + +function readActiveConfig(database){ + var obj; + readConfig(database,function(config){obj=config},"isActive", 0); + return obj; +} + +function deleteConfig(database,userobj,callback) { // delete user data from DB + if (userobj){var where = " WHERE username='"+ userobj.username+"' and server='"+userobj.server+"'";} else { return "no user selected!";} + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + if(!db) { return; } + db.transaction( function(tx) { + var rs = tx.executeSql('delete from config'+where); + callback(rs); + }) +} + +function cleanNews(database,callback){ + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + db.transaction( function(tx) { + var maxnewsrs = tx.executeSql("SELECT DISTINCT maxnews FROM config"); + var maxnews=maxnewsrs.rows.item(0).maxnews; + var newscountrs = tx.executeSql('SELECT COUNT(*) from news'); + var newscount = newscountrs.rows.item(0)["COUNT(*)"]; + if (newscount>maxnews){var lastvalidtimers= tx.executeSql('select created_at from news ORDER BY created_at DESC LIMIT ' +(newscount-maxnews)); + var lastvalidtime=lastvalidtimers.rows.item(maxnews).created_at; + var deleters = tx.executeSql('DELETE from news WHERE created_at<='+lastvalidtime)} + callback() + }) + } + +function processNews(callback){ + Newsjs.getCurrentContacts(login,db,function(contacts){ + contactlist=contacts}); + + if (contactLoadType=="news"){ + if(root.news.length==0){} + else{// show news + Newsjs.storeNews(login,db,news,root,function(dbnews){ + root.newsSignal(dbnews); + newstab.newstabstatus=login.newsViewType + })} + } + else if (contactLoadType=="friends"){// show friends + root.friendsSignal(login.username); + Newsjs.getCurrentContacts(login,db,function(contacts){ + contactlist=contacts; + })} + + else if (contactLoadType=="conversation"){ + var conversationid=news[0].statusnet_conversation_id + if (!isNaN(parseInt(conversationid))){//no directmessage conversation + Newsjs.storeNews(root.login,root.db,news,root,function(){ + Newsjs.conversationfromdb(db,root.login.username,conversationid, function(newsarray){ + newstab.conversation=newsarray; + }) + })} + else {newstab.conversation=news}//only DM conversations from database + } + else if (contactLoadType=="favorites"){//show favorited news + Newsjs.storeNews(root.login,root.db,news,root,function(){ + Newsjs.favoritesfromdb(db,login.username,function(newsarray){ + root.newsSignal(newsarray); + newstab.newstabstatus="Favorites"; + }) + })} + callback() +} + +function updateContactInDB(login,database,isFriend,contact){// for newstab and friendstab + var suffix=contact.profile_image_url.substring(contact.profile_image_url.lastIndexOf("."), contact.profile_image_url.length); + var imagename=login.imagestore+"contacts/"+contact.screen_name.trim()+suffix; + contacttimer.restart(); + var currentTime=Date.now(); + xhr.setUrl(Qt.resolvedUrl(contact.profile_image_url)); + xhr.setFilename(imagename); + xhr.setDownloadtype("contact"); + xhr.download(); + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + var result; + db.transaction( function(tx) { + result = tx.executeSql('SELECT * from contacts where username="'+root.login.username+'" AND id = '+contact.id); // check for news id + if(result.rows.length === 1) {// use update + result = tx.executeSql('UPDATE contacts SET username="'+login.username+'", id='+contact.id+', name="'+Qt.btoa(contact.name)+'", screen_name="'+contact.screen_name+'", location="'+contact.location+'",imageAge='+currentTime+', profile_image_url="'+contact.profile_image_url+'", description="'+Qt.btoa(contact.description)+'", profile_image="'+imagename+'", url="'+contact.url+'" , protected="'+contact.protected+'", followers_count='+contact.followers_count+', friends_count='+contact.friends_count+', created_at="'+ Date.parse(Newsjs.cleanDate(contact.created_at))+'", favourites_count="'+contact.favorites_count+'", utc_offset="'+contact.utc_offset+'", time_zone="'+contact.time_zone+'", statuses_count='+contact.statuses_count+', following="'+contact.following+'", verified ="'+contact.verified+'", statusnet_blocking="'+contact.statusnet_blocking+'", notifications="'+contact.notifictions+'", statusnet_profile_url="'+contact.statusnet_profile_url+'", cid='+contact.cid+', network="'+contact.network+'", isFriend='+isFriend+' where username="'+root.login.username+'" AND id='+contact.id); + } else {// use insert + result = tx.executeSql('INSERT INTO contacts VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,contact.id,Qt.btoa(contact.name),contact.screen_name,contact.location,currentTime,contact.profile_image_url, Qt.btoa(contact.description),imagename,contact.url,contact.protected,contact.followers_count, contact.friends_count,Date.parse(Newsjs.cleanDate(contact.created_at)),contact.favorites_count,contact.utc_offset,contact.time_zone,contact.statuses_count,contact.following,contact.verfied,contact.statusnet_blocking,contact.notifications,contact.statusnet_profile_url,contact.cid,contact.network,isFriend]);} + }); +} diff --git a/v0.002/Release/source-android/qml/configqml/ConfigTab.qml b/v0.002/Release/source-android/qml/configqml/ConfigTab.qml new file mode 100644 index 0000000..c7dd2a3 --- /dev/null +++ b/v0.002/Release/source-android/qml/configqml/ConfigTab.qml @@ -0,0 +1,314 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.2 +import "qrc:/js/service.js" as Service +import "qrc:/js/layout.js" as Layoutjs +import "qrc:/js/helper.js" as Helperjs +import "qrc:/qml/configqml" +import "qrc:/qml/genericqml" + +StackView{ + id: configStack + anchors.fill:parent + initialItem: Flickable{ + width:root.width-5*mm + height:root.height-12*mm + contentHeight: configBackground.height + boundsBehavior: Flickable.StopAtBounds + Rectangle{ + id:configBackground + color: "grey" + width:parent.width + height:Math.max(90*mm,root.height-12*mm) + property var users:[] + BlueButton{ + id:userButton + text:qsTr("User") + y:mm + width: root.width/2 + onClicked:{ + var useritems=""; + for (var i=0;iFriendiqa v0.002
Licensed under GPL 3
"+ + "Profile https://freunde.ma-nic.de/profile/friendiqa
"+ + "Sourcecode: https://github.com/LubuWest/Friendica
"+ + "C++ code by Fabio
"+ + "QML and Javascript code by Marco" + onLinkActivated:{ + Qt.openUrlExternally(link)} + } + BlueButton{ + text:qsTr("Close") + onClicked:{configStack.pop()} + anchors.top:infoBoxText.bottom + } + } diff --git a/v0.002/Release/source-android/qml/contactqml/ContactComponent.qml b/v0.002/Release/source-android/qml/contactqml/ContactComponent.qml new file mode 100644 index 0000000..26432bc --- /dev/null +++ b/v0.002/Release/source-android/qml/contactqml/ContactComponent.qml @@ -0,0 +1,67 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import "qrc:/js/layout.js" as Layoutjs +import "qrc:/qml/genericqml" + +Item { +id: contactComponent +property var createdAtDate: new Date(contact.created_at) +property var linkUrl: contact.network!=="dfrn"?contact.url:contact.url.replace("profile","dfrn_request") +Rectangle { + id: wrapper + width: 16*mm + height: 15*mm + border.color: "grey" + color:"white" + Image { + id: photoImage + x:1 + y:1 + width: 10*mm + height:10*mm + source:(contact.profile_image!="")? "file://"+contact.profile_image : contact.profile_image_url + onStatusChanged: if (photoImage.status == Image.Error) {source="qrc:/images/defaultcontact.jpg"} + } + + Label { + id: namelabel + x: 1 + width: wrapper.width-4 + height: 3*mm + text: contact.screen_name + elide:Text.ElideRight + anchors.topMargin: 0 + anchors.left: photoImage.left + color: "#303030" + font.pixelSize: 3*mm + anchors.top: photoImage.bottom + } + BlueButton{ + id:infobutton + width: 5*mm + height: 5*mm + color:"transparent" + text:"?" + anchors.left: photoImage.right + anchors.leftMargin: 3 + anchors.topMargin: 3 + anchors.top: parent.top + onClicked:{ + contactComponent.state="large"; + var component = Qt.createComponent("qrc:/qml/contactqml/ContactDetailsComponent.qml"); + if (component.status== Component.Ready){ + var contactDetails = component.createObject(wrapper,{"contact": contact})} + } + } + } +states: [ + State { + name: "large" + PropertyChanges { target: namelabel; font.pixelSize: 4*mm; width:friendsTabView.width-4*mm; text:Qt.atob(contact.name)+" (@"+contact.screen_name+")"} + PropertyChanges { target: contactComponent; z: 2 } + PropertyChanges { target: wrapper; width:friendsTabView.width-3*mm;height:friendsTabView.height-10*mm} + PropertyChanges { target: photoImage; width:15*mm;height:15*mm } + PropertyChanges { target:contactComponent.GridView.view;contentY:contactComponent.y;contentX:contactComponent.x;interactive:false} + } +] +} diff --git a/v0.002/Release/source-android/qml/contactqml/ContactDetailsComponent.qml b/v0.002/Release/source-android/qml/contactqml/ContactDetailsComponent.qml new file mode 100644 index 0000000..da82738 --- /dev/null +++ b/v0.002/Release/source-android/qml/contactqml/ContactDetailsComponent.qml @@ -0,0 +1,81 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import "qrc:/qml/genericqml" + +Rectangle{ + id: detailsrectangle + anchors.top: namelabel.bottom + anchors.topMargin: 2*mm + //opacity: 0 + + ScrollView{ + horizontalScrollBarPolicy:Qt.ScrollBarAlwaysOff + frameVisible: true + //Flickable{ + id:namelabelflickable + width: root.width-10*mm + height:friendsTabView.height-45*mm + //boundsBehavior:Flickable.StopAtBounds + //flickableDirection:Flickable.VerticalFlick + //contentWidth:width + //contentHeight: namelabeltext.height + clip:true + Text{ + id:namelabeltext + width: namelabelflickable.width + height: implicitHeight + font.pixelSize: 3*mm + textFormat:Text.RichText + wrapMode: Text.Wrap + text:""+qsTr("Description")+": "+Qt.atob(contact.description)+"
"+qsTr("Location")+": "+contact.location+"
"+qsTr("Posts")+": "+contact.statuses_count+ + "
"+qsTr("URL")+": "+linkUrl+"
"+ + qsTr("Created at")+": "+createdAtDate.toLocaleString(Qt.locale()) + onLinkActivated: { + Qt.openUrlExternally(link)} + } + } + + Row{ + anchors.top: namelabelflickable.bottom + anchors.topMargin: 2*mm + spacing:4 + + BlueButton{ + id:photobutton + text:"Photos" + visible:contact.network=="dfrn"? 1:0 + onClicked:{contactComponent.state="";detailsrectangle.destroy(); + root.currentIndex=2; + fotostab.active=true; + root.fotoSignal(contact) ; + } + } + + BlueButton{ + id:messagebutton + text:"Messages" + onClicked:{contactComponent.state="";detailsrectangle.destroy(); + root.currentIndex=0; + newstab.active=true; + root.messageSignal(contact.id) ; + } + } + + BlueButton{ + id:dmbutton + visible: contact.following=="true"?true:false + text: "DM" + onClicked:{contactComponent.state="";detailsrectangle.destroy(); + root.currentIndex=0; + newstab.active=true; + root.directmessageSignal(contact.screen_name); + } + } + + BlueButton{ + id: closeButton + text: "close" + onClicked:{contactComponent.state="";detailsrectangle.destroy()} + } + } + } diff --git a/v0.002/Release/source-android/qml/contactqml/FriendsTab.qml b/v0.002/Release/source-android/qml/contactqml/FriendsTab.qml new file mode 100644 index 0000000..c2e3521 --- /dev/null +++ b/v0.002/Release/source-android/qml/contactqml/FriendsTab.qml @@ -0,0 +1,197 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.4 +import "qrc:/js/service.js" as Service +import "qrc:/js/helper.js" as Helperjs +import "qrc:/js/news.js" as Newsjs +import "qrc:/qml/contactqml" +import "qrc:/qml/genericqml" + +Rectangle { + y:1 + color: "white" + TabView{ + id:friendsTabView + tabPosition: Qt.TopEdge + x:mm + y:mm + width: root.width-2*mm + height: root.height-10*mm + currentIndex: 0 + + signal contactsSignal(var username) + signal groupsSignal(var username) + onCurrentIndexChanged:{ + if (currentIndex==0){root.friendsSignal(root.login.username)} + else if (currentIndex==1){contactsSignal(root.login.username)} + else if (currentIndex==2){groupsSignal(root.login.username)} + } + style: TabViewStyle { + frameOverlap: 1 + tab: Rectangle { + color: "white" + //border.color: "light grey" + implicitWidth: root.width/3-2*mm + implicitHeight: 4*mm + Text { id: text + anchors.centerIn: parent + text: styleData.title + color: "dark grey" + font.pixelSize:2.5*mm + font.bold: styleData.selected + } + } + frame: Rectangle { color: "light grey" } + tabsAlignment:Qt.AlignHCenter + } + + Tab{ + title: qsTr("Friends") + Rectangle{ + id: friendsGridTab + function showFriends(username){ + try {friendsModel.clear()} catch(e){print(e)}; + Helperjs.readData(db,"contacts",username,function(friendsobject){ + for (var i=0;i0){// download first contact image and update db + Service.updateContactInDB(login,db,newContacts[currentContact].isFriend,newContacts[currentContact])} + else if (contactLoadType!=""){ + Service.processNews(function(){ + root.contactLoadType=""; + root.news=[]; + })} + } + + onCurrentContactChanged:{// download next contact image after photoplaceholder is finished saving and update db + if(currentContact0) {xhr.setParam("group_allow", Helperjs.cleanArray(group_allow))}; + if (group_deny.length>0) {xhr.setParam("group_deny", Helperjs.cleanArray(group_deny))}; + if (contact_allow.length>0) {xhr.setParam("contact_allow", Helperjs.cleanArray(contact_allow))}; + if (contact_deny.length>0) {xhr.setParam("contact_deny", Helperjs.cleanArray(contact_deny))}; + if (attachImageURL!=="") {xhr.setImageFileParam("media", attachImageURL )}; + xhr.post(); + } + + function dmUpdate(title,text,replyto,screen_name,attachImageURL) { + xhr.url= login.server + "/api/direct_messages/new.xml"; + xhr.setLogin(login.username+":"+Qt.atob(login.password)); + xhr.clearParams(); + xhr.setParam("text", text); + xhr.setParam("screen_name", screen_name); + if (parentId!="") {xhr.setParam("replyto", replyto)}; + if (title!=="") {xhr.setParam("title", title)}; + xhr.post(); + } + + Column { + id:messageColumn + spacing: 2 + width: parent.width + TextField { + id: titleField + width: parent.width + placeholderText: qsTr("Title (optional)") + visible: messageSend.parentId === "" + } + + TextArea { + id: bodyField + width: parent.width + height: 30*mm + wrapMode: TextEdit.Wrap + textFormat: TextEdit.PlainText + } + + Row{ + spacing: 2 + CheckBox{ + id:dmCheckbox + text:"DM" + enabled: false + checked: (directmessage==1)?true:false + onClicked:{ + if(dmCheckbox.checkedState==Qt.Checked){directmessage=1} + else if(dmCheckbox.checkedState==Qt.Unchecked){directmessage=0} + } + } + + BlueButton{ + text:qsTr("Url") + onClicked: { + if(bodyField.selectedText==""){Helperjs.showMessage("Error","No text selected",messageSend)} + else{urlTextEdit.text=""; + urlRectangle.visible=true}} + } + Rectangle{ + id:urlRectangle + height:parent.height + width:37*mm + visible:false + TextField{ + id:urlTextEdit + width:30*mm + height:parent.height + } + BlueButton{ + anchors.left:urlTextEdit.right + anchors.leftMargin:mm + text:qsTr("\u2713") + onClicked: {if(urlTextEdit.text!=""){ + var start = bodyField.selectionStart; + var end = bodyField.selectionEnd; + var text = bodyField.getText(start,end); + text = "[url="+urlTextEdit.text+"]" + text + "[/url]"; + bodyField.remove(start,end); + bodyField.insert(start,text);} + urlRectangle.visible=false} + } + } + } + + + Row{ + spacing:2 + BlueButton{ + visible: (directmessage==1)?false:true + text:qsTr("Permissions") + onClicked: { + var component = Qt.createComponent("qrc:/qml/newsqml/PermissionDialog.qml"); + var permissions = component.createObject(messageColumn); + }} + BlueButton { + id: attachButton + text: qsTr("Attach") + visible:(directmessage==0) + onClicked: { + if (attachImageURL!=""){ + Helperjs.showMessage( qsTr("Error"),qsTr("Only one attachment. Remove other attachment first!"), messageColumn)} + else{ + try{imageAttachmentObject.destroy()}catch(e){print(e)} + imageAttachmentDialog.open()} + } + } + BlueButton{ + id:contactButton + text:qsTr("cc") + visible:(directmessage==0) + onClicked:{ + var contactitems=""; + for (var i=0;i0){ + print(Qt.atob(root.newContacts[root.currentContact].name)) + } + } + } + function showNews(newsToShow){ + if (newsStack.depth>1){newsStack.pop()} + newsBusy.running=false; + var currentTime= new Date(); + var msg = {'currentTime': currentTime, 'model': newsModel,'news':newsToShow}; + newsWorker.sendMessage(msg); + } + + function onFriendsMessages(friend){ + newstab.newstabstatus="Contact"; + Newsjs.newsfromdb(db,root.login.username, function(dbnews){showNews(dbnews)},friend) + } + + function onDirectMessage(friend){ + newstab.newstabstatus="SendMessage" + newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{"reply_to_user": friend,"directmessage":1,"login":root.login}}); + } + + + + StackView{ + id: newsStack + anchors.fill:parent + + initialItem:Rectangle { + y:1 + color: "white" + width:root.width-2*mm + height:root.height-8*mm + + BlueButton{ + id:newstabstatusButton + anchors.top: parent.top + anchors.topMargin: 0.5*mm + text: qsTr(newstab.newstabstatus) + onClicked: {newstabmenu.popup()} + } + + Row{ + spacing: mm + anchors.top: parent.top + anchors.topMargin: 0.5*mm + anchors.right: parent.right + + BlueButton { + id: newMessageButton + width:10*mm + text: qsTr("+") + onClicked: { + var groups=[]; + Helperjs.readData(root.db,"groups",root.login.username,function(groupobject){ + groups=groupobject}); + newstab.newstabstatus="SendMessage" + Helperjs.readData(root.db,"contacts",root.login.username,function(friends){ + newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{"contacts": friends,"login":root.login}}) + },"isFriend",1); + } + } + BlueButton { + id: quitButton + width:10*mm + text: qsTr("Quit") + onClicked: {Service.cleanNews(root.db,function(){Qt.quit() })} + } + BlueButton { + id: update + text: "Update" + onClicked: { + newsBusy.running=true; + newstab.newstabstatus=login.newsViewType; + root.contactLoadType="news"; + var onlynew=true; + Newsjs.getFriendsTimeline(login,db,contactlist,onlynew,newstab,function(ns,nc){ + root.news=ns;root.newContacts=nc;root.currentContact=0; + if (ns.length==0){// update last 20 existing news for changes and likes + onlynew=false; + Newsjs.getFriendsTimeline(login,db,contactlist,onlynew,newstab,function(rns,rnc){ + root.contactLoadType="news"; + root.news=rns;root.newContacts=rnc;root.currentContact=0}) + } + })} + } + + } + Component { id:footerComponent + Rectangle{ + border.color: "#EEEEEE" + border.width: 1 + width:parent.width + height:6*mm + Text{ + font.pixelSize: 1.5*mm + anchors.centerIn: parent + text:qsTr("More") + } + MouseArea{anchors.fill:parent + onClicked:{ + var currentTime= new Date(); + if(newstab.newstabstatus=="Timeline"){ + var lastnews_id=newsModel.get(newsModel.count-1).newsitemobject.created_at; + Newsjs.newsfromdb(root.db,root.login.username, function(news){ + var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true}; + newsWorker.sendMessage(msg); + },false,lastnews_id)} + if(newstab.newstabstatus=="Tree"){ + var lastnews_id=newsModel.get(newsModel.count-1).newsitemobject.created_at; + Newsjs.chatsfromdb(root.db,root.login.username, function(news){ + var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true}; + newsWorker.sendMessage(msg); + },lastnews_id)} + else if(newstab.newstabstatus=="Contact"){ + Newsjs.newsfromdb(root.db,root.login.username, function(news){ + var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true}; + newsWorker.sendMessage(msg); + },newsModel.get(newsModel.count-1).newsitemobject.uid,newsModel.get(newsModel.count-1).newsitemobject.created_at)} + }} + } + } + ListView { + id: newsView + anchors.fill: parent + anchors.topMargin: 8*root.mm + anchors.leftMargin: 3*root.mm; anchors.rightMargin: root.mm + anchors.bottomMargin: 1*root.mm + clip: true + spacing: 0 + footer: footerComponent + model: newsModel + delegate: Newsitem{} + } + + ListModel{id: newsModel} + + WorkerScript { + id: newsWorker + source: "qrc:/js/newsworker.js" + } + + BusyIndicator{ + id: newsBusy + anchors.horizontalCenter: newsView.horizontalCenter + anchors.top:newsView.top + anchors.topMargin: 2*mm + width:10*mm + height: 10*mm + } + + Menu { + id:newstabmenu + MenuItem { + text: qsTr("Timeline") + onTriggered: { + newstab.newstabstatus="Timeline"; + newsModel.clear(); + Newsjs.newsfromdb(root.db,root.login.username, function(dbnews){ + showNews(dbnews) + })} + } + MenuItem { + text: qsTr("Favorites") + onTriggered:{ + newstab.newstabstatus="Favorites"; + root.contactLoadType="favorites"; + newsBusy.running=true; + Newsjs.requestFavorites(root.login,db,root.contactlist,root,function(ns,nc){ + root.news=ns; root.newContacts=nc;root.currentContact=0; + }) + } + } + + MenuItem { + text: qsTr("Tree") + onTriggered:{ + newsModel.clear(); + newstab.newstabstatus="Tree"; + Newsjs.chatsfromdb(db,root.login.username,function(news){showNews(news)}) + } + } + MenuItem { + text: qsTr("Notifications") + onTriggered:{ + newstab.newstabstatus="Notifications"; + newsBusy.running=true; + Newsjs.getNotifications(root.login,db,root,function(news){ + showNews(news)} + )} + } + } + Component.onCompleted: { + root.messageSignal.connect(onFriendsMessages); + root.directmessageSignal.connect(onDirectMessage); + root.newsSignal.connect(showNews); + try{newsModel.clear()} catch(e){} + if(login.newsViewType=="Timeline"){Newsjs.newsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + else{Newsjs.chatsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + } + } + } +} diff --git a/v0.002/Release/source-android/qml/newsqml/Newsitem.qml b/v0.002/Release/source-android/qml/newsqml/Newsitem.qml new file mode 100644 index 0000000..e6a85fa --- /dev/null +++ b/v0.002/Release/source-android/qml/newsqml/Newsitem.qml @@ -0,0 +1,343 @@ +import QtQuick 2.0 +import QtQuick.LocalStorage 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "qrc:/js/news.js" as Newsjs + + +Item { + id: newsitem + width: newsView.width + height:Math.max((itemMessage.height+topFlow.height+friendicaActivities.height+4*mm),profileImage.height+user_name.height+mm) + + property string conversation_id: "" + property string attending: "" + onAttendingChanged: {attendLabel.visible=true; + attendLabel.text= qsTr("attending: ")+ qsTr(attending)} + signal replyto(string parent_id) + function showConversation(){ + conversationsymbol.color="black"; + newsBusy.running=true; + root.contactLoadType="conversation"; + //newstabstatus="Conversation"; + + if(newsitemobject.messagetype==0){ + Newsjs.requestConversation(root.login,db,newsitemobject.status_id,root.contactlist,root,function(ns,nc){ + root.news=ns;root.newContacts=nc;root.currentContact=0; + })} + else{Newsjs.conversationfromdb(root.db,root.login.username,newsitemobject.statusnet_conversation_id, function(newsarray){ + root.news=newsarray;root.newContacts=[];root.currentContact=1; + })}} + + Rectangle{width:newsitem.width; height: 1; anchors.bottom: newsitem.bottom; color:"light grey"} + //MouseArea{ + // anchors.fill: parent; + // enabled: (newstabstatus=="Chats") + // onClicked: {showConversation()} + //} + Rectangle{ + width:newsitem.width + height:newsitem.height-1 + color: (newsitemobject.messagetype==1)?"#ffe6e6" : "white" + + Column { + id: authorcolumn + width: 8*mm + + Image { + id:profileImage + source: (newsitemobject.user.profile_image!="")? "file://"+newsitemobject.user.profile_image : newsitemobject.user.profile_image_url + x:1 + width: 7*mm + height: 7*mm + MouseArea{ + anchors.fill: parent + onPressAndHold: { newsmenu.popup()} + } + onStatusChanged: if (profileImage.status == Image.Error) {source="qrc:/images/defaultcontact.jpg"} + } + Label { + id:user_name + color: "grey" + //height:3.5*mm + width:parent.width + font.pixelSize: 1.5*mm + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + text: Qt.atob(newsitemobject.user.name) + } + } + Column { + id:newscolumn + width: newsitem.width-8*mm + anchors.left: authorcolumn.right + + Flow{ + id:topFlow + spacing: mm + width:parent.width + Label { + id:messageTypeLabel + color: "grey" + text: if (newsitemobject.messagetype==0){qsTr("Source: ")+newsitemobject.source + } else if (newsitemobject.messagetype==1){ qsTr("Direct Message")} else {" Notification"} + font.pixelSize: 1.5*mm + } + Label { + id:createdAtLabel + color: "grey" + //height:3.5*mm + font.pixelSize: 1.5*mm + horizontalAlignment: Label.AlignRight + text: dateDiff + } + Label { + id:replytoLabel + color: "grey" + //height:3.5*mm + font.pixelSize: 1.5*mm + horizontalAlignment: Label.AlignRight + text: try {qsTr("In reply to ")+newsitemobject.reply_user.screen_name + }catch(e){" "} + } + + Label { + id:newscountLabel + visible:((newstabstatus=="Tree")&&(newsitemobject.newscount>1))?true:false + color: "grey" + height:3.5*mm + font.pixelSize: 1.5*mm + font.bold: true + horizontalAlignment: Label.AlignRight + text: try {(newsitemobject.newscount-1)+qsTr(" comments") }catch(e){" "} + MouseArea{ + anchors.fill:parent + onClicked: showConversation() + } + } + } + + Text { + color: "#404040" + linkColor: "light green" + id: itemMessage + textFormat: Text.RichText + text: Qt.atob(newsitemobject.statusnet_html) + width: newsitem.width-8*mm-2 + height: implicitHeight + wrapMode: Text.Wrap + onLinkActivated:{ + Qt.openUrlExternally(link)} + } + + Row{id:friendicaActivities + spacing:mm + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.likeText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.dislikeText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.attendyesText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.attendnoText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.attendmaybeText + } + } + Row { + CheckBox{id:likeCheckbox + height:3*mm + width:8*mm + visible: (newsitemobject.messagetype==0)? true:false + checked:(friendica_activities.self.liked==1)?true:false + style: CheckBoxStyle { + background: Rectangle { + implicitWidth: 7*mm + implicitHeight: 3*mm + color:"white" + } + indicator: + Rectangle{ + implicitWidth: 3*mm + implicitHeight:3*mm + color:control.checked?"yellow":"white" + x: 5*mm + Text{ + font.pixelSize: 2*mm + color:"grey" + text:":-)" + }} + } + onClicked: { + if(likeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"like",newsitemobject.status_id,root);dislikeCheckbox.checked=false; model.friendica_activities.self.liked=0 } + else{Newsjs.like(root.login,root.db,0,"like",newsitemobject.status_id,root); model.friendica_activities.self.liked=1}} + } + CheckBox{id: dislikeCheckbox + height:3*mm + width:8*mm + visible: (newsitemobject.messagetype==0)? true:false + checked: (friendica_activities.self.disliked==1)?true:false + style: CheckBoxStyle { + background: Rectangle { + implicitWidth: 7*mm + implicitHeight:3*mm + color:"white" + } + indicator: + Rectangle{ + implicitWidth: 3*mm + implicitHeight:3*mm + color:control.checked?"yellow":"white" + x:5*mm + Text{ + font.pixelSize: 2*mm + color:"grey" + text:":-(" + }} + } + onClicked: { + if (dislikeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"dislike",newsitemobject.status_id,root);likeCheckbox.checked=false; model.friendica_activities.self.disliked=0} + else {Newsjs.like(root.login,root.db,0,"dislike",newsitemobject.status_id,root); model.friendica_activities.self.disliked=1}} + } + CheckBox { + id:favoritedCheckbox + visible:(newsitemobject.messagetype==0) + style: CheckBoxStyle { + background: Rectangle { + implicitWidth: 6*mm + implicitHeight:3*mm + color:"transparent" + } + indicator: + Rectangle{x:3*mm + width: 3*mm + implicitHeight:3*mm + Text{ + anchors.centerIn: parent + color:control.checked?"black":"grey" + text:"\u2605" + }} + } + checked:(newsitemobject.favorited>0) + onClicked:{ + if(favoritedCheckbox.checkedState==Qt.Checked) + {Newsjs.favorite(login,true,newsitemobject.status_id,root); model.newsitemobject.favorited=1} + else if(favoritedCheckbox.checkedState==Qt.Unchecked) + {Newsjs.favorite(login,false,newsitemobject.status_id,root);model.newsitemobject.favorited=0} + } + } + Rectangle{ + width: 4*mm + height: 3*mm + color:"transparent" + Text{ + id:newsmenusymbol + color: "grey" + anchors.centerIn: parent + font.pixelSize: 2*mm + font.bold: true + text: "\u22EE" + } + MouseArea{ + anchors.fill:parent + onClicked: {newsmenu.popup()}} + + } + + Rectangle{ + width: 4*mm + height: 3*mm + color:"transparent" + //visible:(newsitemobject.in_reply_to_status_id!="")?true:false + Text{ + id:conversationsymbol + color: "grey" + anchors.centerIn: parent + font.pixelSize: 2*mm + text: "\u21C4" + } + MouseArea{ + anchors.fill:parent + onClicked: showConversation() + } + } + Label { + id:attendLabel + visible: false + color: "grey" + height:3.5*mm + font.pixelSize: 1.5*mm + horizontalAlignment: Label.AlignRight + text: (friendica_activities.self.attending)?qsTr("attending: ")+ qsTr(attending):"" + } + } + } + + Menu { + id:newsmenu + MenuItem { + text: qsTr("Reply") + onTriggered: { + var directmessage=0; + if (newsitemobject.messagetype==1){ directmessage=1} + newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{"reply_to_user": newsitemobject.user.screen_name,"parentId":newsitemobject.status_id,"login":root.login,"directmessage":directmessage}}); + } + } + MenuItem { + text: qsTr("DM") + onTriggered: { + root.directmessageSignal(newsitemobject.user.screen_name); + } + } + MenuItem { + text: qsTr("Repost") + onTriggered: { + Newsjs.retweetNews(root.login,db,newsitemobject.status_id,root,function(reply){ + print(reply); + }) + } + } + MenuItem { + text: qsTr("Conversation") + onTriggered: showConversation() + } + + Menu{ + title: qsTr("Attending") + MenuItem{text:qsTr("yes") + onTriggered: {Newsjs.attend(root.login,db,"yes",newsitemobject.status_id,root,function(){ + newsitem.attending="yes"; + attendLabel.visible=true})} + } + + MenuItem{text:qsTr("maybe") + onTriggered: {Newsjs.attend(root.login,db,"maybe",newsitemobject.status_id,root,function(){ + newsitem.attending="maybe"})} + } + + MenuItem{text:qsTr("no") + onTriggered: {Newsjs.attend(root.login,db,"no",newsitemobject.status_id,root,function(){ + newsitem.attending="no"})} + } + } + + MenuItem { + text: qsTr("Delete") + onTriggered: { + Newsjs.deleteNews(root.login,root.db,newsitemobject.status_id,newsitemobject.messagetype,root,function(reply){ + newsModel.remove(index); + }) + } + } +} +} +} + diff --git a/v0.002/Release/source-android/qml/newsqml/PermissionDialog.qml b/v0.002/Release/source-android/qml/newsqml/PermissionDialog.qml new file mode 100644 index 0000000..d7e659a --- /dev/null +++ b/v0.002/Release/source-android/qml/newsqml/PermissionDialog.qml @@ -0,0 +1,191 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.4 +import QtQml.Models 2.1 +import "qrc:/js/service.js" as Service +import "qrc:/js/helper.js" as Helperjs +import "qrc:/qml/genericqml" + +Rectangle{ + id:permissionDialog + x: mm + width: messageColumn.width-5*mm + height:root.height/3 + function updatePerms(){ + for (var i=0;i-1){contactstatus="positive";print(contacts[name].cid+" pos")} + else if (contact_deny.indexOf(contacts[name].cid)>-1){contactstatus="negative"} + contactModel.append({"contact":contacts[name],"contactstatus":contactstatus}) + }},"isFriend",1); + + Helperjs.readData(db,"groups",login.username,function(owngroups){ + for (var number in owngroups){ + var groupstatus= "neutral"; + if (group_allow.indexOf(owngroups[number].gid)>-1){groupstatus="positive"} + else if (group_deny.indexOf(owngroups[number].gid)>-1){groupstatus="negative"} + groupModel.append({"group":owngroups[number],"groupstatus":groupstatus}) + }}); + } +} diff --git a/v0.002/Release/source-android/qml/photoqml/PhotoComponent.qml b/v0.002/Release/source-android/qml/photoqml/PhotoComponent.qml new file mode 100644 index 0000000..9884d71 --- /dev/null +++ b/v0.002/Release/source-android/qml/photoqml/PhotoComponent.qml @@ -0,0 +1,99 @@ +import QtQuick 2.0 +//import QtQuick.LocalStorage 2.0 +import QtQuick.Controls 1.2 + +Package { + Item { id: stackItem; Package.name: 'stack'; z: stackItem.PathView.z;width:16.5*mm;height:16.5*mm} + Item { id: listItem; Package.name: 'list'; width: root.width-1*mm; height: root.height-8*mm; } + Item { id: gridItem; Package.name: 'grid';} + + Item { + id: photoWrapper + width: 16.5*mm; height: 16.5*mm + z: stackItem.PathView.z + property string hqphotolink: photoLink + + Rectangle { + id: placeHolder + color: 'lightblue'; antialiasing: true + anchors.fill:parent + } + + BusyIndicator { anchors.centerIn: parent; running: realImage.status != Image.Ready } + Image { + id: realImage; + // property string hqphotolink: photoLink + width: photoWrapper.width; height: photoWrapper.height + antialiasing: true; + asynchronous: true + cache: false + fillMode: Image.PreserveAspectFit; + source: imageLocation + // onStatusChanged: if (realImage.status == Image.Ready) print(realImage.paintedHeight+"x"+realImage.paintedWidth) + } + Rectangle{ + id:phototextRectangle + color:"black" + z:3 + opacity: 0.5 + width:phototext.contentWidth + height: phototext.contentHeight + anchors.bottom: photoWrapper.bottom + } + Text { + id:phototext + z:4 + text: photoDescription.trim() + width:15*mm + anchors.bottom: photoWrapper.bottom + color: "white" + font.pixelSize: 2*mm + wrapMode:Text.Wrap + } + MouseArea { + width: realImage.paintedWidth; height: realImage.paintedHeight; anchors.centerIn: realImage + onClicked: { + if (albumWrapper.state == 'inGrid') { + gridItem.GridView.view.currentIndex = index; + //print("photoLink"+realImage.photoLink) + albumWrapper.state = 'fullscreen' + } else { + gridItem.GridView.view.currentIndex = index; + albumWrapper.state = 'inGrid' + } + } + } + + states: [ + State { + name: 'stacked'; when: albumWrapper.state == '' + ParentChange { target: photoWrapper; parent: stackItem; }//x: 1*mm; y: 1*mm } + PropertyChanges { target: photoWrapper; opacity: stackItem.PathView.onPath ? 1.0 : 0.0 } + PropertyChanges { target: phototext; opacity: 0.0 } + PropertyChanges { target: phototextRectangle; opacity: 0.0 } + }, + State { + name: 'inGrid'; when: albumWrapper.state == 'inGrid' + ParentChange { target: photoWrapper; parent: gridItem; x: 1*mm; y: 1*mm;} + PropertyChanges { target: phototext; opacity: 1.0 } + PropertyChanges { target: phototextRectangle; opacity: 0.5 } + PropertyChanges { target: placeHolder; opacity: 1.0 } + }, + State { + name: 'fullscreen'; when: albumWrapper.state == 'fullscreen' + ParentChange { + target: photoWrapper; parent: listItem; x: 1; y: 1; + width: root.width-mm; height: root.height-8*mm + } + PropertyChanges { target: placeHolder; opacity: 0.0 } + PropertyChanges { target: realImage; source: photoWrapper.hqphotolink} + PropertyChanges { target: phototext; anchors.bottom: realImage.bottom} + PropertyChanges { target: phototext; width:realImage.width } + PropertyChanges { target: phototextRectangle; anchors.bottom: realImage.bottom } + PropertyChanges { target: realImage; width: Math.min(listItem.width,sourceSize.width);height: Math.min(listItem.height,sourceSize.height) } + } + ] + + } +} + diff --git a/v0.002/Release/source-android/qml/photoqml/PhotoPlaceholder.qml b/v0.002/Release/source-android/qml/photoqml/PhotoPlaceholder.qml new file mode 100644 index 0000000..162529c --- /dev/null +++ b/v0.002/Release/source-android/qml/photoqml/PhotoPlaceholder.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 +import "qrc:/js/helper.js" as Helperjs + +Image { +id:photoPlaceholder +property string imageName:"x.jpg" +property string downloadtype:"" +fillMode: Image.PreserveAspectFit +onStatusChanged: { + if (photoPlaceholder.status == Image.Ready) { + //print("Source: "+source+"Photo width"+width+" height"+height+" Ratio "+ fillMode); + var saveprocess=photoPlaceholder.grabToImage(function(result){ + var saveresult=result.saveToFile(imageName.toString()); + if (saveresult){ + if ((downloadtype=="picture")&&(newImages.length>0)){ + photoPlaceholder.destroy(); + currentImageNo=currentImageNo+1 + } +S }}); +}}} + diff --git a/v0.002/Release/source-android/qml/photoqml/PhotoTab.qml b/v0.002/Release/source-android/qml/photoqml/PhotoTab.qml new file mode 100644 index 0000000..edada91 --- /dev/null +++ b/v0.002/Release/source-android/qml/photoqml/PhotoTab.qml @@ -0,0 +1,181 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.4 +import QtQml.Models 2.1 +import "qrc:/js/service.js" as Service +import "qrc:/js/helper.js" as Helperjs +import "qrc:/qml/photoqml" +import "qrc:/qml/genericqml" + +Rectangle { + id:fotorectangle + property string phototabstatus:"Images" + onPhototabstatusChanged:{phototabstatusButton.text=qsTr(phototabstatus)} + + y:1 + width:root.width-mm + height:root.height-5*mm + color: '#fff' + property var newimages:[] + property int currentimageno: 0 +//onLoginChanged:{var msg = {'model': photogroupModel,'albums':[],'firstalbum':0,'foreignPicture':false}; +// photoWorker.sendMessage(msg); +//} + onNewimagesChanged:{ + if(newimages.length>0){ + Helperjs.readField("album",root.db,"imageData",root.login.username,function(albums){ + //print("albums"+JSON.stringify(albums)+JSON.stringify(newimages[currentimageno])); + for (var i=0;i + + qml/friendiqa.qml + qml/newsqml/NewsTab.qml + qml/newsqml/Newsitem.qml + qml/newsqml/MessageSend.qml + qml/newsqml/PermissionDialog.qml + qml/contactqml/FriendsTab.qml + qml/contactqml/GroupComponent.qml + qml/contactqml/ContactComponent.qml + qml/contactqml/ContactDetailsComponent.qml + qml/genericqml/BlueButton.qml + qml/photoqml/PhotoComponent.qml + qml/photoqml/PhotogroupComponent.qml + qml/photoqml/PhotoTab.qml + qml/configqml/InfoBox.qml + qml/configqml/ConfigTab.qml + js/layout.js + js/photoworker.js + js/service.js + js/news.js + js/newsworker.js + js/helper.js + images/defaultcontact.jpg + qml/newsqml/Conversation.qml + + diff --git a/v0.002/Release/source-linux/common/filesystem.cpp b/v0.002/Release/source-linux/common/filesystem.cpp new file mode 100644 index 0000000..758f7c8 --- /dev/null +++ b/v0.002/Release/source-linux/common/filesystem.cpp @@ -0,0 +1,49 @@ +#include "filesystem.h" + +FILESYSTEM *FILESYSTEM::instance() +{ + static FILESYSTEM filesystem; + return &filesystem; +} + +FILESYSTEM::FILESYSTEM(QObject *parent) : QObject(parent){} + +void FILESYSTEM::setDirectory(QString Directory) +{ + if (Directory!=m_Directory) { + m_Directory = Directory; + emit directoryChanged(); + } +} + +QString FILESYSTEM::Directory() const +{ + return m_Directory; +} + +void FILESYSTEM::makeDir(QString name) +{ + QDir dir(m_Directory); + if (dir.mkdir(name)){ + emit success(name); + } + else {emit error(name,1);} +} + +void FILESYSTEM::rmDir() +{ + QDir dir(m_Directory); + if (dir.removeRecursively()){ + emit success(m_Directory); + } + else {emit error(m_Directory,1);} +} + +void FILESYSTEM::rmFile(QString name) +{ + QDir dir(m_Directory); + if(dir.remove(name)){ + emit success(name); + } + else {emit error(name,1);} +} diff --git a/v0.002/Release/source-linux/common/filesystem.h b/v0.002/Release/source-linux/common/filesystem.h new file mode 100644 index 0000000..53d2caf --- /dev/null +++ b/v0.002/Release/source-linux/common/filesystem.h @@ -0,0 +1,33 @@ +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + +#include +#include + +class FILESYSTEM : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString Directory READ Directory WRITE setDirectory NOTIFY directoryChanged) + +public: + static FILESYSTEM *instance(); + explicit FILESYSTEM(QObject *parent = 0); + void setDirectory(QString Directory); + QString Directory() const; + +signals: + void directoryChanged(); + void success(QString data); + void error(QString data, int code); + +public slots: + //void setDirectory(QString Directory); + void makeDir(QString name); + void rmDir(); + void rmFile(QString name); + +private: + QString m_Directory; +}; + +#endif // FILSYSTEM_H diff --git a/v0.002/Release/source-linux/common/friendiqa.cpp b/v0.002/Release/source-linux/common/friendiqa.cpp new file mode 100644 index 0000000..de055f0 --- /dev/null +++ b/v0.002/Release/source-linux/common/friendiqa.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include "xhr.h" +#include "filesystem.h" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + QQuickView view; + + + XHR* xhr = XHR::instance(); + view.rootContext()->setContextProperty("xhr", xhr); + FILESYSTEM* filesystem = FILESYSTEM::instance(); + view.rootContext()->setContextProperty("filesystem", filesystem); + view.setSource(QUrl("qrc:/qml/friendiqa.qml")); + view.show(); + view.connect(view.rootContext()->engine(), SIGNAL(quit()), &app, SLOT(quit())); + return app.exec(); + +} + diff --git a/v0.002/Release/source-linux/common/uploadableimage.cpp b/v0.002/Release/source-linux/common/uploadableimage.cpp new file mode 100644 index 0000000..81bea1a --- /dev/null +++ b/v0.002/Release/source-linux/common/uploadableimage.cpp @@ -0,0 +1,90 @@ +#include "uploadableimage.h" + + +#include +#include +#include +#include + +void UploadableImage::setSource(const QString &a) { + if (a != m_source) { + m_source = a; + //m_base64 = ""; + m_mimetype = ""; + m_filename = ""; + + qDebug() << "UploadableImage::setSource : " << m_source; + + if (m_source=="") { + emit sourceChanged(); + //emit base64Changed(); + emit mimetypeChanged(); + emit filenameChanged(); + return; + } + + QImage fullimage = QImage(QUrl(m_source).toLocalFile()); + if (fullimage.width() > 800 || fullimage.height() > 800) { + if (fullimage.width() > fullimage.height()) { + m_image = fullimage.scaledToWidth(800); + } else { + m_image = fullimage.scaledToHeight(800); + } + } else { + m_image = fullimage; + } + qDebug() << "UploadableImage::setSource : " << m_image.width() << "x" << m_image.height(); + emit sourceChanged(); + + QFileInfo fi(m_source); + + m_filename = fi.fileName(); + emit filenameChanged(); + + QString filetype = fi.suffix().toUpper(); + if (filetype!="PNG" && filetype!="JPG") { + filetype = "JPG"; + } + qDebug() << "UploadableImage::setSource : " << "Saving as " << filetype; + + m_mimetype = "image/"+filetype.toLower(); + emit mimetypeChanged(); + + /* + QByteArray byteArray; + QBuffer buffer(&byteArray); + m_image.save(&buffer, filetype.toLatin1().constData()); + QString b64 = QString::fromLatin1(byteArray.toBase64().data()); + + for(int k=0; k +#include +#include + +class UploadableImage : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) + //Q_PROPERTY(QString base64 READ base64 NOTIFY base64Changed) + Q_PROPERTY(QString filename READ filename NOTIFY filenameChanged) + Q_PROPERTY(QString mimetype READ mimetype NOTIFY mimetypeChanged) + Q_PROPERTY(QByteArray bytes READ bytes) + +public: + void setSource(const QString &a); + QString source() const; + + //QString base64() const; + QString filename() const; + QString mimetype() const; + + QByteArray bytes(); +signals: + void sourceChanged(); + //void base64Changed(); + void filenameChanged(); + void mimetypeChanged(); + +private: + QString m_source; + QImage m_image; + //QString m_base64; + QString m_filename; + QString m_mimetype; +}; + +#endif // UPLOADABLEIMAGE_H diff --git a/v0.002/Release/source-linux/common/xhr.cpp b/v0.002/Release/source-linux/common/xhr.cpp new file mode 100644 index 0000000..9661273 --- /dev/null +++ b/v0.002/Release/source-linux/common/xhr.cpp @@ -0,0 +1,235 @@ +#include "xhr.h" + +#include +#include +#include + +#include "uploadableimage.h" + +XHR *XHR::instance() +{ + static XHR xhr; + return &xhr; +} + +XHR::XHR(QObject *parent) : QObject(parent) +{ + request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); +} + +void XHR::setUrl(QString url) +{ + if (url!=m_url) { + m_url = url; + emit urlChanged(); + } +} + +void XHR::setLogin(QString login) +{ + if (login!=m_login) { + m_login = login; + emit loginChanged(); + } +} + +void XHR::setFilename(QString filename) +{ + if (filename!=m_filename) { + m_filename = filename; + emit filenameChanged(); + } +} + +void XHR::setDownloadtype(QString downloadtype) +{ + if (downloadtype!=m_downloadtype) { + m_downloadtype = downloadtype; + emit downloadtypeChanged(); + } +} + +QString XHR::url() const +{ + return m_url; +} + +QString XHR::login() const +{ + return m_login; +} + +QString XHR::filename() const +{ + return m_filename; +} + +QString XHR::downloadtype() const +{ + return m_downloadtype; +} + +void XHR::setParam(QString name, QString value) +{ + params.insert(name, value); +} + +void XHR::setImageFileParam(QString name, QString url) +{ + files.insert(name, url); +} + +void XHR::clearParams() +{ + files.clear(); + params.clear(); +} + +void XHR::download() +{ + QUrl requrl(m_url); + //qDebug()<< "replyerror"<error(); +// qDebug() << "start download of " << requrl; + request.setUrl(requrl); + reply = manager.get(request); +// qDebug() << "reply " << reply->header(QNetworkRequest::LocationHeader)<header(QNetworkRequest::LastModifiedHeader); + // qDebug() << "request " << request.url(); + // reply->ignoreSslErrors(); + connect(reply, &QNetworkReply::readyRead,this, &XHR::onReadyRead); + //connect(reply,SIGNAL(downloadProgress(qint64,qint64)), this,SLOT(updateDownloadProgress(qint64,qint64))); + connect(reply, &QNetworkReply::finished,this, &XHR::onRequestFinished); + //connect(reply, SIGNAL(finished()),this, SLOT(onRequestFinished())); + connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError))); +} + +void XHR::get() +{ + QUrlQuery query; + + QHashIterator i(params); + while(i.hasNext()) { + i.next(); + query.addQueryItem(i.key(), i.value()); + } + + QUrl requrl(m_url); + requrl.setQuery(query); + + QByteArray loginData = m_login.toLocal8Bit().toBase64(); + QString headerData = "Basic " + loginData; + request.setRawHeader("Authorization", headerData.toLocal8Bit()); + + + request.setUrl(requrl); + reply = manager.get(request); + + connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError))); + connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead); + connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError); +} + +void XHR::post() +{ + qDebug() << "start post to " << m_url; + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHashIterator iparams(params); + while(iparams.hasNext()) { + iparams.next(); + qDebug() << "\t add param " << iparams.key() << " : " << iparams.value(); + QHttpPart textPart; + textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + iparams.key() + "\"")); + + + textPart.setBody(iparams.value().toUtf8()); + multiPart->append(textPart); + } + + UploadableImage uimg; + QHashIterator ifiles(files); + while(ifiles.hasNext()) { + ifiles.next(); + + uimg.setSource(ifiles.value()); + qDebug() << "\t image: " << uimg.mimetype() << ", " << ifiles.key(); + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(uimg.mimetype())); + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + ifiles.key() + "\"; filename=\""+uimg.filename()+"\"")); + imagePart.setBody(uimg.bytes()); + multiPart->append(imagePart); + } + + QByteArray loginData = m_login.toLocal8Bit().toBase64(); + QString headerData = "Basic " + loginData; + request.setRawHeader(QByteArray("Authorization"), headerData.toLocal8Bit()); + + request.setUrl(m_url); + reply = manager.post(request, multiPart); + qDebug() << "\t request sent"; + connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError))); + connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead); + connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError); + qDebug() << "\t reply signals connected"; +} + +void XHR::onReplyError(QNetworkReply::NetworkError code) +{ + qDebug() << code; + emit this->error( bufferToString(), (int) code); + reply->deleteLater(); +} + +void XHR::onReplySuccess() +{ + qDebug() << "!"; + emit this->success( bufferToString() ); + reply->deleteLater(); +} + +void XHR::onRequestFinished() +{ + // Save the file here + //qDebug() << "buffer downloaded "<error(m_downloadtype,1);} + else {QFile file(m_filename); + file.open(QIODevice::WriteOnly); + file.write(buffer); + buffer.clear(); + file.close(); + //qDebug() << m_url << "File downloaded "<downloaded(m_downloadtype); + //reply->deleteLater(); + } +} + +void XHR::onReadyRead() +{ + qDebug() << "."; + buffer += reply->readAll(); +} + +//void XHR::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes) +//{ +// qDebug() << "Bytes: " << bytesRead<<" / "< &errors) +{ + qDebug() << "XHR::onSSLError :" ; + QListIterator ierrs(errors); + while(ierrs.hasNext()) { + qDebug() << "\t" << ierrs.next().errorString(); + } +} + +QString XHR::bufferToString() +{ + return QTextCodec::codecForName("utf-8")->toUnicode(buffer); +} + + diff --git a/v0.002/Release/source-linux/common/xhr.h b/v0.002/Release/source-linux/common/xhr.h new file mode 100644 index 0000000..6e93c6c --- /dev/null +++ b/v0.002/Release/source-linux/common/xhr.h @@ -0,0 +1,73 @@ +#ifndef XHR_H +#define XHR_H + +#include +#include +#include +#include + +class XHR : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QString login READ login WRITE setLogin NOTIFY loginChanged) + Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged) + Q_PROPERTY(QString downloadtype READ downloadtype WRITE setDownloadtype NOTIFY downloadtypeChanged) + + +public: + static XHR *instance(); + + explicit XHR(QObject *parent = 0); + + QString url() const; + QString login() const; + QString filename() const; + QString downloadtype() const; + +signals: + void urlChanged(); + void loginChanged(); + void filenameChanged(); + void downloadtypeChanged(); + void downloaded(QString data); + void success(QString data); + void error(QString data, int code); + +public slots: + void setUrl(QString url); + void setLogin(QString login); + void setDownloadtype(QString downloadtype); + void setFilename(QString filename); + void setParam(QString name, QString value); + void setImageFileParam(QString name, QString url); + void clearParams(); + void post(); + void get(); + void download(); + +private slots: + void onReplyError(QNetworkReply::NetworkError code); + void onReplySuccess(); + void onRequestFinished(); + void onReadyRead(); + void onSSLError(const QList &errors); + //void updateDownloadProgress(qint64 bytesRead, qint64 totalBytes); + +private: + QByteArray buffer; + QString m_url; + QString m_login; + QString m_filename; + QString m_downloadtype; + QHash params; + QHash files; + + QNetworkAccessManager manager; + QNetworkRequest request; + QNetworkReply *reply; + + QString bufferToString(); +}; + +#endif // XHR_H diff --git a/v0.002/Release/source-linux/friendiqa.pro b/v0.002/Release/source-linux/friendiqa.pro new file mode 100644 index 0000000..31c1b2d --- /dev/null +++ b/v0.002/Release/source-linux/friendiqa.pro @@ -0,0 +1,44 @@ +# NOTICE: +# +# Application name defined in TARGET has a corresponding QML filename. +# If name defined in TARGET is changed, the following needs to be done +# to match new name: +# - corresponding QML filename must be changed +# - desktop icon filename must be changed +# - desktop filename must be changed +# - icon definition filename in desktop file must be changed +# - translation filenames have to be changed + +# The name of your application +TARGET = friendiqa +CONFIG += debug +QT += qml quick gui widgets + +SOURCES += common/friendiqa.cpp \ + common/uploadableimage.cpp \ + common/xhr.cpp \ + common/filesystem.cpp + +RESOURCES = application.qrc + +OTHER_FILES += qml/friendiqa.qml \ + translations/*.ts \ + qml/*.qml + qml/newsqml/*.qml + qml/contactqml/*.qml + qml/photoqml/*.qml + qml/configqml/*.qml + js/*.js + +# German translation is enabled as an example. If you aren't +# planning to localize your app, remember to comment out the +# following TRANSLATIONS line. And also do not forget to +# modify the localized app name in the the .desktop file. +TRANSLATIONS += translations/friendiqa-de.ts + +HEADERS += \ + common/uploadableimage.h \ + common/xhr.h \ + common/filesystem.h + + diff --git a/v0.002/Release/source-linux/images/defaultcontact.jpg b/v0.002/Release/source-linux/images/defaultcontact.jpg new file mode 100644 index 0000000..bb7bce2 Binary files /dev/null and b/v0.002/Release/source-linux/images/defaultcontact.jpg differ diff --git a/v0.002/Release/source-linux/js/friendworker.js b/v0.002/Release/source-linux/js/friendworker.js new file mode 100644 index 0000000..6b6c5be --- /dev/null +++ b/v0.002/Release/source-linux/js/friendworker.js @@ -0,0 +1,12 @@ +WorkerScript.onMessage = function(msg) { + msg.model.clear(); + for (var j=0;j 0 ) { + for (var i in list) { if (list[i][prop] === val) { + return i; + } + } + } return -1; +} +function cleanArray(array) { +var arraystring=JSON.stringify(array); + arraystring=arraystring.replace(/[\[\]]/g , ''); +return arraystring; +} diff --git a/v0.002/Release/source-linux/js/layout.js b/v0.002/Release/source-linux/js/layout.js new file mode 100644 index 0000000..23815fd --- /dev/null +++ b/v0.002/Release/source-linux/js/layout.js @@ -0,0 +1,44 @@ +function showFriends(db) { + Service.readActiveConfig(db,function(login){ + Service.requestFriends(login.url,login.user,login.password,displayFriends); + }); +} +function displayFriends(obj){ + for (var i=0; i= c.x) + f.contentX = c.x; + else if (f.contentX+f.width <= c.x+c.width) + f.contentX = c.x+c.width-f.width; + if (f.contentY >= c.y) + f.contentY = c.y; + else if (f.contentY+f.height <= c.y+c.height) + f.contentY = c.y+c.height-f.height; +} + +function createObject(objectQml,qmlParameters,parentitem,callback) { + var component = Qt.createComponent(objectQml); + if (component.status === Component.Ready || component.status === Component.Error) + finishCreation(component,qmlParameters,parentitem,callback); + else + component.statusChanged.connect(finishCreation(qmlParameters)); +} + +function finishCreation(component,qmlParameters,parentitem,callback) { + if (component.status === Component.Ready) { + var createdObject = component.createObject(parentitem, qmlParameters); + if (createdObject === null) + print("Error creating image"); } + else if (component.status === Component.Error) + print("Error loading component:"+component.errorString()); + else {print("created")} + //callback(createdObject); +} + diff --git a/v0.002/Release/source-linux/js/news.js b/v0.002/Release/source-linux/js/news.js new file mode 100644 index 0000000..a23fb58 --- /dev/null +++ b/v0.002/Release/source-linux/js/news.js @@ -0,0 +1,399 @@ +.pragma library +.import QtQuick.LocalStorage 2.0 as Sql +.import "qrc:/js/helper.js" as Helperjs + +function requestFriends(login,database,rootwindow,callback){ +// return array of friends + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + db.transaction( function(tx) { + var result = tx.executeSql('UPDATE contacts SET isFriend=0 where username="'+login.username+'"')}); // clean old friends + Helperjs.friendicaRequest(login,"/api/statuses/friends", rootwindow,function (obj){ + var friends=JSON.parse(obj); + for (var i=0;i'+lastDate); + var result2 = tx.executeSql('SELECT id from contacts WHERE username="'+login.username+'" AND isFriend=0 AND imageAge > '+lastDate); + for (var j=0;j0){ + for (var j=0;j0){ + for (var k=0;j1){ + var helpernews=newsrs.rows.item(0); + helpernews.newscount=newsrs.rows.length; + helpernews=fetchUsersForNews(database,user,helpernews) + newsArray.push(helpernews); + //} +} + callback(newsArray); + })} + +function inArray(list, prop, val) { + if (list.length > 0 ) { + for (var i in list) {if (list[i][prop] == val) { + return true; + } + } + } return false; +} + +function cleanDate(date){ +var cleanedDate= date.slice(0,3)+", "+date.slice(8,11)+date.slice(4,7)+date.slice(25,30)+date.slice(10,25); +return cleanedDate +} diff --git a/v0.002/Release/source-linux/js/newsworker.js b/v0.002/Release/source-linux/js/newsworker.js new file mode 100644 index 0000000..9ae91b6 --- /dev/null +++ b/v0.002/Release/source-linux/js/newsworker.js @@ -0,0 +1,63 @@ +WorkerScript.onMessage = function(msg) { + if(msg.appendnews!==true){ msg.model.clear()}; + + for (var j=0;j0){ + if (newsitemobject.like.length==1){likeText= Qt.atob(newsitemobject.like[0].name)+" "+ qsTr("likes this.")} + else {likeText= newsitemobject.like.length+" "+ qsTr("like this.")} + } + if (newsitemobject.dislike.length>0){ + if (newsitemobject.dislike.length==1){dislikeText= QT.atob(newsitemobject.dislike[0].name)+" "+ qsTr("doesn't like this.")} + else {dislikeText= newsitemobject.dislike.length+" "+ qsTr("don't like this.")} + } + if (newsitemobject.attendyes.length>0){ + if (newsitemobject.attendyes.length==1){attendyesText= Qt.atob(newsitemobject.attendyes[0].name)+" "+ qsTr("will attend.")} + else {attendyesText= newsitemobject.attendyes.length+" "+ qsTr("persons will attend.")} + } + if (newsitemobject.attendno.length>0){ + if (newsitemobject.attendno.length==1){attendnoText= Qt.atob(newsitemobject.attendno[0].name)+" "+ qsTr("will not attend.")} + else {attendnoText= newsitemobject.attendno.length+" "+ qsTr("persons will not attend.")} + } + if (newsitemobject.attendmaybe.length>0){ + if (newsitemobject.attendmaybe.length==1){attendmaybeText= Qt.atob(newsitemobject.attendmaybe[0].name)+" "+ qsTr("may attend.")} + else {attendmaybeText= newsitemobject.attendmaybe.length+" "+ qsTr("persons may attend.")} + } + var friendica_activities_self=JSON.parse(newsitemobject.friendica_activities_self); + if (friendica_activities_self.indexOf(3)!=-1){self.attending=qsTr("yes")} + if (friendica_activities_self.indexOf(4)!=-1){self.attending=qsTr("no")} + if (friendica_activities_self.indexOf(5)!=-1){self.attending=qsTr("maybe")} + if (friendica_activities_self.indexOf(1)!=-1){self.liked=1} + if (friendica_activities_self.indexOf(2)!=-1){self.disliked=1} + } + var friendica_activities={likeText:likeText,dislikeText:dislikeText,attendyesText:attendyesText,attendnoText:attendnoText,attendmaybeText:attendmaybeText,self:self} + //print(JSON.stringify(friendica_activities) ) ; + var seconds=(msg.currentTime-newsitemobject.created_at)/1000; + var timestring=""; + if (seconds<60) {timestring=seconds+" "+qsTr("seconds") +" "+qsTr("ago");} + else if (seconds<90){timestring=Math.round(seconds/60)+" "+qsTr("minute") +" "+qsTr("ago");} + else if (seconds<3600){timestring=Math.round(seconds/60)+" "+qsTr("minutes") +" "+qsTr("ago");} + else if (seconds<5400){timestring=Math.round(seconds/3600)+" "+qsTr("hour") +" "+qsTr("ago");} + else if (seconds<86400){timestring=Math.round(seconds/3600)+" "+qsTr("hours") +" "+qsTr("ago");} + else if (seconds<129600){timestring=Math.round(seconds/86400)+" "+qsTr("day") +" "+qsTr("ago");} + else if (seconds<3888000){timestring=Math.round(seconds/86400)+" "+qsTr("days") +" "+qsTr("ago");} + else if (seconds<5832000){timestring=Math.round(seconds/3888000)+" "+qsTr("month") +" "+qsTr("ago");} + else if (seconds<69984000){timestring=Math.round(seconds/3888000)+" "+qsTr("months") +" "+qsTr("ago");} + else {timestring=Math.round(seconds/69984000)+" "+qsTr("years") +" "+qsTr("ago");} + var data=({"newsitemobject": newsitemobject,"dateDiff":timestring,"friendica_activities":friendica_activities})} + //print("News:"+j+msg.news.length+JSON.stringify(data)); + msg.model.append(data);} + if (j==msg.news.length){ + msg.model.sync() + }; +} diff --git a/v0.002/Release/source-linux/js/photoworker.js b/v0.002/Release/source-linux/js/photoworker.js new file mode 100644 index 0000000..36646c7 --- /dev/null +++ b/v0.002/Release/source-linux/js/photoworker.js @@ -0,0 +1,15 @@ +WorkerScript.onMessage = function(msg) { + if (msg.firstalbum==0){msg.model.clear();} + var limit=0; if (msg.albums.length-msg.firstalbum<20){limit=msg.albums.length} else{limit=msg.firstalbum+20} + for (var j=msg.firstalbum;j0){ + for(var i=0;i< AllStoredImages.length;i++){ + var position=Helperjs.inArray(obj,"resourceID",AllStoredImages[i]); + if (position>-1){obj.splice(position,1)} + //obj.splice(obj.indexOf(AllStoredImages[i]),1);// list of objects instead of list!!! + }} + callback(obj) + }) +})} + +function dataRequest(login,photoID,database,rootwindow) { + // check if image exist and call download function + Helperjs.friendicaRequest(login,"/api/friendica/photo?photo_id="+photoID, rootwindow, function (image){ + try{ if(image==""){currentimageno=currentimageno+1}else{ + var obj = JSON.parse(image); + var helpfilename=obj.filename.substring(0,obj.filename.lastIndexOf(".")); + var filesuffix=""; + if (obj.type=="image/jpeg"){filesuffix=".jpg"} + else if (obj.type=="image/png"){filesuffix=".png"} + else {filesuffix=""} + + if (helpfilename==""){// check if file has any filename + obj.filename=obj["id"]+filesuffix; + } + else{obj.filename=helpfilename+filesuffix} + + var link=""; + if(obj["link"][0]){link=obj["link"][0]} else{link=obj["link"]["4"]} + xhr.setUrl(Qt.resolvedUrl(link)); + xhr.setFilename(login.imagestore+'albums/'+obj.album+"/"+obj["filename"]); + xhr.setDownloadtype("picture"); + xhr.download(); + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + db.transaction( function(tx) { + var result = tx.executeSql('SELECT * from imageData where id = "'+obj["id"]+'"'); + if(result.rows.length === 1) {// use update + result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+obj.id+'", created="'+obj.created+'", edited="'+obj.edited+'", profile="'+obj.profile+'", link="'+obj["link"]["4"]+'", filename="'+obj.filename+'",title="'+obj.title+'", desc="'+obj.desc+'", type="'+obj.type+'", width="'+obj.width+'", height="'+obj.height+'", album="'+obj.album+'", location="file://'+login.imagestore+'albums/'+obj.album+'/" where id="'+obj["id"]+'"'); + } else {// use insert print('... does not exists, create it') + result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,obj.id,obj.created,obj.edited, obj.title, obj.desc, obj.album, obj.filename, obj.type, obj.height, obj.width,obj. profile,obj["link"]["4"],'file://'+login.imagestore+'albums/'+obj.album+"/"]); + } + })}} + catch (e){print("Data retrieval failure! "+ e+obj);} +})} + + +function deleteImageData(database,user,field,selection,callback) { // does nothing useful at the moment + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + //print(' delete Image Data() for ' + field +"="+selection) + db.transaction( function(tx) { + result = tx.executeSql('UPDATE imageData SET data="" where '+ field +'="'+selection+'"'); + callback(result)}) +} + +function requestFriendsAlbumPictures(login,friend,rootwindow,callback){ +// screenscraping of albums page of contact without user and password + Helperjs.friendicaWebRequest(friend.url.replace("profile","photos"),rootwindow,function(photohtml){ + //print(photohtml); + var photoarray=[]; + var arr = photohtml.split("sidebar-photos-albums-li"); + for (var i=2;i')-1); + var album={'link':albumlink,'name':albumname} + photoarray.push(album); + } + callback(photoarray) + }) +} + +function requestFriendsPictures(link,rootwindow,callback){ +// screenscraping of pictures page for given album + Helperjs.friendicaWebRequest(link,rootwindow,function(photohtml){ + var photoarray=[]; + var basehtml=photohtml.substring(photohtml.indexOf('',photohtml.indexOf('-1){ //theme 1 + var arr = photohtml.split("photo-album-image-wrapper-end");} + +// other themes + if (photohtml.indexOf("photo-album-wrapper")>-1){ //theme 2 + var photoarea=photohtml.substring(photohtml.indexOf("photo-album-wrapper"),photohtml.indexOf("photo-album-end")) + var arr = photoarea.split("");} + + for (var i=0;i0){ + for(var i = 0; i < rs.rows.length; i++) { + rsArray.push(rs.rows.item(i)) + } + var rsObject={server:rsArray[0].server,username:rsArray[0].username, password:rsArray[0].password,imagestore:rsArray[0].imagestore,maxnews:rsArray[0].maxnews,isActive:rsArray[0].isActive,timerInterval:rsArray[0].timerInterval, newsViewType:rsArray[0].newsViewType,permissions:JSON.parse(rsArray[0].permissions),maxContactAge:rsArray[0].maxContactAge,APIVersion:rsArray[0].APIVersion}; + } else {var rsObject=""} + callback(rsObject)}} + ) +} + +function readActiveConfig(database){ + var obj; + readConfig(database,function(config){obj=config},"isActive", 0); + return obj; +} + +function deleteConfig(database,userobj,callback) { // delete user data from DB + if (userobj){var where = " WHERE username='"+ userobj.username+"' and server='"+userobj.server+"'";} else { return "no user selected!";} + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + if(!db) { return; } + db.transaction( function(tx) { + var rs = tx.executeSql('delete from config'+where); + callback(rs); + }) +} + +function cleanNews(database,callback){ + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + db.transaction( function(tx) { + var maxnewsrs = tx.executeSql("SELECT DISTINCT maxnews FROM config"); + var maxnews=maxnewsrs.rows.item(0).maxnews; + var newscountrs = tx.executeSql('SELECT COUNT(*) from news'); + var newscount = newscountrs.rows.item(0)["COUNT(*)"]; + if (newscount>maxnews){var lastvalidtimers= tx.executeSql('select created_at from news ORDER BY created_at DESC LIMIT ' +(newscount-maxnews)); + var lastvalidtime=lastvalidtimers.rows.item(maxnews).created_at; + var deleters = tx.executeSql('DELETE from news WHERE created_at<='+lastvalidtime)} + callback() + }) + } + +function processNews(callback){ + Newsjs.getCurrentContacts(login,db,function(contacts){ + contactlist=contacts}); + + if (contactLoadType=="news"){ + if(root.news.length==0){} + else{// show news + Newsjs.storeNews(login,db,news,root,function(dbnews){ + root.newsSignal(dbnews); + newstab.newstabstatus=login.newsViewType + })} + } + else if (contactLoadType=="friends"){// show friends + root.friendsSignal(login.username); + Newsjs.getCurrentContacts(login,db,function(contacts){ + contactlist=contacts; + })} + + else if (contactLoadType=="conversation"){ + var conversationid=news[0].statusnet_conversation_id + if (!isNaN(parseInt(conversationid))){//no directmessage conversation + Newsjs.storeNews(root.login,root.db,news,root,function(){ + Newsjs.conversationfromdb(db,root.login.username,conversationid, function(newsarray){ + newstab.conversation=newsarray; + }) + })} + else {newstab.conversation=news}//only DM conversations from database + } + else if (contactLoadType=="favorites"){//show favorited news + Newsjs.storeNews(root.login,root.db,news,root,function(){ + Newsjs.favoritesfromdb(db,login.username,function(newsarray){ + root.newsSignal(newsarray); + newstab.newstabstatus="Favorites"; + }) + })} + callback() +} + +function updateContactInDB(login,database,isFriend,contact){// for newstab and friendstab + var suffix=contact.profile_image_url.substring(contact.profile_image_url.lastIndexOf("."), contact.profile_image_url.length); + var imagename=login.imagestore+"contacts/"+contact.screen_name.trim()+suffix; + contacttimer.restart(); + var currentTime=Date.now(); + xhr.setUrl(Qt.resolvedUrl(contact.profile_image_url)); + xhr.setFilename(imagename); + xhr.setDownloadtype("contact"); + xhr.download(); + var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); + var result; + db.transaction( function(tx) { + result = tx.executeSql('SELECT * from contacts where username="'+root.login.username+'" AND id = '+contact.id); // check for news id + if(result.rows.length === 1) {// use update + result = tx.executeSql('UPDATE contacts SET username="'+login.username+'", id='+contact.id+', name="'+Qt.btoa(contact.name)+'", screen_name="'+contact.screen_name+'", location="'+contact.location+'",imageAge='+currentTime+', profile_image_url="'+contact.profile_image_url+'", description="'+Qt.btoa(contact.description)+'", profile_image="'+imagename+'", url="'+contact.url+'" , protected="'+contact.protected+'", followers_count='+contact.followers_count+', friends_count='+contact.friends_count+', created_at="'+ Date.parse(Newsjs.cleanDate(contact.created_at))+'", favourites_count="'+contact.favorites_count+'", utc_offset="'+contact.utc_offset+'", time_zone="'+contact.time_zone+'", statuses_count='+contact.statuses_count+', following="'+contact.following+'", verified ="'+contact.verified+'", statusnet_blocking="'+contact.statusnet_blocking+'", notifications="'+contact.notifictions+'", statusnet_profile_url="'+contact.statusnet_profile_url+'", cid='+contact.cid+', network="'+contact.network+'", isFriend='+isFriend+' where username="'+root.login.username+'" AND id='+contact.id); + } else {// use insert + result = tx.executeSql('INSERT INTO contacts VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,contact.id,Qt.btoa(contact.name),contact.screen_name,contact.location,currentTime,contact.profile_image_url, Qt.btoa(contact.description),imagename,contact.url,contact.protected,contact.followers_count, contact.friends_count,Date.parse(Newsjs.cleanDate(contact.created_at)),contact.favorites_count,contact.utc_offset,contact.time_zone,contact.statuses_count,contact.following,contact.verfied,contact.statusnet_blocking,contact.notifications,contact.statusnet_profile_url,contact.cid,contact.network,isFriend]);} + }); +} diff --git a/v0.002/Release/source-linux/qml/configqml/ConfigTab.qml b/v0.002/Release/source-linux/qml/configqml/ConfigTab.qml new file mode 100644 index 0000000..c7dd2a3 --- /dev/null +++ b/v0.002/Release/source-linux/qml/configqml/ConfigTab.qml @@ -0,0 +1,314 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.2 +import "qrc:/js/service.js" as Service +import "qrc:/js/layout.js" as Layoutjs +import "qrc:/js/helper.js" as Helperjs +import "qrc:/qml/configqml" +import "qrc:/qml/genericqml" + +StackView{ + id: configStack + anchors.fill:parent + initialItem: Flickable{ + width:root.width-5*mm + height:root.height-12*mm + contentHeight: configBackground.height + boundsBehavior: Flickable.StopAtBounds + Rectangle{ + id:configBackground + color: "grey" + width:parent.width + height:Math.max(90*mm,root.height-12*mm) + property var users:[] + BlueButton{ + id:userButton + text:qsTr("User") + y:mm + width: root.width/2 + onClicked:{ + var useritems=""; + for (var i=0;iFriendiqa v0.002
Licensed under GPL 3
"+ + "Profile https://freunde.ma-nic.de/profile/friendiqa
"+ + "Sourcecode: https://github.com/LubuWest/Friendica
"+ + "C++ code by Fabio
"+ + "QML and Javascript code by Marco" + onLinkActivated:{ + Qt.openUrlExternally(link)} + } + BlueButton{ + text:qsTr("Close") + onClicked:{configStack.pop()} + anchors.top:infoBoxText.bottom + } + } diff --git a/v0.002/Release/source-linux/qml/contactqml/ContactComponent.qml b/v0.002/Release/source-linux/qml/contactqml/ContactComponent.qml new file mode 100644 index 0000000..26432bc --- /dev/null +++ b/v0.002/Release/source-linux/qml/contactqml/ContactComponent.qml @@ -0,0 +1,67 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import "qrc:/js/layout.js" as Layoutjs +import "qrc:/qml/genericqml" + +Item { +id: contactComponent +property var createdAtDate: new Date(contact.created_at) +property var linkUrl: contact.network!=="dfrn"?contact.url:contact.url.replace("profile","dfrn_request") +Rectangle { + id: wrapper + width: 16*mm + height: 15*mm + border.color: "grey" + color:"white" + Image { + id: photoImage + x:1 + y:1 + width: 10*mm + height:10*mm + source:(contact.profile_image!="")? "file://"+contact.profile_image : contact.profile_image_url + onStatusChanged: if (photoImage.status == Image.Error) {source="qrc:/images/defaultcontact.jpg"} + } + + Label { + id: namelabel + x: 1 + width: wrapper.width-4 + height: 3*mm + text: contact.screen_name + elide:Text.ElideRight + anchors.topMargin: 0 + anchors.left: photoImage.left + color: "#303030" + font.pixelSize: 3*mm + anchors.top: photoImage.bottom + } + BlueButton{ + id:infobutton + width: 5*mm + height: 5*mm + color:"transparent" + text:"?" + anchors.left: photoImage.right + anchors.leftMargin: 3 + anchors.topMargin: 3 + anchors.top: parent.top + onClicked:{ + contactComponent.state="large"; + var component = Qt.createComponent("qrc:/qml/contactqml/ContactDetailsComponent.qml"); + if (component.status== Component.Ready){ + var contactDetails = component.createObject(wrapper,{"contact": contact})} + } + } + } +states: [ + State { + name: "large" + PropertyChanges { target: namelabel; font.pixelSize: 4*mm; width:friendsTabView.width-4*mm; text:Qt.atob(contact.name)+" (@"+contact.screen_name+")"} + PropertyChanges { target: contactComponent; z: 2 } + PropertyChanges { target: wrapper; width:friendsTabView.width-3*mm;height:friendsTabView.height-10*mm} + PropertyChanges { target: photoImage; width:15*mm;height:15*mm } + PropertyChanges { target:contactComponent.GridView.view;contentY:contactComponent.y;contentX:contactComponent.x;interactive:false} + } +] +} diff --git a/v0.002/Release/source-linux/qml/contactqml/ContactDetailsComponent.qml b/v0.002/Release/source-linux/qml/contactqml/ContactDetailsComponent.qml new file mode 100644 index 0000000..da82738 --- /dev/null +++ b/v0.002/Release/source-linux/qml/contactqml/ContactDetailsComponent.qml @@ -0,0 +1,81 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import "qrc:/qml/genericqml" + +Rectangle{ + id: detailsrectangle + anchors.top: namelabel.bottom + anchors.topMargin: 2*mm + //opacity: 0 + + ScrollView{ + horizontalScrollBarPolicy:Qt.ScrollBarAlwaysOff + frameVisible: true + //Flickable{ + id:namelabelflickable + width: root.width-10*mm + height:friendsTabView.height-45*mm + //boundsBehavior:Flickable.StopAtBounds + //flickableDirection:Flickable.VerticalFlick + //contentWidth:width + //contentHeight: namelabeltext.height + clip:true + Text{ + id:namelabeltext + width: namelabelflickable.width + height: implicitHeight + font.pixelSize: 3*mm + textFormat:Text.RichText + wrapMode: Text.Wrap + text:""+qsTr("Description")+": "+Qt.atob(contact.description)+"
"+qsTr("Location")+": "+contact.location+"
"+qsTr("Posts")+": "+contact.statuses_count+ + "
"+qsTr("URL")+": "+linkUrl+"
"+ + qsTr("Created at")+": "+createdAtDate.toLocaleString(Qt.locale()) + onLinkActivated: { + Qt.openUrlExternally(link)} + } + } + + Row{ + anchors.top: namelabelflickable.bottom + anchors.topMargin: 2*mm + spacing:4 + + BlueButton{ + id:photobutton + text:"Photos" + visible:contact.network=="dfrn"? 1:0 + onClicked:{contactComponent.state="";detailsrectangle.destroy(); + root.currentIndex=2; + fotostab.active=true; + root.fotoSignal(contact) ; + } + } + + BlueButton{ + id:messagebutton + text:"Messages" + onClicked:{contactComponent.state="";detailsrectangle.destroy(); + root.currentIndex=0; + newstab.active=true; + root.messageSignal(contact.id) ; + } + } + + BlueButton{ + id:dmbutton + visible: contact.following=="true"?true:false + text: "DM" + onClicked:{contactComponent.state="";detailsrectangle.destroy(); + root.currentIndex=0; + newstab.active=true; + root.directmessageSignal(contact.screen_name); + } + } + + BlueButton{ + id: closeButton + text: "close" + onClicked:{contactComponent.state="";detailsrectangle.destroy()} + } + } + } diff --git a/v0.002/Release/source-linux/qml/contactqml/FriendsTab.qml b/v0.002/Release/source-linux/qml/contactqml/FriendsTab.qml new file mode 100644 index 0000000..c2e3521 --- /dev/null +++ b/v0.002/Release/source-linux/qml/contactqml/FriendsTab.qml @@ -0,0 +1,197 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.4 +import "qrc:/js/service.js" as Service +import "qrc:/js/helper.js" as Helperjs +import "qrc:/js/news.js" as Newsjs +import "qrc:/qml/contactqml" +import "qrc:/qml/genericqml" + +Rectangle { + y:1 + color: "white" + TabView{ + id:friendsTabView + tabPosition: Qt.TopEdge + x:mm + y:mm + width: root.width-2*mm + height: root.height-10*mm + currentIndex: 0 + + signal contactsSignal(var username) + signal groupsSignal(var username) + onCurrentIndexChanged:{ + if (currentIndex==0){root.friendsSignal(root.login.username)} + else if (currentIndex==1){contactsSignal(root.login.username)} + else if (currentIndex==2){groupsSignal(root.login.username)} + } + style: TabViewStyle { + frameOverlap: 1 + tab: Rectangle { + color: "white" + //border.color: "light grey" + implicitWidth: root.width/3-2*mm + implicitHeight: 4*mm + Text { id: text + anchors.centerIn: parent + text: styleData.title + color: "dark grey" + font.pixelSize:2.5*mm + font.bold: styleData.selected + } + } + frame: Rectangle { color: "light grey" } + tabsAlignment:Qt.AlignHCenter + } + + Tab{ + title: qsTr("Friends") + Rectangle{ + id: friendsGridTab + function showFriends(username){ + try {friendsModel.clear()} catch(e){print(e)}; + Helperjs.readData(db,"contacts",username,function(friendsobject){ + for (var i=0;i0){// download first contact image and update db + Service.updateContactInDB(login,db,newContacts[currentContact].isFriend,newContacts[currentContact])} + else if (contactLoadType!=""){ + Service.processNews(function(){ + root.contactLoadType=""; + root.news=[]; + })} + } + + onCurrentContactChanged:{// download next contact image after photoplaceholder is finished saving and update db + if(currentContact0) {xhr.setParam("group_allow", Helperjs.cleanArray(group_allow))}; + if (group_deny.length>0) {xhr.setParam("group_deny", Helperjs.cleanArray(group_deny))}; + if (contact_allow.length>0) {xhr.setParam("contact_allow", Helperjs.cleanArray(contact_allow))}; + if (contact_deny.length>0) {xhr.setParam("contact_deny", Helperjs.cleanArray(contact_deny))}; + if (attachImageURL!=="") {xhr.setImageFileParam("media", attachImageURL )}; + xhr.post(); + } + + function dmUpdate(title,text,replyto,screen_name,attachImageURL) { + xhr.url= login.server + "/api/direct_messages/new.xml"; + xhr.setLogin(login.username+":"+Qt.atob(login.password)); + xhr.clearParams(); + xhr.setParam("text", text); + xhr.setParam("screen_name", screen_name); + if (parentId!="") {xhr.setParam("replyto", replyto)}; + if (title!=="") {xhr.setParam("title", title)}; + xhr.post(); + } + + Column { + id:messageColumn + spacing: 2 + width: parent.width + TextField { + id: titleField + width: parent.width + placeholderText: qsTr("Title (optional)") + visible: messageSend.parentId === "" + } + + TextArea { + id: bodyField + width: parent.width + height: 30*mm + wrapMode: TextEdit.Wrap + textFormat: TextEdit.PlainText + } + + Row{ + spacing: 2 + CheckBox{ + id:dmCheckbox + text:"DM" + enabled: false + checked: (directmessage==1)?true:false + onClicked:{ + if(dmCheckbox.checkedState==Qt.Checked){directmessage=1} + else if(dmCheckbox.checkedState==Qt.Unchecked){directmessage=0} + } + } + + BlueButton{ + text:qsTr("Url") + onClicked: { + if(bodyField.selectedText==""){Helperjs.showMessage("Error","No text selected",messageSend)} + else{urlTextEdit.text=""; + urlRectangle.visible=true}} + } + Rectangle{ + id:urlRectangle + height:parent.height + width:37*mm + visible:false + TextField{ + id:urlTextEdit + width:30*mm + height:parent.height + } + BlueButton{ + anchors.left:urlTextEdit.right + anchors.leftMargin:mm + text:qsTr("\u2713") + onClicked: {if(urlTextEdit.text!=""){ + var start = bodyField.selectionStart; + var end = bodyField.selectionEnd; + var text = bodyField.getText(start,end); + text = "[url="+urlTextEdit.text+"]" + text + "[/url]"; + bodyField.remove(start,end); + bodyField.insert(start,text);} + urlRectangle.visible=false} + } + } + } + + + Row{ + spacing:2 + BlueButton{ + visible: (directmessage==1)?false:true + text:qsTr("Permissions") + onClicked: { + var component = Qt.createComponent("qrc:/qml/newsqml/PermissionDialog.qml"); + var permissions = component.createObject(messageColumn); + }} + BlueButton { + id: attachButton + text: qsTr("Attach") + visible:(directmessage==0) + onClicked: { + if (attachImageURL!=""){ + Helperjs.showMessage( qsTr("Error"),qsTr("Only one attachment. Remove other attachment first!"), messageColumn)} + else{ + try{imageAttachmentObject.destroy()}catch(e){print(e)} + imageAttachmentDialog.open()} + } + } + BlueButton{ + id:contactButton + text:qsTr("cc") + visible:(directmessage==0) + onClicked:{ + var contactitems=""; + for (var i=0;i0){ + print(Qt.atob(root.newContacts[root.currentContact].name)) + } + } + } + function showNews(newsToShow){ + if (newsStack.depth>1){newsStack.pop()} + newsBusy.running=false; + var currentTime= new Date(); + var msg = {'currentTime': currentTime, 'model': newsModel,'news':newsToShow}; + newsWorker.sendMessage(msg); + } + + function onFriendsMessages(friend){ + newstab.newstabstatus="Contact"; + Newsjs.newsfromdb(db,root.login.username, function(dbnews){showNews(dbnews)},friend) + } + + function onDirectMessage(friend){ + newstab.newstabstatus="SendMessage" + newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{"reply_to_user": friend,"directmessage":1,"login":root.login}}); + } + + + + StackView{ + id: newsStack + anchors.fill:parent + + initialItem:Rectangle { + y:1 + color: "white" + width:root.width-2*mm + height:root.height-8*mm + + BlueButton{ + id:newstabstatusButton + anchors.top: parent.top + anchors.topMargin: 0.5*mm + text: qsTr(newstab.newstabstatus) + onClicked: {newstabmenu.popup()} + } + + Row{ + spacing: mm + anchors.top: parent.top + anchors.topMargin: 0.5*mm + anchors.right: parent.right + + BlueButton { + id: newMessageButton + width:10*mm + text: qsTr("+") + onClicked: { + var groups=[]; + Helperjs.readData(root.db,"groups",root.login.username,function(groupobject){ + groups=groupobject}); + newstab.newstabstatus="SendMessage" + Helperjs.readData(root.db,"contacts",root.login.username,function(friends){ + newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{"contacts": friends,"login":root.login}}) + },"isFriend",1); + } + } + BlueButton { + id: quitButton + width:10*mm + text: qsTr("Quit") + onClicked: {Service.cleanNews(root.db,function(){Qt.quit() })} + } + BlueButton { + id: update + text: "Update" + onClicked: { + newsBusy.running=true; + newstab.newstabstatus=login.newsViewType; + root.contactLoadType="news"; + var onlynew=true; + Newsjs.getFriendsTimeline(login,db,contactlist,onlynew,newstab,function(ns,nc){ + root.news=ns;root.newContacts=nc;root.currentContact=0; + if (ns.length==0){// update last 20 existing news for changes and likes + onlynew=false; + Newsjs.getFriendsTimeline(login,db,contactlist,onlynew,newstab,function(rns,rnc){ + root.contactLoadType="news"; + root.news=rns;root.newContacts=rnc;root.currentContact=0}) + } + })} + } + + } + Component { id:footerComponent + Rectangle{ + border.color: "#EEEEEE" + border.width: 1 + width:parent.width + height:6*mm + Text{ + font.pixelSize: 1.5*mm + anchors.centerIn: parent + text:qsTr("More") + } + MouseArea{anchors.fill:parent + onClicked:{ + var currentTime= new Date(); + if(newstab.newstabstatus=="Timeline"){ + var lastnews_id=newsModel.get(newsModel.count-1).newsitemobject.created_at; + Newsjs.newsfromdb(root.db,root.login.username, function(news){ + var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true}; + newsWorker.sendMessage(msg); + },false,lastnews_id)} + if(newstab.newstabstatus=="Tree"){ + var lastnews_id=newsModel.get(newsModel.count-1).newsitemobject.created_at; + Newsjs.chatsfromdb(root.db,root.login.username, function(news){ + var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true}; + newsWorker.sendMessage(msg); + },lastnews_id)} + else if(newstab.newstabstatus=="Contact"){ + Newsjs.newsfromdb(root.db,root.login.username, function(news){ + var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true}; + newsWorker.sendMessage(msg); + },newsModel.get(newsModel.count-1).newsitemobject.uid,newsModel.get(newsModel.count-1).newsitemobject.created_at)} + }} + } + } + ListView { + id: newsView + anchors.fill: parent + anchors.topMargin: 8*root.mm + anchors.leftMargin: 3*root.mm; anchors.rightMargin: root.mm + anchors.bottomMargin: 1*root.mm + clip: true + spacing: 0 + footer: footerComponent + model: newsModel + delegate: Newsitem{} + } + + ListModel{id: newsModel} + + WorkerScript { + id: newsWorker + source: "qrc:/js/newsworker.js" + } + + BusyIndicator{ + id: newsBusy + anchors.horizontalCenter: newsView.horizontalCenter + anchors.top:newsView.top + anchors.topMargin: 2*mm + width:10*mm + height: 10*mm + } + + Menu { + id:newstabmenu + MenuItem { + text: qsTr("Timeline") + onTriggered: { + newstab.newstabstatus="Timeline"; + newsModel.clear(); + Newsjs.newsfromdb(root.db,root.login.username, function(dbnews){ + showNews(dbnews) + })} + } + MenuItem { + text: qsTr("Favorites") + onTriggered:{ + newstab.newstabstatus="Favorites"; + root.contactLoadType="favorites"; + newsBusy.running=true; + Newsjs.requestFavorites(root.login,db,root.contactlist,root,function(ns,nc){ + root.news=ns; root.newContacts=nc;root.currentContact=0; + }) + } + } + + MenuItem { + text: qsTr("Tree") + onTriggered:{ + newsModel.clear(); + newstab.newstabstatus="Tree"; + Newsjs.chatsfromdb(db,root.login.username,function(news){showNews(news)}) + } + } + MenuItem { + text: qsTr("Notifications") + onTriggered:{ + newstab.newstabstatus="Notifications"; + newsBusy.running=true; + Newsjs.getNotifications(root.login,db,root,function(news){ + showNews(news)} + )} + } + } + Component.onCompleted: { + root.messageSignal.connect(onFriendsMessages); + root.directmessageSignal.connect(onDirectMessage); + root.newsSignal.connect(showNews); + try{newsModel.clear()} catch(e){} + if(login.newsViewType=="Timeline"){Newsjs.newsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + else{Newsjs.chatsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + } + } + } +} diff --git a/v0.002/Release/source-linux/qml/newsqml/Newsitem.qml b/v0.002/Release/source-linux/qml/newsqml/Newsitem.qml new file mode 100644 index 0000000..e6a85fa --- /dev/null +++ b/v0.002/Release/source-linux/qml/newsqml/Newsitem.qml @@ -0,0 +1,343 @@ +import QtQuick 2.0 +import QtQuick.LocalStorage 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "qrc:/js/news.js" as Newsjs + + +Item { + id: newsitem + width: newsView.width + height:Math.max((itemMessage.height+topFlow.height+friendicaActivities.height+4*mm),profileImage.height+user_name.height+mm) + + property string conversation_id: "" + property string attending: "" + onAttendingChanged: {attendLabel.visible=true; + attendLabel.text= qsTr("attending: ")+ qsTr(attending)} + signal replyto(string parent_id) + function showConversation(){ + conversationsymbol.color="black"; + newsBusy.running=true; + root.contactLoadType="conversation"; + //newstabstatus="Conversation"; + + if(newsitemobject.messagetype==0){ + Newsjs.requestConversation(root.login,db,newsitemobject.status_id,root.contactlist,root,function(ns,nc){ + root.news=ns;root.newContacts=nc;root.currentContact=0; + })} + else{Newsjs.conversationfromdb(root.db,root.login.username,newsitemobject.statusnet_conversation_id, function(newsarray){ + root.news=newsarray;root.newContacts=[];root.currentContact=1; + })}} + + Rectangle{width:newsitem.width; height: 1; anchors.bottom: newsitem.bottom; color:"light grey"} + //MouseArea{ + // anchors.fill: parent; + // enabled: (newstabstatus=="Chats") + // onClicked: {showConversation()} + //} + Rectangle{ + width:newsitem.width + height:newsitem.height-1 + color: (newsitemobject.messagetype==1)?"#ffe6e6" : "white" + + Column { + id: authorcolumn + width: 8*mm + + Image { + id:profileImage + source: (newsitemobject.user.profile_image!="")? "file://"+newsitemobject.user.profile_image : newsitemobject.user.profile_image_url + x:1 + width: 7*mm + height: 7*mm + MouseArea{ + anchors.fill: parent + onPressAndHold: { newsmenu.popup()} + } + onStatusChanged: if (profileImage.status == Image.Error) {source="qrc:/images/defaultcontact.jpg"} + } + Label { + id:user_name + color: "grey" + //height:3.5*mm + width:parent.width + font.pixelSize: 1.5*mm + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + text: Qt.atob(newsitemobject.user.name) + } + } + Column { + id:newscolumn + width: newsitem.width-8*mm + anchors.left: authorcolumn.right + + Flow{ + id:topFlow + spacing: mm + width:parent.width + Label { + id:messageTypeLabel + color: "grey" + text: if (newsitemobject.messagetype==0){qsTr("Source: ")+newsitemobject.source + } else if (newsitemobject.messagetype==1){ qsTr("Direct Message")} else {" Notification"} + font.pixelSize: 1.5*mm + } + Label { + id:createdAtLabel + color: "grey" + //height:3.5*mm + font.pixelSize: 1.5*mm + horizontalAlignment: Label.AlignRight + text: dateDiff + } + Label { + id:replytoLabel + color: "grey" + //height:3.5*mm + font.pixelSize: 1.5*mm + horizontalAlignment: Label.AlignRight + text: try {qsTr("In reply to ")+newsitemobject.reply_user.screen_name + }catch(e){" "} + } + + Label { + id:newscountLabel + visible:((newstabstatus=="Tree")&&(newsitemobject.newscount>1))?true:false + color: "grey" + height:3.5*mm + font.pixelSize: 1.5*mm + font.bold: true + horizontalAlignment: Label.AlignRight + text: try {(newsitemobject.newscount-1)+qsTr(" comments") }catch(e){" "} + MouseArea{ + anchors.fill:parent + onClicked: showConversation() + } + } + } + + Text { + color: "#404040" + linkColor: "light green" + id: itemMessage + textFormat: Text.RichText + text: Qt.atob(newsitemobject.statusnet_html) + width: newsitem.width-8*mm-2 + height: implicitHeight + wrapMode: Text.Wrap + onLinkActivated:{ + Qt.openUrlExternally(link)} + } + + Row{id:friendicaActivities + spacing:mm + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.likeText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.dislikeText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.attendyesText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.attendnoText + } + Label{color: "grey" + font.pixelSize: 1.5*mm + text: friendica_activities.attendmaybeText + } + } + Row { + CheckBox{id:likeCheckbox + height:3*mm + width:8*mm + visible: (newsitemobject.messagetype==0)? true:false + checked:(friendica_activities.self.liked==1)?true:false + style: CheckBoxStyle { + background: Rectangle { + implicitWidth: 7*mm + implicitHeight: 3*mm + color:"white" + } + indicator: + Rectangle{ + implicitWidth: 3*mm + implicitHeight:3*mm + color:control.checked?"yellow":"white" + x: 5*mm + Text{ + font.pixelSize: 2*mm + color:"grey" + text:":-)" + }} + } + onClicked: { + if(likeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"like",newsitemobject.status_id,root);dislikeCheckbox.checked=false; model.friendica_activities.self.liked=0 } + else{Newsjs.like(root.login,root.db,0,"like",newsitemobject.status_id,root); model.friendica_activities.self.liked=1}} + } + CheckBox{id: dislikeCheckbox + height:3*mm + width:8*mm + visible: (newsitemobject.messagetype==0)? true:false + checked: (friendica_activities.self.disliked==1)?true:false + style: CheckBoxStyle { + background: Rectangle { + implicitWidth: 7*mm + implicitHeight:3*mm + color:"white" + } + indicator: + Rectangle{ + implicitWidth: 3*mm + implicitHeight:3*mm + color:control.checked?"yellow":"white" + x:5*mm + Text{ + font.pixelSize: 2*mm + color:"grey" + text:":-(" + }} + } + onClicked: { + if (dislikeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"dislike",newsitemobject.status_id,root);likeCheckbox.checked=false; model.friendica_activities.self.disliked=0} + else {Newsjs.like(root.login,root.db,0,"dislike",newsitemobject.status_id,root); model.friendica_activities.self.disliked=1}} + } + CheckBox { + id:favoritedCheckbox + visible:(newsitemobject.messagetype==0) + style: CheckBoxStyle { + background: Rectangle { + implicitWidth: 6*mm + implicitHeight:3*mm + color:"transparent" + } + indicator: + Rectangle{x:3*mm + width: 3*mm + implicitHeight:3*mm + Text{ + anchors.centerIn: parent + color:control.checked?"black":"grey" + text:"\u2605" + }} + } + checked:(newsitemobject.favorited>0) + onClicked:{ + if(favoritedCheckbox.checkedState==Qt.Checked) + {Newsjs.favorite(login,true,newsitemobject.status_id,root); model.newsitemobject.favorited=1} + else if(favoritedCheckbox.checkedState==Qt.Unchecked) + {Newsjs.favorite(login,false,newsitemobject.status_id,root);model.newsitemobject.favorited=0} + } + } + Rectangle{ + width: 4*mm + height: 3*mm + color:"transparent" + Text{ + id:newsmenusymbol + color: "grey" + anchors.centerIn: parent + font.pixelSize: 2*mm + font.bold: true + text: "\u22EE" + } + MouseArea{ + anchors.fill:parent + onClicked: {newsmenu.popup()}} + + } + + Rectangle{ + width: 4*mm + height: 3*mm + color:"transparent" + //visible:(newsitemobject.in_reply_to_status_id!="")?true:false + Text{ + id:conversationsymbol + color: "grey" + anchors.centerIn: parent + font.pixelSize: 2*mm + text: "\u21C4" + } + MouseArea{ + anchors.fill:parent + onClicked: showConversation() + } + } + Label { + id:attendLabel + visible: false + color: "grey" + height:3.5*mm + font.pixelSize: 1.5*mm + horizontalAlignment: Label.AlignRight + text: (friendica_activities.self.attending)?qsTr("attending: ")+ qsTr(attending):"" + } + } + } + + Menu { + id:newsmenu + MenuItem { + text: qsTr("Reply") + onTriggered: { + var directmessage=0; + if (newsitemobject.messagetype==1){ directmessage=1} + newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{"reply_to_user": newsitemobject.user.screen_name,"parentId":newsitemobject.status_id,"login":root.login,"directmessage":directmessage}}); + } + } + MenuItem { + text: qsTr("DM") + onTriggered: { + root.directmessageSignal(newsitemobject.user.screen_name); + } + } + MenuItem { + text: qsTr("Repost") + onTriggered: { + Newsjs.retweetNews(root.login,db,newsitemobject.status_id,root,function(reply){ + print(reply); + }) + } + } + MenuItem { + text: qsTr("Conversation") + onTriggered: showConversation() + } + + Menu{ + title: qsTr("Attending") + MenuItem{text:qsTr("yes") + onTriggered: {Newsjs.attend(root.login,db,"yes",newsitemobject.status_id,root,function(){ + newsitem.attending="yes"; + attendLabel.visible=true})} + } + + MenuItem{text:qsTr("maybe") + onTriggered: {Newsjs.attend(root.login,db,"maybe",newsitemobject.status_id,root,function(){ + newsitem.attending="maybe"})} + } + + MenuItem{text:qsTr("no") + onTriggered: {Newsjs.attend(root.login,db,"no",newsitemobject.status_id,root,function(){ + newsitem.attending="no"})} + } + } + + MenuItem { + text: qsTr("Delete") + onTriggered: { + Newsjs.deleteNews(root.login,root.db,newsitemobject.status_id,newsitemobject.messagetype,root,function(reply){ + newsModel.remove(index); + }) + } + } +} +} +} + diff --git a/v0.002/Release/source-linux/qml/newsqml/PermissionDialog.qml b/v0.002/Release/source-linux/qml/newsqml/PermissionDialog.qml new file mode 100644 index 0000000..d7e659a --- /dev/null +++ b/v0.002/Release/source-linux/qml/newsqml/PermissionDialog.qml @@ -0,0 +1,191 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.4 +import QtQml.Models 2.1 +import "qrc:/js/service.js" as Service +import "qrc:/js/helper.js" as Helperjs +import "qrc:/qml/genericqml" + +Rectangle{ + id:permissionDialog + x: mm + width: messageColumn.width-5*mm + height:root.height/3 + function updatePerms(){ + for (var i=0;i-1){contactstatus="positive";print(contacts[name].cid+" pos")} + else if (contact_deny.indexOf(contacts[name].cid)>-1){contactstatus="negative"} + contactModel.append({"contact":contacts[name],"contactstatus":contactstatus}) + }},"isFriend",1); + + Helperjs.readData(db,"groups",login.username,function(owngroups){ + for (var number in owngroups){ + var groupstatus= "neutral"; + if (group_allow.indexOf(owngroups[number].gid)>-1){groupstatus="positive"} + else if (group_deny.indexOf(owngroups[number].gid)>-1){groupstatus="negative"} + groupModel.append({"group":owngroups[number],"groupstatus":groupstatus}) + }}); + } +} diff --git a/v0.002/Release/source-linux/qml/photoqml/PhotoComponent.qml b/v0.002/Release/source-linux/qml/photoqml/PhotoComponent.qml new file mode 100644 index 0000000..9884d71 --- /dev/null +++ b/v0.002/Release/source-linux/qml/photoqml/PhotoComponent.qml @@ -0,0 +1,99 @@ +import QtQuick 2.0 +//import QtQuick.LocalStorage 2.0 +import QtQuick.Controls 1.2 + +Package { + Item { id: stackItem; Package.name: 'stack'; z: stackItem.PathView.z;width:16.5*mm;height:16.5*mm} + Item { id: listItem; Package.name: 'list'; width: root.width-1*mm; height: root.height-8*mm; } + Item { id: gridItem; Package.name: 'grid';} + + Item { + id: photoWrapper + width: 16.5*mm; height: 16.5*mm + z: stackItem.PathView.z + property string hqphotolink: photoLink + + Rectangle { + id: placeHolder + color: 'lightblue'; antialiasing: true + anchors.fill:parent + } + + BusyIndicator { anchors.centerIn: parent; running: realImage.status != Image.Ready } + Image { + id: realImage; + // property string hqphotolink: photoLink + width: photoWrapper.width; height: photoWrapper.height + antialiasing: true; + asynchronous: true + cache: false + fillMode: Image.PreserveAspectFit; + source: imageLocation + // onStatusChanged: if (realImage.status == Image.Ready) print(realImage.paintedHeight+"x"+realImage.paintedWidth) + } + Rectangle{ + id:phototextRectangle + color:"black" + z:3 + opacity: 0.5 + width:phototext.contentWidth + height: phototext.contentHeight + anchors.bottom: photoWrapper.bottom + } + Text { + id:phototext + z:4 + text: photoDescription.trim() + width:15*mm + anchors.bottom: photoWrapper.bottom + color: "white" + font.pixelSize: 2*mm + wrapMode:Text.Wrap + } + MouseArea { + width: realImage.paintedWidth; height: realImage.paintedHeight; anchors.centerIn: realImage + onClicked: { + if (albumWrapper.state == 'inGrid') { + gridItem.GridView.view.currentIndex = index; + //print("photoLink"+realImage.photoLink) + albumWrapper.state = 'fullscreen' + } else { + gridItem.GridView.view.currentIndex = index; + albumWrapper.state = 'inGrid' + } + } + } + + states: [ + State { + name: 'stacked'; when: albumWrapper.state == '' + ParentChange { target: photoWrapper; parent: stackItem; }//x: 1*mm; y: 1*mm } + PropertyChanges { target: photoWrapper; opacity: stackItem.PathView.onPath ? 1.0 : 0.0 } + PropertyChanges { target: phototext; opacity: 0.0 } + PropertyChanges { target: phototextRectangle; opacity: 0.0 } + }, + State { + name: 'inGrid'; when: albumWrapper.state == 'inGrid' + ParentChange { target: photoWrapper; parent: gridItem; x: 1*mm; y: 1*mm;} + PropertyChanges { target: phototext; opacity: 1.0 } + PropertyChanges { target: phototextRectangle; opacity: 0.5 } + PropertyChanges { target: placeHolder; opacity: 1.0 } + }, + State { + name: 'fullscreen'; when: albumWrapper.state == 'fullscreen' + ParentChange { + target: photoWrapper; parent: listItem; x: 1; y: 1; + width: root.width-mm; height: root.height-8*mm + } + PropertyChanges { target: placeHolder; opacity: 0.0 } + PropertyChanges { target: realImage; source: photoWrapper.hqphotolink} + PropertyChanges { target: phototext; anchors.bottom: realImage.bottom} + PropertyChanges { target: phototext; width:realImage.width } + PropertyChanges { target: phototextRectangle; anchors.bottom: realImage.bottom } + PropertyChanges { target: realImage; width: Math.min(listItem.width,sourceSize.width);height: Math.min(listItem.height,sourceSize.height) } + } + ] + + } +} + diff --git a/v0.002/Release/source-linux/qml/photoqml/PhotoTab.qml b/v0.002/Release/source-linux/qml/photoqml/PhotoTab.qml new file mode 100644 index 0000000..edada91 --- /dev/null +++ b/v0.002/Release/source-linux/qml/photoqml/PhotoTab.qml @@ -0,0 +1,181 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls 1.4 +import QtQml.Models 2.1 +import "qrc:/js/service.js" as Service +import "qrc:/js/helper.js" as Helperjs +import "qrc:/qml/photoqml" +import "qrc:/qml/genericqml" + +Rectangle { + id:fotorectangle + property string phototabstatus:"Images" + onPhototabstatusChanged:{phototabstatusButton.text=qsTr(phototabstatus)} + + y:1 + width:root.width-mm + height:root.height-5*mm + color: '#fff' + property var newimages:[] + property int currentimageno: 0 +//onLoginChanged:{var msg = {'model': photogroupModel,'albums':[],'firstalbum':0,'foreignPicture':false}; +// photoWorker.sendMessage(msg); +//} + onNewimagesChanged:{ + if(newimages.length>0){ + Helperjs.readField("album",root.db,"imageData",root.login.username,function(albums){ + //print("albums"+JSON.stringify(albums)+JSON.stringify(newimages[currentimageno])); + for (var i=0;i