Merge lp:~mzanetti/reminders-app/contenthub-import into lp:reminders-app

Proposed by Michael Zanetti
Status: Merged
Approved by: Riccardo Padovani
Approved revision: 455
Merged at revision: 461
Proposed branch: lp:~mzanetti/reminders-app/contenthub-import
Merge into: lp:reminders-app
Diff against target: 597 lines (+335/-89)
11 files modified
CMakeLists.txt (+1/-0)
manifest.json.in (+2/-1)
reminders-contenthub.json (+8/-0)
src/app/qml/components/NotesDelegate.qml (+1/-1)
src/app/qml/components/StatusBar.qml (+35/-21)
src/app/qml/reminders.qml (+112/-18)
src/app/qml/ui/NotesPage.qml (+0/-3)
src/libqtevernote/note.cpp (+15/-1)
src/libqtevernote/note.h (+2/-0)
src/libqtevernote/utils/enmldocument.cpp (+155/-44)
src/libqtevernote/utils/enmldocument.h (+4/-0)
To merge this branch: bzr merge lp:~mzanetti/reminders-app/contenthub-import
Reviewer Review Type Date Requested Status
Riccardo Padovani Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+261919@code.launchpad.net

Commit message

Make Notes a Content Hub Target for pictures and links

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Looks good (just three little things to change if you don't mind).

review: Needs Fixing
455. By Michael Zanetti

fix nitpicks from review

Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-02-16 22:01:20 +0000
3+++ CMakeLists.txt 2015-06-22 08:35:28 +0000
4@@ -74,6 +74,7 @@
5 reminders.url-dispatcher
6 push-helper.json
7 push-helper.apparmor
8+ reminders-contenthub.json
9 DESTINATION ${CMAKE_INSTALL_PREFIX})
10 install(FILES COPYING DESTINATION ${CMAKE_INSTALL_PREFIX})
11 else(CLICK_MODE)
12
13=== modified file 'manifest.json.in'
14--- manifest.json.in 2015-03-20 11:16:35 +0000
15+++ manifest.json.in 2015-06-22 08:35:28 +0000
16@@ -8,7 +8,8 @@
17 "account-service": "@ACCOUNT_SERVICE_DIR@/com.ubuntu.reminders_reminders.service",
18 "apparmor": "reminders.apparmor",
19 "desktop": "com.ubuntu.reminders.desktop",
20- "urls": "reminders.url-dispatcher"
21+ "urls": "reminders.url-dispatcher",
22+ "content-hub": "reminders-contenthub.json"
23 },
24 "evernote-account-plugin": {
25 "account-qml-plugin": "@ACCOUNT_QML_PLUGIN_DIR@/evernote",
26
27=== added file 'reminders-contenthub.json'
28--- reminders-contenthub.json 1970-01-01 00:00:00 +0000
29+++ reminders-contenthub.json 2015-06-22 08:35:28 +0000
30@@ -0,0 +1,8 @@
31+{
32+ "destination": [
33+ "pictures",
34+ "links",
35+ "text"
36+ ]
37+}
38+
39
40=== modified file 'src/app/qml/components/NotesDelegate.qml'
41--- src/app/qml/components/NotesDelegate.qml 2015-03-15 20:00:21 +0000
42+++ src/app/qml/components/NotesDelegate.qml 2015-06-22 08:35:28 +0000
43@@ -136,7 +136,7 @@
44 Layout.fillHeight: true
45 text: "<font color=\"" + root.notebookColor + "\">" +
46 Qt.formatDateTime(root.date, Qt.LocalDate) +
47- " </font>" + root.content
48+ " </font>" + root.content.replace("\n", " ").trim()
49 wrapMode: Text.WordWrap
50 textFormat: Text.StyledText
51 maximumLineCount: 2
52
53=== modified file 'src/app/qml/components/StatusBar.qml'
54--- src/app/qml/components/StatusBar.qml 2015-03-08 21:29:28 +0000
55+++ src/app/qml/components/StatusBar.qml 2015-06-22 08:35:28 +0000
56@@ -10,36 +10,50 @@
57
58 property bool shown: false
59 property alias iconName: icon.name
60+ property alias iconColor: icon.color
61 property alias text: label.text
62+ property alias showCancelButton: cancelButton.visible
63+
64+ signal cancel();
65
66 Behavior on height {
67 UbuntuNumberAnimation {}
68 }
69
70- ColumnLayout {
71+ RowLayout {
72 id: statusBarContents
73- anchors { left: parent.left; top: parent.top; right: parent.right }
74+ anchors { left: parent.left; right: parent.right; leftMargin: units.gu(1); rightMargin: units.gu(1); verticalCenter: parent.verticalCenter }
75 spacing: units.gu(1)
76-
77- Row {
78- anchors { left: parent.left; right: parent.right; margins: units.gu(1) }
79+ Column {
80 spacing: units.gu(1)
81- height: label.height
82-
83- Icon {
84- id: icon
85- height: units.gu(3)
86- width: height
87- color: UbuntuColors.red
88- anchors.verticalCenter: parent.verticalCenter
89- }
90-
91- Label {
92- id: label
93- width: parent.width - x
94- wrapMode: Text.WordWrap
95- anchors.verticalCenter: parent.verticalCenter
96- }
97+ Layout.fillWidth: true
98+
99+ Row {
100+ anchors { left: parent.left; right: parent.right }
101+ spacing: units.gu(1)
102+ height: label.height
103+
104+ Icon {
105+ id: icon
106+ height: units.gu(3)
107+ width: height
108+ anchors.verticalCenter: parent.verticalCenter
109+ }
110+
111+ Label {
112+ id: label
113+ width: parent.width - x
114+ wrapMode: Text.WordWrap
115+ anchors.verticalCenter: parent.verticalCenter
116+ }
117+ }
118+ }
119+
120+ Button {
121+ id: cancelButton
122+ Layout.preferredWidth: height
123+ iconName: "cancel"
124+ onClicked: root.cancel();
125 }
126 }
127 }
128
129=== modified file 'src/app/qml/reminders.qml'
130--- src/app/qml/reminders.qml 2015-06-12 09:48:22 +0000
131+++ src/app/qml/reminders.qml 2015-06-22 08:35:28 +0000
132@@ -26,6 +26,7 @@
133 import Ubuntu.OnlineAccounts 0.1
134 import Ubuntu.OnlineAccounts.Client 0.1
135 import Ubuntu.PushNotifications 0.1
136+import Ubuntu.Content 1.0
137 import "components"
138 import "ui"
139
140@@ -79,6 +80,45 @@
141 }
142 }
143
144+ property var importTransfer: null
145+ function handleImportTransfer(note) {
146+ if (importTransfer == null) return;
147+
148+ for (var i = 0; i < importTransfer.items.length; i++) {
149+ var url = importTransfer.items[i].url;
150+ switch (importTransfer.contentType) {
151+ case ContentType.Links:
152+ note.insertLink(note.plaintextContent.length, url)
153+ break;
154+ default:
155+ note.attachFile(note.plaintextContent.length, url)
156+ break;
157+ }
158+ }
159+ note.save();
160+ importTransfer = null;
161+ }
162+
163+ Connections {
164+ target: ContentHub
165+ onImportRequested: {
166+ importTransfer = transfer;
167+ var popup = PopupUtils.open(importQuestionComponent);
168+ popup.accepted.connect(function(createNew) {
169+ PopupUtils.close(popup);
170+ if (createNew) {
171+ var note = NotesStore.createNote(i18n.tr("Untitled"));
172+ handleImportTransfer(note);
173+ }
174+ })
175+
176+ popup.rejected.connect(function() {
177+ PopupUtils.close(popup);
178+ importTransfer = null;
179+ })
180+ }
181+ }
182+
183 Timer {
184 id: connectDelayTimer
185 interval: 2000
186@@ -107,6 +147,10 @@
187 conflictMode = false;
188 }
189
190+ if (importTransfer != null) {
191+ handleImportTransfer(note);
192+ }
193+
194 print("displayNote:", note.guid)
195 note.load(true);
196 if (root.narrowMode) {
197@@ -436,26 +480,43 @@
198 }
199 }
200
201- StatusBar {
202+ Column {
203 id: statusBar
204 anchors { left: parent.left; right: parent.right; top: parent.top; topMargin: units.gu(9) }
205- color: root.backgroundColor
206- shown: text
207- text: EvernoteConnection.error || NotesStore.error
208- iconName: "sync-error"
209-
210- Timer {
211- interval: 5000
212- repeat: true
213- running: NotesStore.error
214- onTriggered: NotesStore.clearError();
215- }
216-
217- MouseArea {
218- anchors.fill: parent
219- onClicked: NotesStore.clearError();
220- }
221-
222+
223+ StatusBar {
224+ anchors { left: parent.left; right: parent.right }
225+ color: root.backgroundColor
226+ shown: text
227+ text: EvernoteConnection.error || NotesStore.error
228+ iconName: "sync-error"
229+ iconColor: UbuntuColors.red
230+ showCancelButton: true
231+
232+ onCancel: {
233+ NotesStore.clearError();
234+ }
235+
236+ Timer {
237+ interval: 5000
238+ repeat: true
239+ running: NotesStore.error
240+ onTriggered: NotesStore.clearError();
241+ }
242+ }
243+
244+ StatusBar {
245+ anchors { left: parent.left; right: parent.right }
246+ color: root.backgroundColor
247+ shown: importTransfer != null
248+ text: importTransfer.items.length === 1 ? i18n.tr("Select note to attach imported file") : i18n.tr("Select note to attach imported files")
249+ iconName: "document-save"
250+ showCancelButton: true
251+
252+ onCancel: {
253+ importTransfer = null;
254+ }
255+ }
256 }
257
258 PageStack {
259@@ -673,4 +734,37 @@
260 }
261 }
262 }
263+
264+ Component {
265+ id: importQuestionComponent
266+
267+ Dialog {
268+ id: importDialog
269+ title: importTransfer.items.length > 1 ?
270+ i18n.tr("Importing %1 items").arg(importTransfer.items.length)
271+ : i18n.tr("Importing 1 item")
272+ text: importTransfer.items.length > 1 ?
273+ i18n.tr("Do you want to create a new note for those items or do you want to attach them to an existing note?")
274+ : i18n.tr("Do you want to create a new note for this item or do you want to attach it to an existing note?")
275+
276+ signal accepted(bool createNew);
277+ signal rejected();
278+
279+ Button {
280+ text: i18n.tr("Create new note")
281+ onClicked: importDialog.accepted(true)
282+ color: UbuntuColors.green
283+ }
284+ Button {
285+ text: i18n.tr("Attach to existing note")
286+ onClicked: importDialog.accepted(false);
287+ color: UbuntuColors.blue
288+ }
289+ Button {
290+ text: i18n.tr("Cancel import")
291+ onClicked: importDialog.rejected();
292+ color: UbuntuColors.red
293+ }
294+ }
295+ }
296 }
297
298=== modified file 'src/app/qml/ui/NotesPage.qml'
299--- src/app/qml/ui/NotesPage.qml 2015-06-11 22:02:25 +0000
300+++ src/app/qml/ui/NotesPage.qml 2015-06-22 08:35:28 +0000
301@@ -142,15 +142,12 @@
302 }
303
304 function sortOrderToString(sortOrder){
305- print("asking for sortOrder", sortOrder, Notes.SortOrderDateUpdatedNewest)
306 switch(sortOrder) {
307 case Notes.SortOrderDateCreatedNewest:
308 case Notes.SortOrderDateCreatedOldest:
309- print("returning createdString")
310 return "createdString";
311 case Notes.SortOrderDateUpdatedNewest:
312 case Notes.SortOrderDateUpdatedOldest:
313- print("returning updatedString")
314 return "updatedString";
315 case Notes.SortOrderTitleAscending:
316 case Notes.SortOrderTitleDescending:
317
318=== modified file 'src/libqtevernote/note.cpp'
319--- src/libqtevernote/note.cpp 2015-06-03 17:45:45 +0000
320+++ src/libqtevernote/note.cpp 2015-06-22 08:35:28 +0000
321@@ -277,7 +277,7 @@
322
323 QString Note::plaintextContent() const
324 {
325- return m_content.toPlaintext().trimmed();
326+ return m_content.toPlaintext();
327 }
328
329 QString Note::tagline() const
330@@ -570,6 +570,20 @@
331 NotesStore::instance()->untagNote(m_guid, tagGuid);
332 }
333
334+void Note::insertText(int position, const QString &text)
335+{
336+ m_content.insertText(position, text);
337+ m_tagline = m_content.toPlaintext().left(100);
338+ emit contentChanged();
339+}
340+
341+void Note::insertLink(int position, const QString &url)
342+{
343+ m_content.insertLink(position, url);
344+ m_tagline = m_content.toPlaintext().left(100);
345+ emit contentChanged();
346+}
347+
348 int Note::renderWidth() const
349 {
350 return m_content.renderWidth();
351
352=== modified file 'src/libqtevernote/note.h'
353--- src/libqtevernote/note.h 2015-03-15 20:00:21 +0000
354+++ src/libqtevernote/note.h 2015-06-22 08:35:28 +0000
355@@ -165,6 +165,8 @@
356 Q_INVOKABLE void attachFile(int position, const QUrl &fileName);
357 Q_INVOKABLE void addTag(const QString &tagGuid);
358 Q_INVOKABLE void removeTag(const QString &tagGuid);
359+ Q_INVOKABLE void insertText(int position, const QString &text);
360+ Q_INVOKABLE void insertLink(int position, const QString &url);
361
362 int renderWidth() const;
363 void setRenderWidth(int renderWidth);
364
365=== modified file 'src/libqtevernote/utils/enmldocument.cpp'
366--- src/libqtevernote/utils/enmldocument.cpp 2015-04-13 20:42:38 +0000
367+++ src/libqtevernote/utils/enmldocument.cpp 2015-06-22 08:35:28 +0000
368@@ -486,48 +486,161 @@
369
370 void EnmlDocument::attachFile(int position, const QString &hash, const QString &type)
371 {
372- QXmlStreamReader reader(m_enml);
373-
374- QString output;
375- QXmlStreamWriter writer(&output);
376- writer.writeStartDocument();
377- writer.writeDTD("<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">");
378-
379- int textPos = 0;
380- bool inserted = false;
381-
382- while (!reader.atEnd() && !reader.hasError()) {
383- QXmlStreamReader::TokenType token = reader.readNext();
384-
385- if (token == QXmlStreamReader::StartElement) {
386- writer.writeStartElement(reader.name().toString());
387- writer.writeAttributes(reader.attributes());
388- }
389-
390- if (token == QXmlStreamReader::Characters) {
391- QString textString = reader.text().toString();
392- if (textPos <= position && textPos + textString.length() > position) {
393- writer.writeCharacters(textString.left(position - textPos));
394-
395- writer.writeStartElement("en-media");
396- writer.writeAttribute("hash", hash);
397- writer.writeAttribute("type", type);
398- writer.writeEndElement();
399- inserted = true;
400-
401- writer.writeCharacters(textString.right(textString.length() - (position - textPos)));
402- } else {
403- writer.writeCharacters(reader.text().toString());
404- }
405- textPos += textString.length();
406- }
407- if (token == QXmlStreamReader::EndElement) {
408-
409- // The above logic would fail on an empty note
410- if (reader.name() == "en-note" && !inserted) {
411- writer.writeStartElement("en-media");
412- writer.writeAttribute("hash", hash);
413- writer.writeAttribute("type", type);
414+ qCDebug(dcEnml) << "Attaching file at position" << position;
415+ QXmlStreamReader reader(m_enml);
416+
417+ QString output;
418+ QXmlStreamWriter writer(&output);
419+ writer.writeStartDocument();
420+ writer.writeDTD("<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">");
421+
422+ int textPos = 0;
423+ bool inserted = false;
424+
425+ while (!reader.atEnd() && !reader.hasError()) {
426+ QXmlStreamReader::TokenType token = reader.readNext();
427+
428+ if (token == QXmlStreamReader::StartElement) {
429+ writer.writeStartElement(reader.name().toString());
430+ writer.writeAttributes(reader.attributes());
431+ }
432+
433+ if (token == QXmlStreamReader::Characters) {
434+ QString textString = reader.text().toString();
435+ if (textPos <= position && textPos + textString.length() > position) {
436+ writer.writeCharacters(textString.left(position - textPos));
437+
438+ writer.writeStartElement("en-media");
439+ writer.writeAttribute("hash", hash);
440+ writer.writeAttribute("type", type);
441+ writer.writeEndElement();
442+ inserted = true;
443+
444+ writer.writeCharacters(textString.right(textString.length() - (position - textPos)));
445+ } else {
446+ writer.writeCharacters(reader.text().toString());
447+ }
448+ textPos += textString.length();
449+ }
450+ if (token == QXmlStreamReader::EndElement) {
451+
452+ // The above logic would fail on an empty note
453+ if (reader.name() == "en-note" && !inserted) {
454+ writer.writeStartElement("en-media");
455+ writer.writeAttribute("hash", hash);
456+ writer.writeAttribute("type", type);
457+ writer.writeEndElement();
458+ }
459+
460+ writer.writeEndElement();
461+ }
462+ }
463+ m_enml = output;
464+}
465+
466+void EnmlDocument::insertText(int position, const QString &text)
467+{
468+ qCDebug(dcEnml) << "Inserting Text at position" << position;
469+
470+ QXmlStreamReader reader(m_enml);
471+
472+ QString output;
473+ QXmlStreamWriter writer(&output);
474+ writer.writeStartDocument();
475+ writer.writeDTD("<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">");
476+
477+ int textPos = 0;
478+ bool inserted = false;
479+
480+ while (!reader.atEnd() && !reader.hasError()) {
481+ QXmlStreamReader::TokenType token = reader.readNext();
482+
483+ if (token == QXmlStreamReader::StartElement) {
484+ writer.writeStartElement(reader.name().toString());
485+ writer.writeAttributes(reader.attributes());
486+ }
487+
488+ if (token == QXmlStreamReader::Characters) {
489+ QString textString = reader.text().toString();
490+ if (textPos <= position && textPos + textString.length() > position) {
491+ writer.writeCharacters(textString.left(position - textPos));
492+
493+ writer.writeStartElement("div");
494+ writer.writeCharacters(text);
495+ writer.writeEndElement();
496+ inserted = true;
497+
498+ writer.writeCharacters(textString.right(textString.length() - (position - textPos)));
499+ } else {
500+ writer.writeCharacters(reader.text().toString());
501+ }
502+ textPos += textString.length();
503+ }
504+ if (token == QXmlStreamReader::EndElement) {
505+
506+ // The above logic would fail on an empty note
507+ if (reader.name() == "en-note" && !inserted) {
508+ writer.writeStartElement("div");
509+ writer.writeCharacters(text);
510+ writer.writeEndElement();
511+ }
512+
513+ writer.writeEndElement();
514+ }
515+ }
516+ m_enml = output;
517+}
518+
519+void EnmlDocument::insertLink(int position, const QString &url)
520+{
521+ qCDebug(dcEnml) << "Inserting Link at position" << position;
522+
523+ QXmlStreamReader reader(m_enml);
524+
525+ QString output;
526+ QXmlStreamWriter writer(&output);
527+ writer.writeStartDocument();
528+ writer.writeDTD("<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">");
529+
530+ int textPos = 0;
531+ bool inserted = false;
532+
533+ while (!reader.atEnd() && !reader.hasError()) {
534+ QXmlStreamReader::TokenType token = reader.readNext();
535+
536+ if (token == QXmlStreamReader::StartElement) {
537+ writer.writeStartElement(reader.name().toString());
538+ writer.writeAttributes(reader.attributes());
539+ }
540+
541+ if (token == QXmlStreamReader::Characters) {
542+ QString textString = reader.text().toString();
543+ if (textPos <= position && textPos + textString.length() > position) {
544+ writer.writeCharacters(textString.left(position - textPos));
545+
546+ writer.writeStartElement("div");
547+ writer.writeStartElement("a");
548+ writer.writeAttribute("href", url);
549+ writer.writeCharacters(url);
550+ writer.writeEndElement();
551+ writer.writeEndElement();
552+ inserted = true;
553+
554+ writer.writeCharacters(textString.right(textString.length() - (position - textPos)));
555+ } else {
556+ writer.writeCharacters(reader.text().toString());
557+ }
558+ textPos += textString.length();
559+ }
560+ if (token == QXmlStreamReader::EndElement) {
561+
562+ // The above logic would fail on an empty note
563+ if (reader.name() == "en-note" && !inserted) {
564+ writer.writeStartElement("div");
565+ writer.writeStartElement("a");
566+ writer.writeAttribute("href", url);
567+ writer.writeCharacters(url);
568+ writer.writeEndElement();
569 writer.writeEndElement();
570 }
571
572@@ -551,10 +664,8 @@
573 // Write all normal text inside <body> </body> to output
574 if (token == QXmlStreamReader::Characters) {
575 plaintext.append(reader.text().toString());
576- plaintext.append(' ');
577 }
578 }
579
580- plaintext.remove('\n').trimmed();
581 return plaintext;
582 }
583
584=== modified file 'src/libqtevernote/utils/enmldocument.h'
585--- src/libqtevernote/utils/enmldocument.h 2015-03-05 18:23:25 +0000
586+++ src/libqtevernote/utils/enmldocument.h 2015-06-22 08:35:28 +0000
587@@ -41,6 +41,10 @@
588 // Will insert the file described by hash at position in the plaintext string
589 void attachFile(int position, const QString &hash, const QString &type);
590
591+ // Convenience functions to insert some text without having a complete Editor attached
592+ void insertText(int position, const QString &text);
593+ void insertLink(int position, const QString &url);
594+
595 void markTodo(const QString &todoId, bool checked);
596
597 int renderWidth() const;

Subscribers

People subscribed via source and target branches