Merge lp:~kai-mast/friends-app/new-postview into lp:friends-app
- new-postview
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Robert Bruce Park |
Approved revision: | 139 |
Merged at revision: | 128 |
Proposed branch: | lp:~kai-mast/friends-app/new-postview |
Merge into: | lp:friends-app |
Diff against target: |
1836 lines (+827/-610) (has conflicts) 22 files modified
friends-app.pro (+2/-0) qml/AvatarImage.qml (+29/-0) qml/ConversationDetails.qml (+39/-101) qml/ConversationPage.qml (+111/-0) qml/EntryField.qml (+119/-0) qml/FavoriteIcon.qml (+50/-0) qml/Feed.qml (+33/-91) qml/FeedTab.qml (+23/-0) qml/FeedTabs.qml (+25/-0) qml/LinkDescription.qml (+20/-0) qml/LinkItem.qml (+24/-0) qml/LocationItem.qml (+25/-0) qml/Post.qml (+14/-0) qml/RefreshBar.qml (+3/-0) qml/RepeatIcon.qml (+43/-0) qml/ReplyArea.qml (+77/-0) qml/SenderAndTimeItem.qml (+36/-0) qml/StatusUpdateContent.qml (+44/-0) qml/StatusUpdateTile.qml (+52/-298) qml/ThreadView.qml (+25/-72) qml/friends-app.qml (+32/-46) qml/qml.pro (+1/-2) Text conflict in qml/Post.qml |
To merge this branch: | bzr merge lp:~kai-mast/friends-app/new-postview |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Bruce Park | Approve | ||
Ken VanDine | Needs Fixing | ||
Review via email: mp+197537@code.launchpad.net |
Commit message
Description of the change
This branch moves the post/conversation view to a new page. The reason for this is that the current details view get very cluttered if there are a lot of comments. Also, in the future this can be used to view images in high resolution (which I would like to work on after this branch has been merged).
I also adds the "reply all" button as requested. The code has been refactored, so this seems like a big merge but it it actually isn't that big.
Finally, I kept the amount of clicks needed to comment or view a post the same. So while this introduces a new view, the ease of use should stay the same.
Robert Bruce Park (robru) wrote : | # |
When I look at the new detail page, the contents are visible through the Tabs header.
Robert Bruce Park (robru) wrote : | # |
Looks like you need to add 'flickable: content' to the Page object in ConversationPag
Kai Mast (kai-mast) wrote : | # |
Thanks for the tip. I thought that was a bug in the toolkit :D
Ken VanDine (ken-vandine) wrote : | # |
A couple more notes:
* Your merge includes adding a untitled directory which doesn't look intentional.
* The text in the return to feed button should be translatable, add a i18n.tr().
* When the ConverstationPage gets loaded, it scrolls which cuts off the top of the content. Not sure why, but please look into that.
Kai Mast (kai-mast) wrote : | # |
Okay I will do all that later. I also have the problem that on the post view the protocol icons are not shown (which makes it really hard to figure out which social network you are posting to). But I also have this problem on trunk... Not sure where this comes from.
Kai Mast (kai-mast) wrote : | # |
Okay. I think I fixed everything except for the scrolling problem. Is there some SDK guru who can look at this? It really goes beyond my knowledge of the toolkit. Even setting content.contentY manually doesn't seem to work. I tried different anchors of the flickable towards the page (the documentation suggests fill), but that didn't change anything. I also tried increasing the height of the flickable content but that also didn't change anything.
The service property was used for some time to feed can only display specific services. I kept it, because there is a request for custom feeds and I may implement them some day (most of the functionality is already in place, one only needs to add a GUI to create and remove them).
Ken VanDine (ken-vandine) wrote : | # |
In trying to fix the scrolling issue, I went a little overboard... see my proposal to merge into your's https:/
Kai Mast (kai-mast) wrote : | # |
I just reviewed the branch again and everything looks fine now.
However, Ken changes aligned the reply area in the postview to the width of the text and not the width of the whole page. Was this on prupose? In my opinion, the reply area is a bit too small now.
Ken VanDine (ken-vandine) wrote : | # |
Sorry, that was not intentional
Kai Mast (kai-mast) wrote : | # |
It looks fine now in my opinion. I also made the reply area a little bigger as there is now space (maybe we should make its size dynamic depending on its content in the future).
Please review again and merge if you are happy with it.
Kai Mast (kai-mast) wrote : | # |
Just discovered that the header did not hide anymore when flicking the content. This bug is already occuring in trunk so I guess it was introduced by my Tabs branch.
To fix this, I made the Feed item a Page and set flickable: listView. Now, the header hides again.
But the content below the header is still clipped, which seems to be this bug: https:/
@Ken: would be nice if you could review it again :)
Robert Bruce Park (robru) wrote : | # |
What's the status of this one, Kai? I see some new commits. You ready for a review?
Kai Mast (kai-mast) wrote : | # |
I just fixed the clipping problem. The header now hides again when scrolling downwards.
So this is ready to be merged.
Robert Bruce Park (robru) wrote : | # |
Ok, took a look at this finally, sorry for the delay ;-)
Found some problems though. On the actual postview, there's a margin problem that I find quite ugly -- screenshot attached. Basically the replies should have a larger left margin than the parent, so that they look "set in" from the parent. What I'm seeing instead is zero margin, which is really ugly.
There's also a scrolling problem -- for messages where there are too many replies to fit on the screen, the whole thing just snaps to the top of the message. I can drag the window up to reveal more replies, but if I let go of the screen it just snaps back, so it's impossible to see all the replies.
Robert Bruce Park (robru) wrote : | # |
Sorry, here is the screenshot: http://
Also the timestamps should be aligned, but as you can see the children have a different alignment than the parent.
Kai Mast (kai-mast) wrote : | # |
Okay I think I screwed this up with my last commit. Thanks for spotting it. Will take a look at it tomorrow.
Kai Mast (kai-mast) wrote : | # |
@Rob: Did you have the problem that the qml files aren't copied into the build folder? Not sure if I broke something locally or on the branch.
Kai Mast (kai-mast) wrote : | # |
I think I resolved everything. Please check again :)
Robert Bruce Park (robru) wrote : | # |
Sorry for the delay in reviewing, been a bit busy with other projects!
It looks really good now! The scrolling and margin errors I previously mentioned are fixed. There is however one new issue that I didn't notice before: The top Tab header is overlapping the page content poorly. I remember seeing this issue before a long time ago, but this time it's affecting the main timeline view:
I confirmed that this is happening only in your branch, trunk does not exhibit this behavior. In trunk the header is always visible and always opaque, but in your branch the header scrolls away with the page content fine, but then when you scroll it back into view, the feed is visible behind it.
Kai Mast (kai-mast) wrote : | # |
Oh sorry I forgot to mention this. I am not sure what I am doing wrong there. The issue is described in a bug ( https:/
I have the feeling this is some UI toolkit bug. Maybe it is currently not possible to have a flickable that is not directly the child of the Page.
Robert Bruce Park (robru) wrote : | # |
Unfortunately my experience with QML is limited, and Ken is wayyy busier than I am so he can't look at this probably any time soon.
What confuses me is that you have at least one working flickable and one that doesn't. Surely you can compare them and see what's different about them, and make the broken one look more like the working one? Can you experiment with it a little bit further? Can you make the flickable be a direct child of the page somehow? (ie, is it possible to just have one flickable child and then change the content it displays, rather than trying to re-parent the flickable object to the page). Does that make sense?
These changes are looking really good, I'm excited to merge them, but I don't want to regress on this flickable bug since we already fixed it in friends-app back in July according to that bug you linked.
Kai Mast (kai-mast) wrote : | # |
well the difference is that one uses Tabs and one doesn't. I simplified the code here: http://
What I don't understand is that the flickable is handled correctly (the header hides) but the content is not clipped even though I set "clip: true".
I'll ask some of the QML/SDK "gurus". Maybe they can help.
For now, we could also set "flickable: null", so the behaviour would be the same as on trunk.
Keep in mind that this is just the first change. With the images branch (that is already mostly finished except for some convergence stuff) this will even look nicer ;)
Ken VanDine (ken-vandine) wrote : | # |
I just noticed the Tab usage doesn't include a page. The Tab expects the Page to be set to "page", not just declared there. So I think what's happening here is the flickable isn't properly trickling up the stack.
Something like this:
Tabs {
Tab {
page: Feed {
}
}
Kai Mast (kai-mast) wrote : | # |
You were right Ken!
So the Feed is a page now and we push the Tabs directly on the PageStack (was wrapped in a Page before). This resolved the clipping issue.
One small problem is left: When you jump to the top the header reappears and you are not completely at the top. There is no way to fix this right now. We need a way to reveal the header first and then jump to the top which is not possible right now.
But I think it looks really good now and is mergeable.
Robert Bruce Park (robru) wrote : | # |
Ok, this is looking really good to me now. I can't find anything wrong with it. Thanks!
Preview Diff
1 | === modified file 'friends-app.pro' |
2 | --- friends-app.pro 2013-04-08 14:56:46 +0000 |
3 | +++ friends-app.pro 2014-01-23 12:09:15 +0000 |
4 | @@ -24,3 +24,5 @@ |
5 | INSTALLS += accounts |
6 | |
7 | SUBDIRS += src qml po tests |
8 | + |
9 | + |
10 | |
11 | === added file 'qml/AvatarImage.qml' |
12 | --- qml/AvatarImage.qml 1970-01-01 00:00:00 +0000 |
13 | +++ qml/AvatarImage.qml 2014-01-23 12:09:15 +0000 |
14 | @@ -0,0 +1,29 @@ |
15 | +import QtQuick 2.0 |
16 | +import Ubuntu.Components 0.1 |
17 | + |
18 | +UbuntuShape { |
19 | + id: avatarImage |
20 | + height: units.dp(48) |
21 | + width: units.dp(48) |
22 | + |
23 | + // We can only specify top margin, as this is displayed in a Row element |
24 | + anchors { |
25 | + topMargin: units.gu(1) |
26 | + top: parent.top |
27 | + } |
28 | + |
29 | + image: Image { |
30 | + source: Qt.resolvedUrl(avatar) |
31 | + asynchronous: true |
32 | + smooth: true |
33 | + fillMode: Image.PreserveAspectCrop |
34 | + sourceSize.width: units.dp(48) |
35 | + } |
36 | + |
37 | + MouseArea { |
38 | + anchors.fill: avatarImage |
39 | + onClicked: { |
40 | + Qt.openUrlExternally(url); |
41 | + } |
42 | + } |
43 | +} |
44 | |
45 | === renamed file 'qml/StatusUpdateTileDetails.qml' => 'qml/ConversationDetails.qml' |
46 | --- qml/StatusUpdateTileDetails.qml 2013-08-20 15:17:54 +0000 |
47 | +++ qml/ConversationDetails.qml 2014-01-23 12:09:15 +0000 |
48 | @@ -22,7 +22,11 @@ |
49 | |
50 | Item { |
51 | id: details |
52 | - height: visible ? childrenRect.height + units.gu(1) : 0 |
53 | + height: childrenRect.height + units.gu(1) |
54 | + |
55 | + function isMicroblogging() { |
56 | + return (service === "twitter") || (service === "identica") |
57 | + } |
58 | |
59 | FriendsDispatcher { |
60 | id: friends |
61 | @@ -41,12 +45,18 @@ |
62 | |
63 | Item { |
64 | id: pictureWrapper |
65 | - anchors.right: parent.right |
66 | + |
67 | + anchors { |
68 | + right: parent.right |
69 | + top: parent.top |
70 | + } |
71 | + |
72 | width: parent.width - units.gu(8) |
73 | height: childrenRect.height |
74 | |
75 | Image { |
76 | id: pictureImage |
77 | + anchors.fill: parent |
78 | visible: linkPicture.length > 0 && status == Image.Ready |
79 | source: Qt.resolvedUrl(linkPicture) |
80 | asynchronous: true |
81 | @@ -73,9 +83,9 @@ |
82 | anchors.top: pictureWrapper.bottom |
83 | source: { |
84 | return "http://open.mapquestapi.com/staticmap/v4/getmap?size=" + |
85 | - Math.round(Math.min(parent.width * 0.8, units.gu(20))) + "," + |
86 | - Math.round(Math.min(parent.width * 0.8, units.gu(20))) + |
87 | - "&zoom=12¢er=" + latitude + "," + longitude |
88 | + Math.round(Math.min(parent.width * 0.8, units.gu(20))) + "," + |
89 | + Math.round(Math.min(parent.width * 0.8, units.gu(20))) + |
90 | + "&zoom=12¢er=" + latitude + "," + longitude |
91 | |
92 | } |
93 | visible: location.length > 0 |
94 | @@ -84,7 +94,6 @@ |
95 | ThreadView { |
96 | id: threadView |
97 | width: parent.width |
98 | - height: childrenRect.height |
99 | |
100 | anchors { |
101 | top: mapimage.bottom |
102 | @@ -94,117 +103,46 @@ |
103 | } |
104 | |
105 | Item { |
106 | - id: entry |
107 | - property var lastCursorPosition |
108 | - height: childrenRect.height |
109 | + id: bottom |
110 | anchors { |
111 | + left: parent.left |
112 | + right: parent.right |
113 | top: threadView.bottom |
114 | - left: parent.left |
115 | - right: parent.right |
116 | - topMargin: units.gu(2) |
117 | } |
118 | |
119 | - TextArea { |
120 | - id: entryArea |
121 | + height: childrenRect.height |
122 | + |
123 | + ReplyArea { |
124 | + id: replyArea |
125 | + |
126 | anchors { |
127 | + top: parent.top |
128 | left: parent.left |
129 | - leftMargin: units.gu(2) |
130 | - right: sendButton.left |
131 | - rightMargin: units.gu(1) |
132 | - bottomMargin: units.gu(1.5) |
133 | - } |
134 | - // send message if return was pressed |
135 | - Keys.onReturnPressed: sendButton.clicked(null) |
136 | - Keys.onEscapePressed: text = "" |
137 | - height: units.gu(4) |
138 | - placeholderText: i18n.tr("Reply") |
139 | - autoSize: true |
140 | - maximumLineCount: 0 |
141 | - color: text.length < 140 ? "gray" : "red" |
142 | - textFormat: TextEdit.PlainText |
143 | - contentWidth: width - units.gu(5) |
144 | - onActiveFocusChanged: { |
145 | - if (activeFocus) { |
146 | - if (service == "twitter" || service == "identica") { |
147 | - if (entryArea.text.search("@"+senderNick) == -1) { |
148 | - entryArea.text = "@"+senderNick+" "; |
149 | - cursorPosition = text.length; |
150 | - } |
151 | - } |
152 | - } |
153 | - } |
154 | - |
155 | - onTextChanged: { |
156 | - var enteredText = text.substring(entry.lastCursorPosition, cursorPosition); |
157 | - if (enteredText.substring(0,4) == "http") { |
158 | - var shortUrl = friends.urlShorten(enteredText); |
159 | - if (shortUrl.length > 4) { |
160 | - text = text.replace (enteredText, shortUrl); |
161 | - cursorPosition = text.length; |
162 | - } |
163 | - } |
164 | - entry.lastCursorPosition = cursorPosition; |
165 | - } |
166 | - |
167 | - Item { |
168 | - id: counter |
169 | - anchors { |
170 | - right: entryArea.right |
171 | - top: entryArea.top |
172 | - rightMargin: units.gu(1) |
173 | - } |
174 | - height: units.gu(4) |
175 | - width: childrenRect.width |
176 | - visible: entryArea.text.length > 0 ? true : false |
177 | - Text { |
178 | - text: 140 - entryArea.text.length |
179 | - font.bold: true |
180 | - opacity: 0.3 |
181 | - } |
182 | - } |
183 | - Button { |
184 | - id: clearButton |
185 | - anchors { |
186 | - right: entryArea.right |
187 | - bottom: entryArea.bottom |
188 | - rightMargin: units.gu(1) |
189 | - } |
190 | - width: units.gu(2) |
191 | - height: units.gu(2) |
192 | - visible: entryArea.text.length > 0 ? true : false |
193 | - iconSource: "images/clear-search.png" |
194 | - color: "transparent" |
195 | - opacity: 0.3 |
196 | - onClicked: { |
197 | - entryArea.text = ""; |
198 | - } |
199 | + right: parent.right |
200 | + topMargin: units.gu(2) |
201 | } |
202 | } |
203 | |
204 | + // Back button (much faster than using the toolbar) |
205 | + // At the bottom so it doesn't steal any space (content is flickable) |
206 | Button { |
207 | - id: sendButton |
208 | + id: returnButton |
209 | + |
210 | anchors { |
211 | + top: replyArea.bottom |
212 | + left: parent.left |
213 | right: parent.right |
214 | - rightMargin: units.gu(1) |
215 | - bottom: entryArea.bottom |
216 | + margins: units.gu(5) |
217 | } |
218 | - width: units.gu(9) |
219 | - height: units.gu(4) |
220 | - text: i18n.tr("Send") |
221 | - color: main.headerColor |
222 | + |
223 | + text: i18n.tr("Return to feed") |
224 | + |
225 | + height: units.gu(6) |
226 | + |
227 | onClicked: { |
228 | - console.log ("Reply to " + messageId + " from " + accountId + ": " + entryArea.text); |
229 | - sendSpinner.visible = true |
230 | - friends.sendReplyAsync(accountId, messageId, entryArea.text); |
231 | + conversationPage.close() |
232 | } |
233 | } |
234 | - |
235 | - ActivityIndicator { |
236 | - id: sendSpinner |
237 | - anchors.centerIn: entryArea |
238 | - visible: false |
239 | - running: visible |
240 | - } |
241 | } |
242 | |
243 | Component { |
244 | |
245 | === added file 'qml/ConversationPage.qml' |
246 | --- qml/ConversationPage.qml 1970-01-01 00:00:00 +0000 |
247 | +++ qml/ConversationPage.qml 2014-01-23 12:09:15 +0000 |
248 | @@ -0,0 +1,111 @@ |
249 | +import QtQuick 2.0 |
250 | +import Ubuntu.Components 0.1 |
251 | + |
252 | +Page { |
253 | + title: i18n.tr("Conversation") |
254 | + id: conversationPage |
255 | + |
256 | + flickable: content |
257 | + |
258 | + function setContent(tile) { |
259 | + this.tile = tile |
260 | + |
261 | + //Assign all values of the tile to the conversation page |
262 | + //This way we can have one page for all tiles |
263 | + liked = tile.liked |
264 | + likeCount = tile.likeCount |
265 | + fromMe = tile.fromMe |
266 | + service = tile.service |
267 | + linkUrl = tile.linkUrl |
268 | + linkName = tile.linkName |
269 | + linkDescription = tile.linkDescription |
270 | + avatar = tile.avatar |
271 | + message = tile.message |
272 | + timeString = tile.timeString |
273 | + location = tile.location |
274 | + sender = tile.sender |
275 | + linkPicture = tile.linkPicture |
276 | + latitude = tile.latitude |
277 | + longitude = tile.longitude |
278 | + messageId = tile.messageId |
279 | + stream = tile.stream |
280 | + senderNick = tile.senderNick |
281 | + url = tile.url |
282 | + } |
283 | + |
284 | + //Forward properties from StatusUpdateTile |
285 | + property bool liked |
286 | + property int likeCount |
287 | + property bool fromMe |
288 | + property string service |
289 | + property string linkUrl |
290 | + property string linkName |
291 | + property string linkDescription |
292 | + property variant avatar |
293 | + property string message |
294 | + property variant tile |
295 | + property string timeString |
296 | + property string location |
297 | + property string sender |
298 | + property string linkPicture |
299 | + property int latitude |
300 | + property int longitude |
301 | + property string messageId |
302 | + property string stream |
303 | + property string senderNick |
304 | + property string url |
305 | + |
306 | + function close() { |
307 | + main.closeConversation(); |
308 | + } |
309 | + |
310 | + Flickable { |
311 | + id: content |
312 | + |
313 | + anchors { |
314 | + fill: parent |
315 | + margins: units.gu(2) |
316 | + } |
317 | + |
318 | + contentHeight: column.height |
319 | + |
320 | + flickableDirection: Flickable.VerticalFlick |
321 | + boundsBehavior: Flickable.DragOverBounds |
322 | + |
323 | + // Looks much nicer on broad displays |
324 | + width: Math.min(parent.width, units.gu(80)) |
325 | + |
326 | + Column { |
327 | + id: column |
328 | + |
329 | + width: parent.width |
330 | + height: childrenRect.height |
331 | + |
332 | + Row { |
333 | + width: parent.width |
334 | + spacing: units.gu(1) |
335 | + |
336 | + AvatarImage { |
337 | + id: avatarImage |
338 | + } |
339 | + |
340 | + StatusUpdateContent { |
341 | + id: statusUpdateContent |
342 | + |
343 | + width: parent.width - avatarImage.width |
344 | + showDetails: true |
345 | + } |
346 | + } |
347 | + |
348 | + ConversationDetails { |
349 | + width: parent.width |
350 | + |
351 | + id: details |
352 | + } |
353 | + } |
354 | + |
355 | + } |
356 | + Keys.onEscapePressed: { |
357 | + conversationPage.close() |
358 | + } |
359 | +} |
360 | |
361 | === added file 'qml/EntryField.qml' |
362 | --- qml/EntryField.qml 1970-01-01 00:00:00 +0000 |
363 | +++ qml/EntryField.qml 2014-01-23 12:09:15 +0000 |
364 | @@ -0,0 +1,119 @@ |
365 | +import QtQuick 2.0 |
366 | +import Ubuntu.Components 0.1 |
367 | + |
368 | +Item { |
369 | + id: entryField |
370 | + |
371 | + onVisibleChanged: { |
372 | + if(visible) |
373 | + entryArea.forceActiveFocus() |
374 | + } |
375 | + |
376 | + property var lastCursorPosition |
377 | + anchors.fill: parent |
378 | + |
379 | + function setText(text) { |
380 | + entryArea.text = text + " " |
381 | + entryArea.cursorPosition = text.length + 1 |
382 | + } |
383 | + |
384 | + TextArea { |
385 | + id: entryArea |
386 | + anchors { |
387 | + top: parent.top |
388 | + bottom: parent.bottom |
389 | + left: parent.left |
390 | + leftMargin: units.gu(1) |
391 | + right: sendButton.left |
392 | + rightMargin: units.gu(1) |
393 | + } |
394 | + |
395 | + // send message if return was pressed |
396 | + Keys.onReturnPressed: sendButton.clicked(null) |
397 | + Keys.onEscapePressed: text = "" |
398 | + height: units.gu(4) |
399 | + placeholderText: i18n.tr("Reply") |
400 | + autoSize: true |
401 | + maximumLineCount: 0 |
402 | + color: text.length < 140 ? "gray" : "red" |
403 | + textFormat: TextEdit.PlainText |
404 | + contentWidth: width - units.gu(5) |
405 | + |
406 | + onTextChanged: { |
407 | + var enteredText = text.substring(entryField.lastCursorPosition, cursorPosition); |
408 | + if (enteredText.substring(0,4) == "http") { |
409 | + var shortUrl = friends.urlShorten(enteredText); |
410 | + if (shortUrl.length > 4) { |
411 | + text = text.replace (enteredText, shortUrl); |
412 | + cursorPosition = text.length; |
413 | + } |
414 | + } |
415 | + entryField.lastCursorPosition = cursorPosition; |
416 | + } |
417 | + |
418 | + Item { |
419 | + id: counter |
420 | + anchors { |
421 | + right: entryArea.right |
422 | + top: entryArea.top |
423 | + rightMargin: units.gu(1) |
424 | + } |
425 | + height: units.gu(4) |
426 | + width: childrenRect.width |
427 | + |
428 | + visible: (isMicroblogging() && entryArea.text.length > 0) ? true : false |
429 | + Text { |
430 | + text: 140 - entryArea.text.length |
431 | + font.bold: true |
432 | + opacity: 0.3 |
433 | + } |
434 | + } |
435 | + } |
436 | + |
437 | + Button { |
438 | + id: sendButton |
439 | + |
440 | + anchors { |
441 | + right: parent.right |
442 | + rightMargin: units.gu(1) |
443 | + top: parent.top |
444 | + } |
445 | + |
446 | + width: units.gu(9) |
447 | + height: units.gu(4) |
448 | + text: i18n.tr("Send") |
449 | + color: main.headerColor |
450 | + onClicked: { |
451 | + console.log ("Reply to " + messageId + " from " + accountId + ": " + entryArea.text); |
452 | + sendSpinner.visible = true |
453 | + friends.sendReplyAsync(accountId, messageId, entryArea.text); |
454 | + } |
455 | + } |
456 | + Button { |
457 | + id: clearButton |
458 | + |
459 | + anchors { |
460 | + right: parent.right |
461 | + top: sendButton.bottom |
462 | + rightMargin: units.gu(1) |
463 | + topMargin: units.gu(1) |
464 | + } |
465 | + |
466 | + height: sendButton.height |
467 | + width: sendButton.width |
468 | + iconSource: "images/clear-search.png" |
469 | + color: main.headerColor |
470 | + |
471 | + onClicked: { |
472 | + entryArea.text = "" |
473 | + entryField.visible = false |
474 | + } |
475 | + } |
476 | + |
477 | + ActivityIndicator { |
478 | + id: sendSpinner |
479 | + anchors.centerIn: entryArea |
480 | + visible: false |
481 | + running: visible |
482 | + } |
483 | +} |
484 | |
485 | === added file 'qml/FavoriteIcon.qml' |
486 | --- qml/FavoriteIcon.qml 1970-01-01 00:00:00 +0000 |
487 | +++ qml/FavoriteIcon.qml 2014-01-23 12:09:15 +0000 |
488 | @@ -0,0 +1,50 @@ |
489 | +import QtQuick 2.0 |
490 | +import Ubuntu.Components 0.1 |
491 | + |
492 | +Row { |
493 | + id: favorite |
494 | + |
495 | + height: favoriteIcon.height + units.gu(1) |
496 | + anchors { |
497 | + left: parent.left |
498 | + right: parent.right |
499 | + bottomMargin: units.gu(1) |
500 | + } |
501 | + spacing: units.dp(3) |
502 | + |
503 | + Image { |
504 | + id: favoriteIcon |
505 | + width: units.gu(3) |
506 | + height: units.gu(3) |
507 | + opacity: liked ? 1.0 : 0.1 |
508 | + |
509 | + source: "images/favorite.png" |
510 | + asynchronous: true |
511 | + MouseArea { |
512 | + anchors.fill: favoriteIcon |
513 | + onClicked: { |
514 | + favoriteSpinner.visible = true; |
515 | + if (liked) { |
516 | + friends.unlikeAsync(accountId, messageId); |
517 | + } |
518 | + else { |
519 | + friends.likeAsync(accountId, messageId); |
520 | + } |
521 | + } |
522 | + } |
523 | + ActivityIndicator { |
524 | + id: favoriteSpinner |
525 | + anchors.centerIn: parent |
526 | + width: parent.width |
527 | + visible: false |
528 | + running: visible |
529 | + } |
530 | + } |
531 | + |
532 | + Label { |
533 | + id: likesLabel |
534 | + text: (likeCount > 0) ? likeCount + " " + likesString : "" |
535 | + fontSize: "small" |
536 | + color: Theme.palette.normal.baseText |
537 | + } |
538 | +} |
539 | |
540 | === modified file 'qml/Feed.qml' |
541 | --- qml/Feed.qml 2013-11-03 10:35:46 +0000 |
542 | +++ qml/Feed.qml 2014-01-23 12:09:15 +0000 |
543 | @@ -21,9 +21,15 @@ |
544 | import Ubuntu.Components.ListItems 0.1 as ListItem |
545 | import Friends 0.2 |
546 | |
547 | -Tab { |
548 | +Page { |
549 | + anchors.fill: parent |
550 | + clip: true |
551 | + |
552 | + property alias flickable: listView |
553 | + |
554 | property alias stream: streamModel.stream |
555 | - property int unseen: 0 |
556 | + property alias service: streamModel.service |
557 | + |
558 | property string search |
559 | |
560 | /* Jump to the bottom of the view and set the currentIndex */ |
561 | @@ -38,12 +44,8 @@ |
562 | listView.currentIndex = 0; |
563 | } |
564 | |
565 | - onUnseenChanged: { |
566 | - updatesText.text = unseen == 1 ? (unseen + i18n.tr(" new update")) : (unseen + i18n.tr(" new updates")); |
567 | - } |
568 | - |
569 | Component.onCompleted: { |
570 | - listView.focus = true; |
571 | + listView.focus = true |
572 | } |
573 | |
574 | Timer { |
575 | @@ -57,18 +59,12 @@ |
576 | id: statusDelegate |
577 | |
578 | ListItem.Empty { |
579 | - anchors { |
580 | - left: parent.left |
581 | - right: parent.right |
582 | - } |
583 | height: content.height |
584 | |
585 | StatusUpdateTile { |
586 | id: content |
587 | height: childrenRect.height |
588 | - ListView.onAdd: SequentialAnimation { |
589 | - ScriptAction { script: {unseen++; console.log(column_0[0][0] + " " + column_0[0][1] + " " + column_0[0][2]) }} |
590 | - } |
591 | + |
592 | Connections { |
593 | target: updateTimestampsTimer |
594 | onTriggered: { |
595 | @@ -84,23 +80,12 @@ |
596 | objectName: "streamModel" |
597 | } |
598 | |
599 | - onStreamChanged: { |
600 | - console.log("stream changed to " + stream); |
601 | - streamModel.stream = stream; |
602 | - } |
603 | - |
604 | - onSearchChanged: { |
605 | - console.log ("onSearchChanged"); |
606 | - } |
607 | - |
608 | RefreshBar { |
609 | id: refreshBar |
610 | |
611 | - anchors.top: parent.top |
612 | - anchors.topMargin: units.gu(1) |
613 | + opacity: visible ? 1.0 : 0.0 |
614 | + visible: listView.dragging && listView.onTop |
615 | |
616 | - opacity: 0 |
617 | - visible: opacity > 0 ? true : false |
618 | Behavior on opacity { |
619 | NumberAnimation { duration: 200; } |
620 | } |
621 | @@ -109,13 +94,13 @@ |
622 | ListView { |
623 | id: listView |
624 | objectName: "listView" |
625 | - height: parent.height - toolbar.height - units.gu(2) |
626 | - width: parent.width |
627 | + |
628 | anchors { |
629 | - top: updatesBanner.bottom |
630 | - topMargin: units.gu(1) |
631 | fill: parent |
632 | } |
633 | + |
634 | + property bool onTop : listView.contentY <= -main.header.height |
635 | + |
636 | opacity: 1 |
637 | spacing: units.gu(1) |
638 | model: streamModel |
639 | @@ -128,6 +113,7 @@ |
640 | |
641 | preferredHighlightBegin: 0 |
642 | preferredHighlightEnd: preferredHighlightBegin |
643 | + |
644 | /* We need highlightRangeMode for keyboard navigation, |
645 | * but it forces the page header to stay hidden |
646 | * https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1201936 |
647 | @@ -141,81 +127,37 @@ |
648 | } |
649 | } |
650 | |
651 | - /* Ensure we collapse details when scrolling */ |
652 | - onFlickStarted: { |
653 | - if (listView.currentItem.state != "List") |
654 | - listView.currentItem.state = "List"; |
655 | - } |
656 | - |
657 | /* Expand details on enter */ |
658 | Keys.onReturnPressed: { |
659 | - if (listView.currentItem.state != "Details") |
660 | - listView.currentItem.state = "Details"; |
661 | - } |
662 | - |
663 | - /* Collapse details on escape */ |
664 | - Keys.onEscapePressed: { |
665 | - if (listView.currentItem.state != "List") |
666 | - listView.currentItem.state = "List"; |
667 | + listView.currentItem.showConversation() |
668 | } |
669 | |
670 | /* Handle home and end nav */ |
671 | Keys.onPressed: { |
672 | - if (event.key == Qt.Key_Home) { |
673 | + if (event.key === Qt.Key_Home) { |
674 | jumpToTop(); |
675 | } |
676 | - else if (event.key == Qt.Key_End) { |
677 | + else if (event.key === Qt.Key_End) { |
678 | jumpToBottom(); |
679 | } |
680 | } |
681 | - |
682 | - onDragStarted: { |
683 | - if (listView.atYBeginning) |
684 | - refreshBar.opacity = 1.0; |
685 | - } |
686 | - onDragEnded: { |
687 | - refreshBar.opacity = 0 |
688 | - if (refreshBar.refreshed) { |
689 | - console.log ("Refreshed"); |
690 | - friends.refresh (); |
691 | - } |
692 | - } |
693 | - } |
694 | - |
695 | - Item { |
696 | - id: updatesBanner |
697 | - anchors { |
698 | - top: parent.top |
699 | - left: parent.left |
700 | - right: parent.right |
701 | - } |
702 | - visible: unseen > 0 |
703 | - height: visible ? units.gu(3) : 0 |
704 | - Text { |
705 | - id: updatesText |
706 | - anchors { |
707 | - centerIn: parent |
708 | - bottom: parent.bottom |
709 | - } |
710 | - text: "" |
711 | - font.family: "Ubuntu" |
712 | - font.bold: true |
713 | - font.pointSize: 12 |
714 | - color: Theme.palette.normal.baseText |
715 | - } |
716 | - MouseArea { |
717 | - anchors.fill: parent |
718 | - onClicked: { |
719 | - unseen = 0; |
720 | - updatesText.text = ""; |
721 | - updatesBanner.visible = false; |
722 | - jumpToTop(); |
723 | - } |
724 | - } |
725 | } |
726 | |
727 | Scrollbar { |
728 | flickableItem: listView |
729 | align: Qt.AlignTrailing |
730 | } |
731 | + |
732 | + |
733 | + tools: ToolbarItems { |
734 | + ToolbarButton { |
735 | + action: topAction |
736 | + } |
737 | + ToolbarButton { |
738 | + action: refreshAction |
739 | + } |
740 | + ToolbarButton { |
741 | + action: postAction |
742 | + } |
743 | + } |
744 | } |
745 | |
746 | === added file 'qml/FeedTab.qml' |
747 | --- qml/FeedTab.qml 1970-01-01 00:00:00 +0000 |
748 | +++ qml/FeedTab.qml 2014-01-23 12:09:15 +0000 |
749 | @@ -0,0 +1,23 @@ |
750 | +import QtQuick 2.0 |
751 | +import Ubuntu.Components 0.1 |
752 | + |
753 | +Tab { |
754 | + clip: true |
755 | + |
756 | + //property alias flickable: feed.flickable |
757 | + |
758 | + property string stream |
759 | + |
760 | + function jumpToTop() { |
761 | + feed.jumpToTop() |
762 | + } |
763 | + |
764 | + page: feed |
765 | + |
766 | + Feed { |
767 | + anchors.fill: parent |
768 | + |
769 | + id: feed |
770 | + stream: parent.stream |
771 | + } |
772 | +} |
773 | |
774 | === added file 'qml/FeedTabs.qml' |
775 | --- qml/FeedTabs.qml 1970-01-01 00:00:00 +0000 |
776 | +++ qml/FeedTabs.qml 2014-01-23 12:09:15 +0000 |
777 | @@ -0,0 +1,25 @@ |
778 | +import QtQuick 2.0 |
779 | +import Ubuntu.Components 0.1 |
780 | + |
781 | +Tabs { |
782 | + id: feedTabs |
783 | + clip: true |
784 | + |
785 | + function jumpToTop() { |
786 | + feedTabs.selectedTab.jumpToTop() |
787 | + } |
788 | + |
789 | + FeedTab { |
790 | + id: timeline |
791 | + |
792 | + title: i18n.tr("Timeline") |
793 | + stream: "" //all |
794 | + } |
795 | + |
796 | + FeedTab { |
797 | + id: mentions |
798 | + |
799 | + title: i18n.tr("Mentions") |
800 | + stream: "mentions" |
801 | + } |
802 | +} |
803 | |
804 | === added file 'qml/LinkDescription.qml' |
805 | --- qml/LinkDescription.qml 1970-01-01 00:00:00 +0000 |
806 | +++ qml/LinkDescription.qml 2014-01-23 12:09:15 +0000 |
807 | @@ -0,0 +1,20 @@ |
808 | +import QtQuick 2.0 |
809 | +import Ubuntu.Components 0.1 |
810 | + |
811 | +// Show the description link of a status update (if any) |
812 | +// Should only be used by StatusUpdateContent |
813 | +Label { |
814 | + id: linkDesc |
815 | + |
816 | + anchors { |
817 | + right: parent.right |
818 | + left: parent.left |
819 | + } |
820 | + |
821 | + text: linkDescription |
822 | + elide: Text.ElideRight |
823 | + visible: linkDescription.length > 0 |
824 | + wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
825 | + textFormat: Text.StyledText |
826 | + onLinkActivated: Qt.openUrlExternally(link) |
827 | +} |
828 | |
829 | === added file 'qml/LinkItem.qml' |
830 | --- qml/LinkItem.qml 1970-01-01 00:00:00 +0000 |
831 | +++ qml/LinkItem.qml 2014-01-23 12:09:15 +0000 |
832 | @@ -0,0 +1,24 @@ |
833 | +import QtQuick 2.0 |
834 | +import Ubuntu.Components 0.1 |
835 | + |
836 | +// Show link of a status update (if any) |
837 | +// Should only be used by StatusUpdateContent |
838 | +UbuntuShape { |
839 | + height: linkNameShape.height + units.gu(1) |
840 | + width: linkNameShape.width + units.gu(2) |
841 | + visible: linkName.length > 0 |
842 | + Label { |
843 | + id: linkNameShape |
844 | + anchors.centerIn: parent |
845 | + text: linkName |
846 | + wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
847 | + } |
848 | + MouseArea { |
849 | + anchors.fill: parent |
850 | + onClicked: { |
851 | + if (linkUrl.length > 0) |
852 | + Qt.openUrlExternally(linkUrl); |
853 | + |
854 | + } |
855 | + } |
856 | +} |
857 | |
858 | === added file 'qml/LocationItem.qml' |
859 | --- qml/LocationItem.qml 1970-01-01 00:00:00 +0000 |
860 | +++ qml/LocationItem.qml 2014-01-23 12:09:15 +0000 |
861 | @@ -0,0 +1,25 @@ |
862 | +import QtQuick 2.0 |
863 | +import Ubuntu.Components 0.1 |
864 | + |
865 | +// Show location information of a status update (if any) |
866 | +// Should only be used by StatusUpdateContent |
867 | +UbuntuShape { |
868 | + height: locationName.height + units.gu(1) |
869 | + width: locationName.width + units.gu(2) |
870 | + visible: location.length > 0 |
871 | + Label { |
872 | + id: locationName |
873 | + anchors.centerIn: parent |
874 | + text: location |
875 | + wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
876 | + } |
877 | + MouseArea { |
878 | + anchors.fill: parent |
879 | + onClicked: { |
880 | + if (url.length > 0) |
881 | + { |
882 | + Qt.openUrlExternally(url); |
883 | + } |
884 | + } |
885 | + } |
886 | +} |
887 | |
888 | === modified file 'qml/Post.qml' |
889 | --- qml/Post.qml 2013-12-12 18:08:32 +0000 |
890 | +++ qml/Post.qml 2014-01-23 12:09:15 +0000 |
891 | @@ -236,7 +236,21 @@ |
892 | ListItem.Standard { |
893 | text: displayName |
894 | opacity: sw.checked ? 1.0 : 0.3 |
895 | +<<<<<<< TREE |
896 | iconName: accountIcon |
897 | +======= |
898 | + |
899 | + Component.onCompleted: { |
900 | + //this fixes height if no icon can be found |
901 | + if(icon) { |
902 | + height = icon.height |
903 | + } |
904 | + } |
905 | + |
906 | + //Show no icon if none is available |
907 | + iconSource: iconName.length > 0 ? "image://gicon/"+iconName : "" |
908 | + |
909 | +>>>>>>> MERGE-SOURCE |
910 | control: CheckBox { |
911 | id: sw |
912 | checked: sendEnabled |
913 | |
914 | === modified file 'qml/RefreshBar.qml' |
915 | --- qml/RefreshBar.qml 2013-08-05 17:39:52 +0000 |
916 | +++ qml/RefreshBar.qml 2014-01-23 12:09:15 +0000 |
917 | @@ -21,6 +21,9 @@ |
918 | Item { |
919 | height: units.gu(4) |
920 | anchors { |
921 | + topMargin: main.header.height + units.gu(1) // show it below the header |
922 | + |
923 | + top: parent.top |
924 | left: parent.left |
925 | right: parent.right |
926 | } |
927 | |
928 | === added file 'qml/RepeatIcon.qml' |
929 | --- qml/RepeatIcon.qml 1970-01-01 00:00:00 +0000 |
930 | +++ qml/RepeatIcon.qml 2014-01-23 12:09:15 +0000 |
931 | @@ -0,0 +1,43 @@ |
932 | +import QtQuick 2.0 |
933 | +import Ubuntu.Components 0.1 |
934 | + |
935 | +Item { |
936 | + id: repeatIcon |
937 | + |
938 | + anchors { |
939 | + right: parent.right |
940 | + bottom: parent.bottom |
941 | + rightMargin: units.gu(2) |
942 | + bottomMargin: units.gu(1) |
943 | + } |
944 | + |
945 | + height: fromMe ? 0 : childrenRect.height |
946 | + |
947 | + // FIXME: Use features API instead |
948 | + visible: showDetails && |
949 | + (service == "twitter" || service == "identica") && !fromMe && stream !== "private" |
950 | + |
951 | + Image { |
952 | + id: repeatIconImage |
953 | + width: units.gu(3) |
954 | + height: units.gu(3) |
955 | + source: "images/share.png" |
956 | + asynchronous: true |
957 | + visible: retweetSpinner.visble ? false : true |
958 | + MouseArea { |
959 | + anchors.fill: parent |
960 | + onClicked: { |
961 | + console.log ("Retweeting " + messageId + " from " + accountId); |
962 | + retweetSpinner.visible = true |
963 | + friends.retweetAsync(accountId, messageId); |
964 | + } |
965 | + } |
966 | + ActivityIndicator { |
967 | + id: retweetSpinner |
968 | + anchors.centerIn: parent |
969 | + //width: parent.width |
970 | + visible: false |
971 | + running: visible |
972 | + } |
973 | + } |
974 | +} |
975 | |
976 | === added file 'qml/ReplyArea.qml' |
977 | --- qml/ReplyArea.qml 1970-01-01 00:00:00 +0000 |
978 | +++ qml/ReplyArea.qml 2014-01-23 12:09:15 +0000 |
979 | @@ -0,0 +1,77 @@ |
980 | +import QtQuick 2.0 |
981 | +import Ubuntu.Components 0.1 |
982 | + |
983 | +// This element shows either the reply/reply all buttons |
984 | +// Or a field to compose the reply |
985 | +Item { |
986 | + id: replyArea |
987 | + height: units.gu(4) |
988 | + |
989 | + Button { |
990 | + id: replyButton |
991 | + visible: !entryField.visible |
992 | + width: units.gu(10) |
993 | + |
994 | + anchors { |
995 | + leftMargin: units.gu(5) |
996 | + left: parent.left |
997 | + top: parent.top |
998 | + bottom: parent.bottom |
999 | + } |
1000 | + |
1001 | + text: isMicroblogging() ? i18n.tr("Reply"): i18n.tr("Comment") |
1002 | + |
1003 | + property var lastCursorPosition |
1004 | + height: childrenRect.height |
1005 | + |
1006 | + onClicked : { |
1007 | + entryField.visible = true |
1008 | + |
1009 | + if(isMicroblogging()) |
1010 | + entryField.setText("@"+senderNick) |
1011 | + } |
1012 | + } |
1013 | + |
1014 | + Button { |
1015 | + id: replyAllButton |
1016 | + |
1017 | + // Only show if we are using micrblogging. |
1018 | + visible: !entryField.visible && isMicroblogging() |
1019 | + width: replyButton.width |
1020 | + |
1021 | + anchors { |
1022 | + left: replyButton.right |
1023 | + leftMargin: units.gu(1) |
1024 | + top: parent.top |
1025 | + bottom: parent.bottom |
1026 | + } |
1027 | + |
1028 | + text: i18n.tr("Reply All") |
1029 | + |
1030 | + property var lastCursorPosition |
1031 | + height: childrenRect.height |
1032 | + |
1033 | + onClicked : { |
1034 | + entryField.visible = true |
1035 | + |
1036 | + if(isMicroblogging()) { |
1037 | + var text = "@"+senderNick; |
1038 | + |
1039 | + var nicks = message.match(/[@][\w]+/g); |
1040 | + |
1041 | + if (nicks !== null) { |
1042 | + for(var i = 0; i < nicks.length; ++i) { |
1043 | + text += " " + nicks[i]; |
1044 | + } |
1045 | + } |
1046 | + |
1047 | + entryField.setText(text) |
1048 | + } |
1049 | + } |
1050 | + } |
1051 | + |
1052 | + EntryField { |
1053 | + id: entryField |
1054 | + visible: false |
1055 | + } |
1056 | +} |
1057 | |
1058 | === added file 'qml/SenderAndTimeItem.qml' |
1059 | --- qml/SenderAndTimeItem.qml 1970-01-01 00:00:00 +0000 |
1060 | +++ qml/SenderAndTimeItem.qml 2014-01-23 12:09:15 +0000 |
1061 | @@ -0,0 +1,36 @@ |
1062 | +import QtQuick 2.0 |
1063 | +import Ubuntu.Components 0.1 |
1064 | + |
1065 | +// Shows sender and time of a status update |
1066 | +// Should only be used by StatusUpdateContent |
1067 | +Item { |
1068 | + anchors { |
1069 | + left: parent.left |
1070 | + right: parent.right |
1071 | + rightMargin: units.gu(1) |
1072 | + } |
1073 | + height: childrenRect.height |
1074 | + |
1075 | + Label { |
1076 | + text: sender |
1077 | + fontSize: "medium" |
1078 | + font.bold: true |
1079 | + |
1080 | + anchors { |
1081 | + left: parent.left |
1082 | + right: time.left |
1083 | + } |
1084 | + wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1085 | + } |
1086 | + |
1087 | + Label { |
1088 | + id: time |
1089 | + anchors { |
1090 | + right: parent.right |
1091 | + } |
1092 | + |
1093 | + text: timeString |
1094 | + fontSize: "small" |
1095 | + color: Theme.palette.normal.baseText |
1096 | + } |
1097 | +} |
1098 | |
1099 | === added file 'qml/StatusUpdateContent.qml' |
1100 | --- qml/StatusUpdateContent.qml 1970-01-01 00:00:00 +0000 |
1101 | +++ qml/StatusUpdateContent.qml 2014-01-23 12:09:15 +0000 |
1102 | @@ -0,0 +1,44 @@ |
1103 | +import QtQuick 2.0 |
1104 | +import Ubuntu.Components 0.1 |
1105 | + |
1106 | +Column { |
1107 | + property bool showDetails: false |
1108 | + spacing: units.gu(1) |
1109 | + anchors.margins: units.gu(1) |
1110 | + |
1111 | + SenderAndTimeItem { |
1112 | + } |
1113 | + |
1114 | + Label { |
1115 | + id: messageText |
1116 | + anchors { |
1117 | + right: parent.right |
1118 | + left: parent.left |
1119 | + } |
1120 | + text: message |
1121 | + |
1122 | + elide: Text.ElideRight |
1123 | + visible: message.length > 0 && message != linkDescription |
1124 | + wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1125 | + textFormat: Text.StyledText |
1126 | + linkColor: Theme.palette.normal.baseText |
1127 | + onLinkActivated: Qt.openUrlExternally(link) |
1128 | + } |
1129 | + |
1130 | + LocationItem { |
1131 | + } |
1132 | + |
1133 | + LinkItem { |
1134 | + } |
1135 | + |
1136 | + LinkDescription { |
1137 | + } |
1138 | + |
1139 | + FavoriteIcon { |
1140 | + id: favorite |
1141 | + } |
1142 | + |
1143 | + RepeatIcon { |
1144 | + id: repeatIcon |
1145 | + } |
1146 | +} |
1147 | |
1148 | === modified file 'qml/StatusUpdateTile.qml' |
1149 | --- qml/StatusUpdateTile.qml 2013-08-28 19:04:36 +0000 |
1150 | +++ qml/StatusUpdateTile.qml 2014-01-23 12:09:15 +0000 |
1151 | @@ -25,9 +25,32 @@ |
1152 | id: tile |
1153 | anchors { |
1154 | left: parent.left |
1155 | - right: parent.right |
1156 | - } |
1157 | - property real detailsOpacity : 0 |
1158 | + right: parent.right |
1159 | + } |
1160 | + |
1161 | + //Forward properties |
1162 | + property bool liked : liked |
1163 | + property bool fromMe: fromMe |
1164 | + property string message: model.message |
1165 | + property string service: model.service |
1166 | + property string linkUrl: model.linkUrl |
1167 | + property string linkName: model.linkName |
1168 | + property string linkDescription: model.linkDescription |
1169 | + property string avatar: model.avatar |
1170 | + property string location: model.location |
1171 | + property string sender: model.sender |
1172 | + property string linkPicture: model.linkPicture |
1173 | + property int latitude: model.latitude |
1174 | + property int longitude: model.longitude |
1175 | + property string messageId: model.messageId |
1176 | + property string stream: model.stream |
1177 | + property string senderNick: model.senderNick |
1178 | + property string url: model.url |
1179 | + |
1180 | + function showConversation() { |
1181 | + main.addConversation(tile) |
1182 | + } |
1183 | + |
1184 | property string timeString: friendsUtils.createTimeString(timestamp) |
1185 | property int likeCount: likes > 0 ? likes : liked ? 1 : 0 |
1186 | property string likesString: likeCount > 1 ? i18n.tr("people liked this") : i18n.tr("person liked this") |
1187 | @@ -42,8 +65,10 @@ |
1188 | anchors { |
1189 | right: parent.right |
1190 | left: parent.left |
1191 | + leftMargin: units.gu(1) |
1192 | } |
1193 | - height: status_update_content.height + detailsWrapper.height |
1194 | + |
1195 | + height: childrenRect.height + units.gu(1) |
1196 | |
1197 | FriendsUtils { |
1198 | id: friendsUtils |
1199 | @@ -61,46 +86,24 @@ |
1200 | } |
1201 | onUnlikeComplete: { |
1202 | favoriteSpinner.visible = false; |
1203 | - if (success) { |
1204 | - console.log ("Like succeeded"); |
1205 | - } else { |
1206 | - console.log ("UnLike failed: " + errorMessage); |
1207 | - } |
1208 | - } |
1209 | + if (success) { |
1210 | + console.log ("Like succeeded"); |
1211 | + } else { |
1212 | + console.log ("UnLike failed: " + errorMessage); |
1213 | + } |
1214 | + } |
1215 | onRetweetComplete: { |
1216 | retweetSpinner.visible = false; |
1217 | if (success) { |
1218 | - console.log ("Retweet completed successfully"); |
1219 | + console.log ("Retweet completed successfully"); |
1220 | } else { |
1221 | - console.log ("Retweet failed: " + errorMessage); |
1222 | + console.log ("Retweet failed: " + errorMessage); |
1223 | } |
1224 | - } |
1225 | + } |
1226 | } |
1227 | |
1228 | - UbuntuShape { |
1229 | + AvatarImage { |
1230 | id: avatarImage |
1231 | - height: units.dp(48) |
1232 | - width: units.dp(48) |
1233 | - anchors { |
1234 | - left: parent.left |
1235 | - top: parent.top |
1236 | - leftMargin: units.gu(1) |
1237 | - topMargin: units.gu(1) |
1238 | - } |
1239 | - image: Image { |
1240 | - source: Qt.resolvedUrl(avatar) |
1241 | - asynchronous: true |
1242 | - smooth: true |
1243 | - fillMode: Image.PreserveAspectCrop |
1244 | - sourceSize.width: units.dp(48) |
1245 | - } |
1246 | - |
1247 | - MouseArea { |
1248 | - anchors.fill: avatarImage |
1249 | - onClicked: { |
1250 | - Qt.openUrlExternally(url); |
1251 | - } |
1252 | - } |
1253 | } |
1254 | |
1255 | Image { |
1256 | @@ -113,7 +116,7 @@ |
1257 | } |
1258 | source: "images/private.png" |
1259 | asynchronous: true |
1260 | - visible: stream == "private" |
1261 | + visible: stream === "private" |
1262 | } |
1263 | |
1264 | Image { |
1265 | @@ -126,14 +129,14 @@ |
1266 | } |
1267 | source: "images/replies.png" |
1268 | asynchronous: true |
1269 | - visible: stream == "mentions" |
1270 | + visible: stream === "mentions" |
1271 | } |
1272 | |
1273 | Image { |
1274 | id: serviceIcon |
1275 | anchors { |
1276 | left: parent.left |
1277 | - bottom: status_update_content.bottom |
1278 | + bottom: statusUpdateContent.bottom |
1279 | topMargin: units.gu(1) |
1280 | leftMargin: units.gu(1) |
1281 | bottomMargin: units.gu(1) |
1282 | @@ -145,269 +148,20 @@ |
1283 | smooth: true |
1284 | } |
1285 | |
1286 | - Item { |
1287 | - id: status_update_content |
1288 | + StatusUpdateContent { |
1289 | + id: statusUpdateContent |
1290 | anchors { |
1291 | left: avatarImage.right |
1292 | right: parent.right |
1293 | top: parent.top |
1294 | - topMargin: units.gu(1) |
1295 | - leftMargin: units.gu(1) |
1296 | - bottomMargin: units.gu(1) |
1297 | - rightMargin: units.gu(1) |
1298 | - } |
1299 | - height: childrenRect.height |
1300 | - |
1301 | - MouseArea { |
1302 | - anchors.fill: status_update_content |
1303 | - onClicked: { |
1304 | - if (tile.state != 'Details') { |
1305 | - tile.state = 'Details'; |
1306 | - } else { |
1307 | - tile.state = 'List'; |
1308 | - } |
1309 | - } |
1310 | - } |
1311 | - |
1312 | - Column { |
1313 | - spacing: units.gu(1) |
1314 | - anchors { |
1315 | - left: parent.left; |
1316 | - right: parent.right |
1317 | - bottomMargin: units.gu(2) |
1318 | - } |
1319 | - Item { |
1320 | - anchors { |
1321 | - left: parent.left |
1322 | - right: parent.right |
1323 | - rightMargin: units.gu(1) |
1324 | - } |
1325 | - height: childrenRect.height |
1326 | - Label { |
1327 | - text: sender |
1328 | - fontSize: "medium" |
1329 | - font.bold: true |
1330 | - anchors { |
1331 | - left: parent.left |
1332 | - right: time.left |
1333 | - } |
1334 | - wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1335 | - } |
1336 | - Label { |
1337 | - id: time |
1338 | - anchors { |
1339 | - right: parent.right |
1340 | - } |
1341 | - |
1342 | - text: timeString |
1343 | - fontSize: "small" |
1344 | - color: Theme.palette.normal.baseText |
1345 | - } |
1346 | - } |
1347 | - |
1348 | - Label { |
1349 | - id: messageText |
1350 | - anchors { |
1351 | - right: parent.right |
1352 | - left: parent.left |
1353 | - } |
1354 | - text: message |
1355 | - maximumLineCount: tile.state != 'Details' ? 10 : 0 |
1356 | - elide: Text.ElideRight |
1357 | - visible: message.length > 0 && message != linkDescription |
1358 | - wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1359 | - textFormat: Text.StyledText |
1360 | - linkColor: Theme.palette.normal.baseText |
1361 | - onLinkActivated: Qt.openUrlExternally(link) |
1362 | - } |
1363 | - UbuntuShape { |
1364 | - height: locationName.height + units.gu(1) |
1365 | - width: locationName.width + units.gu(2) |
1366 | - visible: location.length > 0 |
1367 | - Label { |
1368 | - id: locationName |
1369 | - anchors.centerIn: parent |
1370 | - text: location |
1371 | - wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1372 | - } |
1373 | - MouseArea { |
1374 | - anchors.fill: parent |
1375 | - onClicked: { |
1376 | - if (url.length > 0) |
1377 | - { |
1378 | - Qt.openUrlExternally(url); |
1379 | - } |
1380 | - } |
1381 | - } |
1382 | - } |
1383 | - UbuntuShape { |
1384 | - height: linkNameShape.height + units.gu(1) |
1385 | - width: linkNameShape.width + units.gu(2) |
1386 | - visible: linkName.length > 0 |
1387 | - Label { |
1388 | - id: linkNameShape |
1389 | - anchors.centerIn: parent |
1390 | - text: linkName |
1391 | - wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1392 | - } |
1393 | - MouseArea { |
1394 | - anchors.fill: parent |
1395 | - onClicked: { |
1396 | - if (linkUrl.length > 0) |
1397 | - Qt.openUrlExternally(linkUrl); |
1398 | - |
1399 | - } |
1400 | - } |
1401 | - } |
1402 | - Label { |
1403 | - id: linkDesc |
1404 | - anchors { |
1405 | - right: parent.right |
1406 | - left: parent.left |
1407 | - } |
1408 | - text: linkDescription |
1409 | - maximumLineCount: tile.state != 'Details' ? 10 : 0 |
1410 | - elide: Text.ElideRight |
1411 | - visible: linkDescription.length > 0 |
1412 | - wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1413 | - //width: parent.width - units.gu(3) |
1414 | - textFormat: Text.StyledText |
1415 | - onLinkActivated: Qt.openUrlExternally(link) |
1416 | - } |
1417 | - |
1418 | - Row { |
1419 | - id: favorite |
1420 | - height: favoriteIcon.height + units.gu(1) |
1421 | - anchors { |
1422 | - left: parent.left |
1423 | - right: parent.right |
1424 | - bottomMargin: units.gu(1) |
1425 | - } |
1426 | - spacing: units.dp(3) |
1427 | - Image { |
1428 | - id: favoriteIcon |
1429 | - width: units.gu(3) |
1430 | - height: units.gu(3) |
1431 | - opacity: liked ? 1.0 : 0.1 |
1432 | - |
1433 | - source: "images/favorite.png" |
1434 | - asynchronous: true |
1435 | - MouseArea { |
1436 | - anchors.fill: favoriteIcon |
1437 | - onClicked: { |
1438 | - favoriteSpinner.visible = true; |
1439 | - if (liked) { |
1440 | - friends.unlikeAsync(accountId, messageId); |
1441 | - } |
1442 | - else { |
1443 | - friends.likeAsync(accountId, messageId); |
1444 | - } |
1445 | - } |
1446 | - } |
1447 | - ActivityIndicator { |
1448 | - id: favoriteSpinner |
1449 | - anchors.centerIn: parent |
1450 | - width: parent.width |
1451 | - visible: false |
1452 | - running: visible |
1453 | - } |
1454 | - } |
1455 | - Label { |
1456 | - id: likesLabel |
1457 | - text: (likeCount > 0) ? likeCount + " " + likesString : "" |
1458 | - fontSize: "small" |
1459 | - color: Theme.palette.normal.baseText |
1460 | - } |
1461 | - } |
1462 | - |
1463 | - Item { |
1464 | - id: repeatIcon |
1465 | - anchors { |
1466 | - right: parent.right |
1467 | - bottom: parent.bottom |
1468 | - rightMargin: units.gu(2) |
1469 | - bottomMargin: units.gu(1) |
1470 | - } |
1471 | - |
1472 | - opacity: detailsOpacity |
1473 | - height: fromMe ? 0 : childrenRect.height |
1474 | - // FIXME: Use features API instead |
1475 | - visible: (service == "twitter" || service == "identica") && !fromMe && stream != "private" |
1476 | - Image { |
1477 | - id: repeatIconImage |
1478 | - width: units.gu(3) |
1479 | - height: units.gu(3) |
1480 | - source: "images/share.png" |
1481 | - asynchronous: true |
1482 | - visible: retweetSpinner.visble ? false : true |
1483 | - MouseArea { |
1484 | - anchors.fill: parent |
1485 | - onClicked: { |
1486 | - console.log ("Retweeting " + messageId + " from " + accountId); |
1487 | - retweetSpinner.visible = true |
1488 | - friends.retweetAsync(accountId, messageId); |
1489 | - } |
1490 | - } |
1491 | - ActivityIndicator { |
1492 | - id: retweetSpinner |
1493 | - anchors.centerIn: parent |
1494 | - //width: parent.width |
1495 | - visible: false |
1496 | - running: visible |
1497 | - } |
1498 | - } |
1499 | - } |
1500 | - |
1501 | - } |
1502 | - } |
1503 | - |
1504 | - Item { |
1505 | - id: detailsWrapper |
1506 | - height: detailsLoader.height + units.gu(1) |
1507 | - opacity: detailsOpacity |
1508 | - visible: detailsOpacity > 0 |
1509 | - anchors { |
1510 | - top: status_update_content.bottom |
1511 | - left: parent.left |
1512 | - right: parent.right |
1513 | - } |
1514 | - |
1515 | - Loader { |
1516 | - id: detailsLoader |
1517 | - anchors { |
1518 | - right: parent.right |
1519 | - left: parent.left |
1520 | - } |
1521 | - visible: true |
1522 | - } |
1523 | - |
1524 | - onVisibleChanged: { |
1525 | - if (visible && detailsLoader.state != Loader.Ready) { |
1526 | - detailsLoader.source = "StatusUpdateTileDetails.qml"; |
1527 | - } |
1528 | - } |
1529 | - } |
1530 | - } |
1531 | - |
1532 | - states: [State { |
1533 | - name: "Details" |
1534 | - PropertyChanges { target: tile; detailsOpacity: 1; x: 0 } |
1535 | - StateChangeScript { |
1536 | - script: { |
1537 | - listView.positionViewAtIndex(listView.indexAt(tile.x, tile.y), ListView.Contain); |
1538 | - } |
1539 | - } |
1540 | - |
1541 | - }, |
1542 | - State { |
1543 | - name: "List" |
1544 | - PropertyChanges { target: tile; detailsOpacity: 0; x: 0 } |
1545 | - }] |
1546 | - |
1547 | - |
1548 | - transitions: Transition { |
1549 | - NumberAnimation { |
1550 | - duration: 300; properties: "detailsOpacity,x,height,width" |
1551 | + } |
1552 | + } |
1553 | + |
1554 | + MouseArea { |
1555 | + anchors.fill: statusUpdateContent |
1556 | + onClicked: { |
1557 | + showConversation(); |
1558 | + } |
1559 | } |
1560 | } |
1561 | } |
1562 | |
1563 | === modified file 'qml/ThreadView.qml' |
1564 | --- qml/ThreadView.qml 2013-08-20 15:17:54 +0000 |
1565 | +++ qml/ThreadView.qml 2014-01-23 12:09:15 +0000 |
1566 | @@ -3,9 +3,15 @@ |
1567 | import Friends 0.2 |
1568 | |
1569 | Item { |
1570 | - height: threadListView.height + pictureImage.height |
1571 | + id: threadView |
1572 | + |
1573 | + height: threadListView.height |
1574 | + |
1575 | width: parent.width |
1576 | + |
1577 | anchors { |
1578 | + left: parent.left |
1579 | + right: parent.right |
1580 | bottomMargin: units.gu(2) |
1581 | } |
1582 | |
1583 | @@ -14,102 +20,49 @@ |
1584 | stream: "reply_to/"+messageId |
1585 | } |
1586 | |
1587 | - |
1588 | FriendsUtils { |
1589 | id: friendsUtils |
1590 | } |
1591 | |
1592 | Column { |
1593 | id: threadListView |
1594 | + anchors { |
1595 | + left: parent.left |
1596 | + right: parent.right |
1597 | + } |
1598 | + |
1599 | width: parent.width |
1600 | + height: childrenRect.height |
1601 | + |
1602 | Repeater { |
1603 | + id: repeater |
1604 | width: parent.width |
1605 | model: streamModel |
1606 | delegate: Item { |
1607 | id: threadItem |
1608 | - width: parent.width - units.gu(8) |
1609 | + |
1610 | anchors { |
1611 | right: parent.right |
1612 | - leftMargin: units.gu(8) |
1613 | - rightMargin: units.gu(2) |
1614 | + left: parent.left |
1615 | + |
1616 | + leftMargin: units.gu(4) |
1617 | bottomMargin: units.gu(2) |
1618 | } |
1619 | - height: status_update_content.height |
1620 | - |
1621 | - UbuntuShape { |
1622 | + |
1623 | + height: childrenRect.height |
1624 | + |
1625 | + AvatarImage { |
1626 | id: avatarImage |
1627 | - radius: "small" |
1628 | - height: units.dp(32) |
1629 | - width: units.dp(32) |
1630 | - anchors { |
1631 | - left: parent.left |
1632 | - top: parent.top |
1633 | - leftMargin: units.gu(1) |
1634 | - topMargin: units.gu(1) |
1635 | - } |
1636 | - image: Image { |
1637 | - source: Qt.resolvedUrl(avatar) |
1638 | - asynchronous: true |
1639 | - fillMode: Image.PreserveAspectFit |
1640 | - } |
1641 | } |
1642 | |
1643 | - Item { |
1644 | - id: status_update_content |
1645 | + StatusUpdateContent { |
1646 | anchors { |
1647 | left: avatarImage.right |
1648 | right: parent.right |
1649 | top: parent.top |
1650 | - topMargin: units.gu(1) |
1651 | - leftMargin: units.gu(1) |
1652 | - bottomMargin: units.gu(1) |
1653 | - rightMargin: units.gu(1) |
1654 | - } |
1655 | - width: parent.width - avatarImage.width |
1656 | - height: childrenRect.height + units.gu(1) |
1657 | - |
1658 | - Column { |
1659 | - width: parent.width |
1660 | - spacing: 0 |
1661 | - anchors { |
1662 | - left: parent.left; |
1663 | - right: parent.right |
1664 | - bottomMargin: units.gu(2) |
1665 | - } |
1666 | - Label { |
1667 | - text: sender |
1668 | - fontSize: "small" |
1669 | - font.bold: true |
1670 | - width: parent.width |
1671 | - } |
1672 | - Label { |
1673 | - id: messageText |
1674 | - text: message |
1675 | - wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
1676 | - width: parent.width - units.gu(3) |
1677 | - fontSize: "small" |
1678 | - onLinkActivated: { |
1679 | - console.log(link + " link activated"); |
1680 | - Qt.openUrlExternally(link); |
1681 | - } |
1682 | - } |
1683 | - } |
1684 | - Row { |
1685 | - anchors.right: parent.right |
1686 | - anchors.rightMargin: units.gu(1) |
1687 | - anchors.top: parent.top |
1688 | - spacing: units.gu(1) |
1689 | - |
1690 | - Text { |
1691 | - id: time |
1692 | - text: friendsUtils.createTimeString(timestamp) |
1693 | - font.family: "Ubuntu" |
1694 | - font.pointSize: 8 |
1695 | - color: Theme.palette.normal.baseText |
1696 | - } |
1697 | } |
1698 | } |
1699 | - } |
1700 | + } |
1701 | } |
1702 | } |
1703 | } |
1704 | |
1705 | === modified file 'qml/friends-app.qml' |
1706 | --- qml/friends-app.qml 2013-12-11 21:06:40 +0000 |
1707 | +++ qml/friends-app.qml 2014-01-23 12:09:15 +0000 |
1708 | @@ -19,9 +19,24 @@ |
1709 | import Ubuntu.Components 0.1 |
1710 | import Ubuntu.Unity.Action 1.0 as UnityActions |
1711 | import Ubuntu.OnlineAccounts 0.1 |
1712 | +import Ubuntu.Layouts 0.1 |
1713 | import Friends 0.2 |
1714 | |
1715 | MainView { |
1716 | + function addConversation(tile) { |
1717 | + conversationPage.setContent(tile) |
1718 | + |
1719 | + pageStack.push(conversationPage) |
1720 | + } |
1721 | + |
1722 | + function closeConversation() { |
1723 | + pageStack.pop() |
1724 | + } |
1725 | + |
1726 | + function refresh() { |
1727 | + friends.refresh() |
1728 | + } |
1729 | + |
1730 | id: main |
1731 | applicationName: "friends-app" |
1732 | // objectName for functional testing purposes (autopilot-qt5) |
1733 | @@ -34,6 +49,8 @@ |
1734 | backgroundColor: "#273d66" |
1735 | headerColor: "#395996" |
1736 | |
1737 | + property bool loaded: false |
1738 | + |
1739 | property bool isPortrait: (height > width) |
1740 | property bool loaded: false |
1741 | |
1742 | @@ -57,7 +74,7 @@ |
1743 | id: topAction |
1744 | text: i18n.tr("Top") |
1745 | iconSource: ("images/go-top.png") |
1746 | - onTriggered: feedTabs.selectedTab.jumpToTop() |
1747 | + onTriggered: feedTabs.jumpToTop() |
1748 | } |
1749 | |
1750 | actions: [topAction, refreshAction, postAction] |
1751 | @@ -87,12 +104,12 @@ |
1752 | id: pageStack |
1753 | |
1754 | Component.onCompleted: { |
1755 | - console.log ("accounts.count: "+accounts.count); |
1756 | - pageStack.push(mainPage); |
1757 | + console.log ("accounts.count: "+accounts.count) |
1758 | + |
1759 | if (accounts.count < 1) |
1760 | - pageStack.push(noAccountsPage); |
1761 | - |
1762 | - loaded = true |
1763 | + pageStack.push(noAccountsPage) |
1764 | + else |
1765 | + pageStack.push(feedTabs) |
1766 | } |
1767 | |
1768 | Page { |
1769 | @@ -126,47 +143,16 @@ |
1770 | } |
1771 | } |
1772 | |
1773 | - Page { |
1774 | - id: mainPage |
1775 | + FeedTabs { |
1776 | visible: false |
1777 | - |
1778 | - Tabs { |
1779 | - id: feedTabs |
1780 | - |
1781 | - Feed { |
1782 | - id: timeline |
1783 | - title: i18n.tr("Timeline") |
1784 | - |
1785 | - stream: "" //all |
1786 | - } |
1787 | - |
1788 | - Feed { |
1789 | - id: mentions |
1790 | - title: i18n.tr("Mentions") |
1791 | - |
1792 | - stream: "mentions" |
1793 | - } |
1794 | - |
1795 | - /* The stream filter doesn't seem to work yet |
1796 | - TimeLine { |
1797 | - id: privateMessages |
1798 | - title: i18n.tr("Private") |
1799 | - |
1800 | - stream: "private" |
1801 | - }*/ |
1802 | - } |
1803 | - |
1804 | - tools: ToolbarItems { |
1805 | - ToolbarButton { |
1806 | - action: topAction |
1807 | - } |
1808 | - ToolbarButton { |
1809 | - action: refreshAction |
1810 | - } |
1811 | - ToolbarButton { |
1812 | - action: postAction |
1813 | - } |
1814 | - } |
1815 | + id: feedTabs |
1816 | + } |
1817 | + |
1818 | + // Pages cannot be nested so we have to work around this restriction |
1819 | + // See this bug for some infos: https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1256420 |
1820 | + ConversationPage { |
1821 | + visible: false |
1822 | + id: conversationPage |
1823 | } |
1824 | } |
1825 | } |
1826 | |
1827 | === modified file 'qml/qml.pro' |
1828 | --- qml/qml.pro 2013-03-30 18:20:11 +0000 |
1829 | +++ qml/qml.pro 2014-01-23 12:09:15 +0000 |
1830 | @@ -13,5 +13,4 @@ |
1831 | INSTALLS = install_resources |
1832 | |
1833 | OTHER_FILES += \ |
1834 | - ThreadView.qml \ |
1835 | - RefreshBar.qml |
1836 | + FeedTabs.qml |
I've been looking forward to seeing this branch since I saw your post on G+, great work!
Looks pretty good, I did notice one typo. In Feed.qml you have an alias "servie". It doesn't appear to be used, if it isn't just remove it. If it is used, fix the typo. Also there are some places where you have commented out code, just remove those lines. Code comments are great, but I'd rather remove the commented out code.