Merge lp:~phablet-team/messaging-app/side_panel into lp:messaging-app
- side_panel
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~phablet-team/messaging-app/side_panel |
Merge into: | lp:messaging-app |
Prerequisite: | lp:~phablet-team/messaging-app/add-apparmor-profile |
Diff against target: |
2824 lines (+1073/-1017) 29 files modified
CMakeLists.txt (+14/-18) TODO.convergence (+3/-0) config.h.in (+1/-0) src/messagingapplication.cpp (+1/-0) src/qml/AccountSectionDelegate.qml (+2/-1) src/qml/ComposeBar.qml (+4/-0) src/qml/EmptyState.qml (+38/-0) src/qml/InputInfo.qml (+24/-0) src/qml/LocalPageWithBottomEdge.qml (+0/-428) src/qml/MMS/Previewer.qml (+26/-15) src/qml/MMS/PreviewerImage.qml (+1/-0) src/qml/MMS/PreviewerMultipleContacts.qml (+7/-2) src/qml/MMS/PreviewerVideo.qml (+1/-0) src/qml/MMSDelegate.qml (+1/-1) src/qml/MainPage.qml (+136/-65) src/qml/Messages.qml (+452/-350) src/qml/MessagesHeader.qml (+7/-2) src/qml/MessagingBottomEdge.qml (+43/-0) src/qml/MessagingContactEditorPage.qml (+6/-2) src/qml/MessagingContactViewPage.qml (+11/-10) src/qml/MessagingPageLayout.qml (+64/-0) src/qml/MultiRecipientInput.qml (+1/-1) src/qml/NewRecipientPage.qml (+69/-37) src/qml/RegularMessageDelegate.qml (+1/-1) src/qml/SettingsPage.qml (+22/-1) src/qml/messaging-app.qml (+115/-30) tests/autopilot/messaging_app/emulators.py (+12/-48) tests/qml/CMakeLists.txt (+2/-1) tests/qml/tst_MessagesView.qml (+9/-4) |
To merge this branch: | bzr merge lp:~phablet-team/messaging-app/side_panel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+284299@code.launchpad.net |
This proposal supersedes a proposal from 2016-01-14.
This proposal has been superseded by a proposal from 2016-02-05.
Commit message
Implement convergent layout for messaging-app.
Description of the change
Implement convergent layout for messaging-app.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:451
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:452
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:454
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:455
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:456
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:457
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:458
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:460
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:461
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:462
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:463
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:464
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:465
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:466
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:468
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:469
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:470
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:471
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:472
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:474
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:475
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:476
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:477
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:478
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:479
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:480
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:481
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 484. By Gustavo Pichorim Boiko
-
Set the title correctly.
- 485. By Gustavo Pichorim Boiko
-
Make it possible to unselect all too in the main view.
- 486. By Gustavo Pichorim Boiko
-
Fix the header in the messages view.
- 487. By Gustavo Pichorim Boiko
-
Clear some warnings.
- 488. By Gustavo Pichorim Boiko
-
Make sure the page has a height when loading.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:483
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 489. By Tiago Salem Herrmann
-
Fix ListView anchors also on NewRecipientPage
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:488
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 490. By Gustavo Pichorim Boiko
-
Create pages manually before pushing to the AdaptivePageLayout as it is very
slow to create the pages by itself.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:489
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:490
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 491. By Gustavo Pichorim Boiko
-
Make sure pages get destroyed when hitting back
- 492. By Tiago Salem Herrmann
-
Implement back button to destroy dynamically created pages
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:491
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:492
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 493. By Tiago Salem Herrmann
-
Push all views synchronously
- 494. By Gustavo Pichorim Boiko
-
Make it possible to launch the new message view from settings.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:494
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 495. By Tiago Salem Herrmann
-
fix anchors in the settings page
- 496. By Tiago Salem Herrmann
-
Empty stack before adding SettingsPage to the stack
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:496
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 497. By Tiago Salem Herrmann
-
Only set properties once during object creation
implement removePage() that destroy instances when needed - 498. By Tiago Salem Herrmann
-
check before destroying instance
- 499. By Tiago Salem Herrmann
-
anchor previewer to header
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:497
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:499
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 500. By Gustavo Pichorim Boiko
-
Move the custom page layout functions to a separate QML file.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:500
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 501. By Gustavo Pichorim Boiko
-
Merge swipe to cancel fix.
- 502. By Gustavo Pichorim Boiko
-
Remove duplicated code.
- 503. By Gustavo Pichorim Boiko
-
Fix loading pages by source
- 504. By Gustavo Pichorim Boiko
-
Hide the bottom edge bar in the empty state screen.
- 505. By Gustavo Pichorim Boiko
-
Messaging-app doesn't belong to side stage anymore
- 506. By Gustavo Pichorim Boiko
-
Set a minimum window size.
- 507. By Gustavo Pichorim Boiko
-
Make it landscape by default on windowed environments
- 508. By Gustavo Pichorim Boiko
-
Hide the new message list item once the message is sent.
- 509. By Gustavo Pichorim Boiko
-
Show the group chat participants popup anchored to the right
- 510. By Gustavo Pichorim Boiko
-
Make the header fixed in two column mode
- 511. By Gustavo Pichorim Boiko
-
Add a scrollbar to the Messages view.
- 512. By Gustavo Pichorim Boiko
-
Merge trunk.
- 513. By Gustavo Pichorim Boiko
-
Make the readme more clear
- 514. By Gustavo Pichorim Boiko
-
In dual panel mode, always hide the empty state label.
Unmerged revisions
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2015-10-13 15:13:32 +0000 | |||
3 | +++ CMakeLists.txt 2016-02-01 23:18:57 +0000 | |||
4 | @@ -35,12 +35,7 @@ | |||
5 | 35 | # Instruct CMake to run moc automatically when needed. | 35 | # Instruct CMake to run moc automatically when needed. |
6 | 36 | set(CMAKE_AUTOMOC ON) | 36 | set(CMAKE_AUTOMOC ON) |
7 | 37 | 37 | ||
8 | 38 | configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) | ||
9 | 39 | |||
10 | 40 | #find_package(Qt5Contacts) | ||
11 | 41 | find_package(Qt5DBus) | 38 | find_package(Qt5DBus) |
12 | 42 | #find_package(Qt5Gui) | ||
13 | 43 | #find_package(Qt5Multimedia) | ||
14 | 44 | find_package(Qt5Qml) | 39 | find_package(Qt5Qml) |
15 | 45 | find_package(Qt5Quick) | 40 | find_package(Qt5Quick) |
16 | 46 | find_package(Qt5Test) | 41 | find_package(Qt5Test) |
17 | @@ -49,23 +44,24 @@ | |||
18 | 49 | include(qt5) | 44 | include(qt5) |
19 | 50 | 45 | ||
20 | 51 | find_package(PkgConfig REQUIRED) | 46 | find_package(PkgConfig REQUIRED) |
21 | 52 | #pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5) | ||
22 | 53 | #pkg_check_modules(TPL_QT5 REQUIRED TelepathyLoggerQt5) | ||
23 | 54 | #pkg_check_modules(QTGLIB REQUIRED QtGLib-2.0) | ||
24 | 55 | #pkg_check_modules(GLIB REQUIRED glib-2.0) | ||
25 | 56 | pkg_check_modules(NOTIFY REQUIRED libnotify) | 47 | pkg_check_modules(NOTIFY REQUIRED libnotify) |
35 | 57 | #pkg_check_modules(MESSAGING_MENU REQUIRED messaging-menu) | 48 | |
36 | 58 | 49 | #find unity8 qml libraries | |
37 | 59 | # Check if the messaging menu has the message header | 50 | set(UNITY8_QML_PATH /usr/lib/${CMAKE_C_LIBRARY_ARCHITECTURE}/unity8/qml/) |
38 | 60 | #set(CMAKE_REQUIRED_INCLUDES ${MESSAGING_MENU_INCLUDE_DIRS}) | 51 | find_path(LIB_UNITY_QML_EXISTS NAMES libUnity-qml.so |
39 | 61 | #check_include_file("messaging-menu-message.h" HAVE_MESSAGING_MENU_MESSAGE) | 52 | HINTS "${UNITY8_QML_PATH}" |
40 | 62 | 53 | NO_CMAKE_PATH | |
41 | 63 | if (HAVE_MESSAGING_MENU_MESSAGE) | 54 | NO_CMAKE_ENVIRONMENT_PATH |
42 | 64 | add_definitions(-DHAVE_MESSAGING_MENU_MESSAGE) | 55 | NO_SYSTEM_ENVIRONMENT_PATH |
43 | 65 | endif (HAVE_MESSAGING_MENU_MESSAGE) | 56 | ) |
44 | 57 | if(!LIB_UNITY_QML_EXISTS) | ||
45 | 58 | MESSAGE(FATAL_ERROR "unity8 private package not-found") | ||
46 | 59 | endif() | ||
47 | 66 | 60 | ||
48 | 67 | add_definitions(-DQT_NO_KEYWORDS) | 61 | add_definitions(-DQT_NO_KEYWORDS) |
49 | 68 | 62 | ||
50 | 63 | configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) | ||
51 | 64 | |||
52 | 69 | include_directories( | 65 | include_directories( |
53 | 70 | ${CMAKE_CURRENT_BINARY_DIR} | 66 | ${CMAKE_CURRENT_BINARY_DIR} |
54 | 71 | ${CMAKE_CURRENT_SOURCE_DIR} | 67 | ${CMAKE_CURRENT_SOURCE_DIR} |
55 | 72 | 68 | ||
56 | === added file 'TODO.convergence' | |||
57 | --- TODO.convergence 1970-01-01 00:00:00 +0000 | |||
58 | +++ TODO.convergence 2016-02-01 23:18:57 +0000 | |||
59 | @@ -0,0 +1,3 @@ | |||
60 | 1 | - Right now if you call startChat() in the two panel layout, it won't match the | ||
61 | 2 | thread on the left side, and if you resize it to one panel, the conversation | ||
62 | 3 | is closed. | ||
63 | 0 | 4 | ||
64 | === modified file 'config.h.in' | |||
65 | --- config.h.in 2014-05-28 20:28:55 +0000 | |||
66 | +++ config.h.in 2016-02-01 23:18:57 +0000 | |||
67 | @@ -27,6 +27,7 @@ | |||
68 | 27 | #include <QtDBus/QDBusReply> | 27 | #include <QtDBus/QDBusReply> |
69 | 28 | 28 | ||
70 | 29 | #define I18N_DIRECTORY "@CMAKE_INSTALL_PREFIX@/share/locale" | 29 | #define I18N_DIRECTORY "@CMAKE_INSTALL_PREFIX@/share/locale" |
71 | 30 | #define UNITY8_QML_PATH "@UNITY8_QML_PATH@" | ||
72 | 30 | 31 | ||
73 | 31 | inline bool isRunningInstalled() { | 32 | inline bool isRunningInstalled() { |
74 | 32 | static bool installed = (QCoreApplication::applicationDirPath() == | 33 | static bool installed = (QCoreApplication::applicationDirPath() == |
75 | 33 | 34 | ||
76 | === modified file 'src/messagingapplication.cpp' | |||
77 | --- src/messagingapplication.cpp 2016-02-01 23:18:57 +0000 | |||
78 | +++ src/messagingapplication.cpp 2016-02-01 23:18:57 +0000 | |||
79 | @@ -165,6 +165,7 @@ | |||
80 | 165 | m_view->rootContext()->setContextProperty("application", this); | 165 | m_view->rootContext()->setContextProperty("application", this); |
81 | 166 | m_view->rootContext()->setContextProperty("i18nDirectory", I18N_DIRECTORY); | 166 | m_view->rootContext()->setContextProperty("i18nDirectory", I18N_DIRECTORY); |
82 | 167 | m_view->engine()->setBaseUrl(QUrl::fromLocalFile(messagingAppDirectory())); | 167 | m_view->engine()->setBaseUrl(QUrl::fromLocalFile(messagingAppDirectory())); |
83 | 168 | m_view->engine()->addImportPath(UNITY8_QML_PATH); | ||
84 | 168 | 169 | ||
85 | 169 | // check if there is a contacts backend override | 170 | // check if there is a contacts backend override |
86 | 170 | QString contactsBackend = qgetenv("QTCONTACTS_MANAGER_OVERRIDE"); | 171 | QString contactsBackend = qgetenv("QTCONTACTS_MANAGER_OVERRIDE"); |
87 | 171 | 172 | ||
88 | === modified file 'src/qml/AccountSectionDelegate.qml' | |||
89 | --- src/qml/AccountSectionDelegate.qml 2015-09-14 13:51:27 +0000 | |||
90 | +++ src/qml/AccountSectionDelegate.qml 2016-02-01 23:18:57 +0000 | |||
91 | @@ -28,7 +28,8 @@ | |||
92 | 28 | property var messageData: null | 28 | property var messageData: null |
93 | 29 | property int index: -1 | 29 | property int index: -1 |
94 | 30 | property Item delegateItem | 30 | property Item delegateItem |
96 | 31 | property string accountLabel: telepathyHelper.accountForId(messageData.accountId).displayName | 31 | property var account: telepathyHelper.accountForId(messageData.accountId) |
97 | 32 | property string accountLabel: account ? account.displayName : "" | ||
98 | 32 | 33 | ||
99 | 33 | // update the accountLabel when the list of accounts become available | 34 | // update the accountLabel when the list of accounts become available |
100 | 34 | Item { | 35 | Item { |
101 | 35 | 36 | ||
102 | === modified file 'src/qml/ComposeBar.qml' | |||
103 | --- src/qml/ComposeBar.qml 2016-02-01 23:18:57 +0000 | |||
104 | +++ src/qml/ComposeBar.qml 2016-02-01 23:18:57 +0000 | |||
105 | @@ -64,6 +64,10 @@ | |||
106 | 64 | } | 64 | } |
107 | 65 | 65 | ||
108 | 66 | function addAttachments(transfer) { | 66 | function addAttachments(transfer) { |
109 | 67 | if (!transfer || !transfer.items) { | ||
110 | 68 | return | ||
111 | 69 | } | ||
112 | 70 | |||
113 | 67 | for (var i = 0; i < transfer.items.length; i++) { | 71 | for (var i = 0; i < transfer.items.length; i++) { |
114 | 68 | if (String(transfer.items[i].text).length > 0) { | 72 | if (String(transfer.items[i].text).length > 0) { |
115 | 69 | composeBar.text = String(transfer.items[i].text) | 73 | composeBar.text = String(transfer.items[i].text) |
116 | 70 | 74 | ||
117 | === added file 'src/qml/EmptyState.qml' | |||
118 | --- src/qml/EmptyState.qml 1970-01-01 00:00:00 +0000 | |||
119 | +++ src/qml/EmptyState.qml 2016-02-01 23:18:57 +0000 | |||
120 | @@ -0,0 +1,38 @@ | |||
121 | 1 | import QtQuick 2.0 | ||
122 | 2 | import Ubuntu.Components 1.3 | ||
123 | 3 | |||
124 | 4 | Item { | ||
125 | 5 | id: emptyStateScreen | ||
126 | 6 | |||
127 | 7 | property alias labelVisible: emptyStateLabel.visible | ||
128 | 8 | |||
129 | 9 | anchors { | ||
130 | 10 | left: parent.left | ||
131 | 11 | leftMargin: units.gu(6) | ||
132 | 12 | right: parent.right | ||
133 | 13 | rightMargin: units.gu(6) | ||
134 | 14 | verticalCenter: parent.verticalCenter | ||
135 | 15 | } | ||
136 | 16 | height: childrenRect.height | ||
137 | 17 | Icon { | ||
138 | 18 | id: emptyStateIcon | ||
139 | 19 | anchors.top: emptyStateScreen.top | ||
140 | 20 | anchors.horizontalCenter: parent.horizontalCenter | ||
141 | 21 | height: units.gu(5) | ||
142 | 22 | width: height | ||
143 | 23 | opacity: 0.3 | ||
144 | 24 | name: "message" | ||
145 | 25 | } | ||
146 | 26 | Label { | ||
147 | 27 | id: emptyStateLabel | ||
148 | 28 | anchors.top: emptyStateIcon.bottom | ||
149 | 29 | anchors.topMargin: units.gu(2) | ||
150 | 30 | anchors.left: parent.left | ||
151 | 31 | anchors.right: parent.right | ||
152 | 32 | text: i18n.tr("Compose a new message by swiping up from the bottom of the screen.") | ||
153 | 33 | color: "#5d5d5d" | ||
154 | 34 | fontSize: "x-large" | ||
155 | 35 | wrapMode: Text.WordWrap | ||
156 | 36 | horizontalAlignment: Text.AlignHCenter | ||
157 | 37 | } | ||
158 | 38 | } | ||
159 | 0 | 39 | ||
160 | === added file 'src/qml/InputInfo.qml' | |||
161 | --- src/qml/InputInfo.qml 1970-01-01 00:00:00 +0000 | |||
162 | +++ src/qml/InputInfo.qml 2016-02-01 23:18:57 +0000 | |||
163 | @@ -0,0 +1,24 @@ | |||
164 | 1 | import QtQuick 2.0 | ||
165 | 2 | //import Unity.InputInfo 0.1 | ||
166 | 3 | |||
167 | 4 | Item { | ||
168 | 5 | // FIXME: implement correctly without relying on unity private stuff | ||
169 | 6 | property bool hasMouse: mainView.dualPanel //miceModel.count > 0 || touchPadModel.count > 0 | ||
170 | 7 | property bool hasKeyboard: false //keyboardsModel.count > 0 | ||
171 | 8 | |||
172 | 9 | /*InputDeviceModel { | ||
173 | 10 | id: miceModel | ||
174 | 11 | deviceFilter: InputInfo.Mouse | ||
175 | 12 | } | ||
176 | 13 | |||
177 | 14 | InputDeviceModel { | ||
178 | 15 | id: touchPadModel | ||
179 | 16 | deviceFilter: InputInfo.TouchPad | ||
180 | 17 | } | ||
181 | 18 | |||
182 | 19 | InputDeviceModel { | ||
183 | 20 | id: keyboardsModel | ||
184 | 21 | deviceFilter: InputInfo.Keyboard | ||
185 | 22 | }*/ | ||
186 | 23 | } | ||
187 | 24 | |||
188 | 0 | 25 | ||
189 | === removed file 'src/qml/LocalPageWithBottomEdge.qml' | |||
190 | --- src/qml/LocalPageWithBottomEdge.qml 2015-09-14 13:51:27 +0000 | |||
191 | +++ src/qml/LocalPageWithBottomEdge.qml 1970-01-01 00:00:00 +0000 | |||
192 | @@ -1,428 +0,0 @@ | |||
193 | 1 | /* | ||
194 | 2 | * Copyright (C) 2014 Canonical, Ltd. | ||
195 | 3 | * | ||
196 | 4 | * This program is free software; you can redistribute it and/or modify | ||
197 | 5 | * it under the terms of the GNU General Public License as published by | ||
198 | 6 | * the Free Software Foundation; version 3. | ||
199 | 7 | * | ||
200 | 8 | * This program is distributed in the hope that it will be useful, | ||
201 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
202 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
203 | 11 | * GNU General Public License for more details. | ||
204 | 12 | * | ||
205 | 13 | * You should have received a copy of the GNU General Public License | ||
206 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
207 | 15 | */ | ||
208 | 16 | |||
209 | 17 | /* | ||
210 | 18 | Example: | ||
211 | 19 | |||
212 | 20 | MainView { | ||
213 | 21 | objectName: "mainView" | ||
214 | 22 | |||
215 | 23 | applicationName: "com.ubuntu.developer.boiko.bottomedge" | ||
216 | 24 | |||
217 | 25 | width: units.gu(100) | ||
218 | 26 | height: units.gu(75) | ||
219 | 27 | |||
220 | 28 | Component { | ||
221 | 29 | id: pageComponent | ||
222 | 30 | |||
223 | 31 | PageWithBottomEdge { | ||
224 | 32 | id: mainPage | ||
225 | 33 | title: i18n.tr("Main Page") | ||
226 | 34 | |||
227 | 35 | Rectangle { | ||
228 | 36 | anchors.fill: parent | ||
229 | 37 | color: "white" | ||
230 | 38 | } | ||
231 | 39 | |||
232 | 40 | bottomEdgePageComponent: Page { | ||
233 | 41 | title: "Contents" | ||
234 | 42 | anchors.fill: parent | ||
235 | 43 | //anchors.topMargin: contentsPage.flickable.contentY | ||
236 | 44 | |||
237 | 45 | ListView { | ||
238 | 46 | anchors.fill: parent | ||
239 | 47 | model: 50 | ||
240 | 48 | delegate: ListItems.Standard { | ||
241 | 49 | text: "One Content Item: " + index | ||
242 | 50 | } | ||
243 | 51 | } | ||
244 | 52 | } | ||
245 | 53 | bottomEdgeTitle: i18n.tr("Bottom edge action") | ||
246 | 54 | } | ||
247 | 55 | } | ||
248 | 56 | |||
249 | 57 | PageStack { | ||
250 | 58 | id: stack | ||
251 | 59 | Component.onCompleted: stack.push(pageComponent) | ||
252 | 60 | } | ||
253 | 61 | } | ||
254 | 62 | |||
255 | 63 | */ | ||
256 | 64 | |||
257 | 65 | import QtQuick 2.2 | ||
258 | 66 | import Ubuntu.Components 1.3 | ||
259 | 67 | |||
260 | 68 | Page { | ||
261 | 69 | id: page | ||
262 | 70 | |||
263 | 71 | property alias bottomEdgePageComponent: edgeLoader.sourceComponent | ||
264 | 72 | property alias bottomEdgePageSource: edgeLoader.source | ||
265 | 73 | property alias bottomEdgeTitle: tipLabel.text | ||
266 | 74 | property bool bottomEdgeEnabled: true | ||
267 | 75 | property int bottomEdgeExpandThreshold: page.height * 0.2 | ||
268 | 76 | property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded | ||
269 | 77 | property bool reloadBottomEdgePage: true | ||
270 | 78 | |||
271 | 79 | readonly property alias bottomEdgePage: edgeLoader.item | ||
272 | 80 | readonly property bool isReady: ((bottomEdge.y === 0) && bottomEdgePageLoaded && edgeLoader.item.active) | ||
273 | 81 | readonly property bool isCollapsed: (bottomEdge.y === page.height) | ||
274 | 82 | readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready) | ||
275 | 83 | property var temporaryProperties: null | ||
276 | 84 | |||
277 | 85 | property bool _showEdgePageWhenReady: false | ||
278 | 86 | property int _areaWhenExpanded: 0 | ||
279 | 87 | |||
280 | 88 | signal bottomEdgeReleased() | ||
281 | 89 | signal bottomEdgeDismissed() | ||
282 | 90 | |||
283 | 91 | |||
284 | 92 | function showBottomEdgePage(source, properties) | ||
285 | 93 | { | ||
286 | 94 | edgeLoader.setSource(source, properties) | ||
287 | 95 | temporaryProperties = properties | ||
288 | 96 | _showEdgePageWhenReady = true | ||
289 | 97 | } | ||
290 | 98 | |||
291 | 99 | function setBottomEdgePage(source, properties) | ||
292 | 100 | { | ||
293 | 101 | edgeLoader.setSource(source, properties) | ||
294 | 102 | } | ||
295 | 103 | |||
296 | 104 | function _pushPage() | ||
297 | 105 | { | ||
298 | 106 | if (edgeLoader.status === Loader.Ready) { | ||
299 | 107 | edgeLoader.item.active = true | ||
300 | 108 | page.pageStack.push(edgeLoader.item) | ||
301 | 109 | if (edgeLoader.item.flickable) { | ||
302 | 110 | edgeLoader.item.flickable.contentY = -page.header.height | ||
303 | 111 | edgeLoader.item.flickable.returnToBounds() | ||
304 | 112 | } | ||
305 | 113 | if (edgeLoader.item.ready) | ||
306 | 114 | edgeLoader.item.ready() | ||
307 | 115 | } | ||
308 | 116 | } | ||
309 | 117 | |||
310 | 118 | |||
311 | 119 | Component.onCompleted: { | ||
312 | 120 | // avoid a binding on the expanded height value | ||
313 | 121 | var expandedHeight = height; | ||
314 | 122 | _areaWhenExpanded = expandedHeight; | ||
315 | 123 | } | ||
316 | 124 | |||
317 | 125 | onActiveChanged: { | ||
318 | 126 | if (active) { | ||
319 | 127 | bottomEdge.state = "collapsed" | ||
320 | 128 | } | ||
321 | 129 | } | ||
322 | 130 | |||
323 | 131 | onBottomEdgePageLoadedChanged: { | ||
324 | 132 | if (_showEdgePageWhenReady && bottomEdgePageLoaded) { | ||
325 | 133 | bottomEdge.state = "expanded" | ||
326 | 134 | _showEdgePageWhenReady = false | ||
327 | 135 | } | ||
328 | 136 | } | ||
329 | 137 | |||
330 | 138 | InverseMouseArea { | ||
331 | 139 | anchors.fill: edgeLoader.item | ||
332 | 140 | sensingArea: mainView | ||
333 | 141 | enabled: !tip.hidden | ||
334 | 142 | onPressed: { | ||
335 | 143 | mouse.accepted = false | ||
336 | 144 | page.focus = false | ||
337 | 145 | } | ||
338 | 146 | z: 1 | ||
339 | 147 | } | ||
340 | 148 | |||
341 | 149 | Rectangle { | ||
342 | 150 | id: bgVisual | ||
343 | 151 | |||
344 | 152 | color: "black" | ||
345 | 153 | anchors.fill: page | ||
346 | 154 | opacity: 0.7 * ((page.height - bottomEdge.y) / page.height) | ||
347 | 155 | z: 1 | ||
348 | 156 | } | ||
349 | 157 | |||
350 | 158 | UbuntuShape { | ||
351 | 159 | id: tip | ||
352 | 160 | objectName: "bottomEdgeTip" | ||
353 | 161 | |||
354 | 162 | property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y) | ||
355 | 163 | |||
356 | 164 | enabled: mouseArea.enabled | ||
357 | 165 | anchors { | ||
358 | 166 | bottom: parent.bottom | ||
359 | 167 | horizontalCenter: bottomEdge.horizontalCenter | ||
360 | 168 | bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1) | ||
361 | 169 | Behavior on bottomMargin { | ||
362 | 170 | SequentialAnimation { | ||
363 | 171 | // wait some msecs in case of the focus change again, to avoid flickering | ||
364 | 172 | PauseAnimation { | ||
365 | 173 | duration: 300 | ||
366 | 174 | } | ||
367 | 175 | UbuntuNumberAnimation { | ||
368 | 176 | duration: UbuntuAnimation.SnapDuration | ||
369 | 177 | } | ||
370 | 178 | } | ||
371 | 179 | } | ||
372 | 180 | } | ||
373 | 181 | |||
374 | 182 | z: 1 | ||
375 | 183 | width: tipLabel.paintedWidth + units.gu(6) | ||
376 | 184 | height: bottomEdge.tipHeight + units.gu(1) | ||
377 | 185 | color: Theme.palette.normal.overlay | ||
378 | 186 | Label { | ||
379 | 187 | id: tipLabel | ||
380 | 188 | |||
381 | 189 | anchors { | ||
382 | 190 | top: parent.top | ||
383 | 191 | left: parent.left | ||
384 | 192 | right: parent.right | ||
385 | 193 | } | ||
386 | 194 | height: bottomEdge.tipHeight | ||
387 | 195 | verticalAlignment: Text.AlignVCenter | ||
388 | 196 | horizontalAlignment: Text.AlignHCenter | ||
389 | 197 | opacity: tip.hidden ? 0.0 : 1.0 | ||
390 | 198 | Behavior on opacity { | ||
391 | 199 | UbuntuNumberAnimation { | ||
392 | 200 | duration: UbuntuAnimation.SnapDuration | ||
393 | 201 | } | ||
394 | 202 | } | ||
395 | 203 | } | ||
396 | 204 | } | ||
397 | 205 | |||
398 | 206 | Rectangle { | ||
399 | 207 | id: shadow | ||
400 | 208 | |||
401 | 209 | anchors { | ||
402 | 210 | left: parent.left | ||
403 | 211 | right: parent.right | ||
404 | 212 | bottom: parent.bottom | ||
405 | 213 | } | ||
406 | 214 | height: units.gu(1) | ||
407 | 215 | z: 1 | ||
408 | 216 | opacity: 0.0 | ||
409 | 217 | gradient: Gradient { | ||
410 | 218 | GradientStop { position: 0.0; color: "transparent" } | ||
411 | 219 | GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) } | ||
412 | 220 | } | ||
413 | 221 | } | ||
414 | 222 | |||
415 | 223 | MouseArea { | ||
416 | 224 | id: mouseArea | ||
417 | 225 | |||
418 | 226 | property real previousY: -1 | ||
419 | 227 | property string dragDirection: "None" | ||
420 | 228 | |||
421 | 229 | preventStealing: true | ||
422 | 230 | drag { | ||
423 | 231 | axis: Drag.YAxis | ||
424 | 232 | target: bottomEdge | ||
425 | 233 | minimumY: bottomEdge.pageStartY | ||
426 | 234 | maximumY: page.height | ||
427 | 235 | } | ||
428 | 236 | enabled: edgeLoader.status == Loader.Ready | ||
429 | 237 | |||
430 | 238 | anchors { | ||
431 | 239 | left: parent.left | ||
432 | 240 | right: parent.right | ||
433 | 241 | bottom: parent.bottom | ||
434 | 242 | |||
435 | 243 | } | ||
436 | 244 | height: bottomEdge.tipHeight | ||
437 | 245 | z: 1 | ||
438 | 246 | |||
439 | 247 | onReleased: { | ||
440 | 248 | page.bottomEdgeReleased() | ||
441 | 249 | if ((dragDirection === "BottomToTop") && | ||
442 | 250 | bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) { | ||
443 | 251 | bottomEdge.state = "expanded" | ||
444 | 252 | } else { | ||
445 | 253 | bottomEdge.state = "collapsed" | ||
446 | 254 | } | ||
447 | 255 | previousY = -1 | ||
448 | 256 | dragDirection = "None" | ||
449 | 257 | } | ||
450 | 258 | |||
451 | 259 | onPressed: { | ||
452 | 260 | previousY = mouse.y | ||
453 | 261 | tip.forceActiveFocus() | ||
454 | 262 | } | ||
455 | 263 | |||
456 | 264 | onMouseYChanged: { | ||
457 | 265 | var yOffset = previousY - mouseY | ||
458 | 266 | // skip if was a small move | ||
459 | 267 | if (Math.abs(yOffset) <= units.gu(2)) { | ||
460 | 268 | return | ||
461 | 269 | } | ||
462 | 270 | previousY = mouseY | ||
463 | 271 | dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom" | ||
464 | 272 | } | ||
465 | 273 | } | ||
466 | 274 | |||
467 | 275 | Rectangle { | ||
468 | 276 | id: bottomEdge | ||
469 | 277 | objectName: "bottomEdge" | ||
470 | 278 | |||
471 | 279 | readonly property int tipHeight: units.gu(3) | ||
472 | 280 | readonly property int pageStartY: 0 | ||
473 | 281 | |||
474 | 282 | z: 1 | ||
475 | 283 | color: Theme.palette.normal.background | ||
476 | 284 | clip: true | ||
477 | 285 | anchors { | ||
478 | 286 | left: parent.left | ||
479 | 287 | right: parent.right | ||
480 | 288 | } | ||
481 | 289 | height: page.height | ||
482 | 290 | y: height | ||
483 | 291 | visible: page.bottomEdgeEnabled && !page.isCollapsed | ||
484 | 292 | state: "collapsed" | ||
485 | 293 | states: [ | ||
486 | 294 | State { | ||
487 | 295 | name: "collapsed" | ||
488 | 296 | PropertyChanges { | ||
489 | 297 | target: bottomEdge | ||
490 | 298 | y: bottomEdge.height | ||
491 | 299 | } | ||
492 | 300 | }, | ||
493 | 301 | State { | ||
494 | 302 | name: "expanded" | ||
495 | 303 | PropertyChanges { | ||
496 | 304 | target: bottomEdge | ||
497 | 305 | y: bottomEdge.pageStartY | ||
498 | 306 | } | ||
499 | 307 | }, | ||
500 | 308 | State { | ||
501 | 309 | name: "floating" | ||
502 | 310 | when: mouseArea.drag.active | ||
503 | 311 | PropertyChanges { | ||
504 | 312 | target: shadow | ||
505 | 313 | opacity: 1.0 | ||
506 | 314 | } | ||
507 | 315 | } | ||
508 | 316 | ] | ||
509 | 317 | |||
510 | 318 | transitions: [ | ||
511 | 319 | Transition { | ||
512 | 320 | to: "expanded" | ||
513 | 321 | SequentialAnimation { | ||
514 | 322 | alwaysRunToEnd: true | ||
515 | 323 | |||
516 | 324 | SmoothedAnimation { | ||
517 | 325 | target: bottomEdge | ||
518 | 326 | property: "y" | ||
519 | 327 | duration: UbuntuAnimation.FastDuration | ||
520 | 328 | easing.type: Easing.Linear | ||
521 | 329 | } | ||
522 | 330 | SmoothedAnimation { | ||
523 | 331 | target: edgeLoader | ||
524 | 332 | property: "anchors.topMargin" | ||
525 | 333 | to: - units.gu(4) | ||
526 | 334 | duration: UbuntuAnimation.FastDuration | ||
527 | 335 | easing.type: Easing.Linear | ||
528 | 336 | } | ||
529 | 337 | SmoothedAnimation { | ||
530 | 338 | target: edgeLoader | ||
531 | 339 | property: "anchors.topMargin" | ||
532 | 340 | to: 0 | ||
533 | 341 | duration: UbuntuAnimation.FastDuration | ||
534 | 342 | easing: UbuntuAnimation.StandardEasing | ||
535 | 343 | } | ||
536 | 344 | ScriptAction { | ||
537 | 345 | script: page._pushPage() | ||
538 | 346 | } | ||
539 | 347 | } | ||
540 | 348 | }, | ||
541 | 349 | Transition { | ||
542 | 350 | from: "expanded" | ||
543 | 351 | to: "collapsed" | ||
544 | 352 | SequentialAnimation { | ||
545 | 353 | alwaysRunToEnd: true | ||
546 | 354 | |||
547 | 355 | ScriptAction { | ||
548 | 356 | script: { | ||
549 | 357 | Qt.inputMethod.hide() | ||
550 | 358 | edgeLoader.item.parent = edgeLoader | ||
551 | 359 | edgeLoader.item.anchors.fill = edgeLoader | ||
552 | 360 | edgeLoader.item.active = false | ||
553 | 361 | } | ||
554 | 362 | } | ||
555 | 363 | SmoothedAnimation { | ||
556 | 364 | target: bottomEdge | ||
557 | 365 | property: "y" | ||
558 | 366 | duration: UbuntuAnimation.SlowDuration | ||
559 | 367 | } | ||
560 | 368 | ScriptAction { | ||
561 | 369 | script: { | ||
562 | 370 | // destroy current bottom page | ||
563 | 371 | if (page.reloadBottomEdgePage) { | ||
564 | 372 | edgeLoader.active = false | ||
565 | 373 | // remove properties from old instance | ||
566 | 374 | if (edgeLoader.source !== "") { | ||
567 | 375 | var properties = {} | ||
568 | 376 | if (temporaryProperties !== null) { | ||
569 | 377 | properties = temporaryProperties | ||
570 | 378 | temporaryProperties = null | ||
571 | 379 | } | ||
572 | 380 | |||
573 | 381 | edgeLoader.setSource(edgeLoader.source, properties) | ||
574 | 382 | } | ||
575 | 383 | // tip will receive focus on page active true | ||
576 | 384 | } else { | ||
577 | 385 | tip.forceActiveFocus() | ||
578 | 386 | } | ||
579 | 387 | |||
580 | 388 | // notify | ||
581 | 389 | page.bottomEdgeDismissed() | ||
582 | 390 | |||
583 | 391 | edgeLoader.active = true | ||
584 | 392 | } | ||
585 | 393 | } | ||
586 | 394 | } | ||
587 | 395 | }, | ||
588 | 396 | Transition { | ||
589 | 397 | from: "floating" | ||
590 | 398 | to: "collapsed" | ||
591 | 399 | SmoothedAnimation { | ||
592 | 400 | target: bottomEdge | ||
593 | 401 | property: "y" | ||
594 | 402 | duration: UbuntuAnimation.FastDuration | ||
595 | 403 | } | ||
596 | 404 | } | ||
597 | 405 | ] | ||
598 | 406 | |||
599 | 407 | Loader { | ||
600 | 408 | id: edgeLoader | ||
601 | 409 | |||
602 | 410 | asynchronous: true | ||
603 | 411 | anchors.fill: parent | ||
604 | 412 | //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging | ||
605 | 413 | Binding { | ||
606 | 414 | target: edgeLoader.status === Loader.Ready ? edgeLoader : null | ||
607 | 415 | property: "anchors.topMargin" | ||
608 | 416 | value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0 | ||
609 | 417 | when: !page.isReady | ||
610 | 418 | } | ||
611 | 419 | |||
612 | 420 | onLoaded: { | ||
613 | 421 | tip.forceActiveFocus() | ||
614 | 422 | if (page.isReady && edgeLoader.item.active !== true) { | ||
615 | 423 | page._pushPage() | ||
616 | 424 | } | ||
617 | 425 | } | ||
618 | 426 | } | ||
619 | 427 | } | ||
620 | 428 | } | ||
621 | 429 | 0 | ||
622 | === modified file 'src/qml/MMS/Previewer.qml' | |||
623 | --- src/qml/MMS/Previewer.qml 2015-11-17 14:39:21 +0000 | |||
624 | +++ src/qml/MMS/Previewer.qml 2016-02-01 23:18:57 +0000 | |||
625 | @@ -31,7 +31,7 @@ | |||
626 | 31 | 31 | ||
627 | 32 | function handleAttachment(filePath, handlerType) | 32 | function handleAttachment(filePath, handlerType) |
628 | 33 | { | 33 | { |
630 | 34 | mainStack.push(picker, {"url": filePath, "handler": handlerType}); | 34 | mainStack.addPageToCurrentColumn(previewerPage, picker, {"url": filePath, "handler": handlerType}); |
631 | 35 | actionTriggered() | 35 | actionTriggered() |
632 | 36 | } | 36 | } |
633 | 37 | 37 | ||
634 | @@ -45,23 +45,29 @@ | |||
635 | 45 | previewerPage.handleAttachment(attachment.filePath, ContentHandler.Share) | 45 | previewerPage.handleAttachment(attachment.filePath, ContentHandler.Share) |
636 | 46 | } | 46 | } |
637 | 47 | 47 | ||
641 | 48 | function backAction() | 48 | header: PageHeader { |
642 | 49 | { | 49 | id: pageHeader |
643 | 50 | mainStack.pop() | 50 | |
644 | 51 | property alias leadingActions: leadingBar.actions | ||
645 | 52 | property alias trailingActions: trailingBar.actions | ||
646 | 53 | |||
647 | 54 | title: previewerPage.title | ||
648 | 55 | leadingActionBar { | ||
649 | 56 | id: leadingBar | ||
650 | 57 | } | ||
651 | 58 | |||
652 | 59 | trailingActionBar { | ||
653 | 60 | id: trailingBar | ||
654 | 61 | } | ||
655 | 51 | } | 62 | } |
656 | 52 | 63 | ||
657 | 53 | title: "" | ||
658 | 54 | state: "default" | 64 | state: "default" |
659 | 55 | states: [ | 65 | states: [ |
661 | 56 | PageHeadState { | 66 | State { |
662 | 67 | id: defaultState | ||
663 | 57 | name: "default" | 68 | name: "default" |
671 | 58 | head: previewerPage.head | 69 | |
672 | 59 | backAction: Action { | 70 | property list<QtObject> trailingActions: [ |
666 | 60 | iconName: "back" | ||
667 | 61 | text: i18n.tr("Back") | ||
668 | 62 | onTriggered: previewerPage.backAction() | ||
669 | 63 | } | ||
670 | 64 | actions: [ | ||
673 | 65 | Action { | 71 | Action { |
674 | 66 | objectName: "saveButton" | 72 | objectName: "saveButton" |
675 | 67 | text: i18n.tr("Save") | 73 | text: i18n.tr("Save") |
676 | @@ -75,6 +81,11 @@ | |||
677 | 75 | onTriggered: previewerPage.shareAttchment() | 81 | onTriggered: previewerPage.shareAttchment() |
678 | 76 | } | 82 | } |
679 | 77 | ] | 83 | ] |
680 | 84 | PropertyChanges { | ||
681 | 85 | target: pageHeader | ||
682 | 86 | |||
683 | 87 | trailingActions: defaultState.trailingActions | ||
684 | 88 | } | ||
685 | 78 | } | 89 | } |
686 | 79 | ] | 90 | ] |
687 | 80 | 91 | ||
688 | @@ -109,11 +120,11 @@ | |||
689 | 109 | 120 | ||
690 | 110 | onPeerSelected: { | 121 | onPeerSelected: { |
691 | 111 | picker.curTransfer = peer.request(); | 122 | picker.curTransfer = peer.request(); |
693 | 112 | mainStack.pop(); | 123 | mainStack.removePage(picker); |
694 | 113 | if (picker.curTransfer.state === ContentTransfer.InProgress) | 124 | if (picker.curTransfer.state === ContentTransfer.InProgress) |
695 | 114 | picker.__exportItems(picker.url); | 125 | picker.__exportItems(picker.url); |
696 | 115 | } | 126 | } |
698 | 116 | onCancelPressed: mainStack.pop(); | 127 | onCancelPressed: mainStack.removePage(picker); |
699 | 117 | } | 128 | } |
700 | 118 | 129 | ||
701 | 119 | Connections { | 130 | Connections { |
702 | 120 | 131 | ||
703 | === modified file 'src/qml/MMS/PreviewerImage.qml' | |||
704 | --- src/qml/MMS/PreviewerImage.qml 2016-02-01 23:18:57 +0000 | |||
705 | +++ src/qml/MMS/PreviewerImage.qml 2016-02-01 23:18:57 +0000 | |||
706 | @@ -25,6 +25,7 @@ | |||
707 | 25 | Previewer { | 25 | Previewer { |
708 | 26 | id: imagePreviewer | 26 | id: imagePreviewer |
709 | 27 | 27 | ||
710 | 28 | // FIXME: this won't work correctly in windowed mode | ||
711 | 28 | Component.onCompleted: application.fullscreen = true | 29 | Component.onCompleted: application.fullscreen = true |
712 | 29 | Component.onDestruction: application.fullscreen = false | 30 | Component.onDestruction: application.fullscreen = false |
713 | 30 | 31 | ||
714 | 31 | 32 | ||
715 | === modified file 'src/qml/MMS/PreviewerMultipleContacts.qml' | |||
716 | --- src/qml/MMS/PreviewerMultipleContacts.qml 2015-11-19 00:34:45 +0000 | |||
717 | +++ src/qml/MMS/PreviewerMultipleContacts.qml 2016-02-01 23:18:57 +0000 | |||
718 | @@ -42,7 +42,12 @@ | |||
719 | 42 | MultipleSelectionListView { | 42 | MultipleSelectionListView { |
720 | 43 | id: contactList | 43 | id: contactList |
721 | 44 | 44 | ||
723 | 45 | anchors.fill: parent | 45 | anchors { |
724 | 46 | top: root.header.bottom | ||
725 | 47 | bottom: parent.bottom | ||
726 | 48 | left: parent.left | ||
727 | 49 | right: parent.right | ||
728 | 50 | } | ||
729 | 46 | listModel: thumbnail.vcard.contacts | 51 | listModel: thumbnail.vcard.contacts |
730 | 47 | listDelegate: ContactDelegate { | 52 | listDelegate: ContactDelegate { |
731 | 48 | id: contactDelegate | 53 | id: contactDelegate |
732 | @@ -51,7 +56,7 @@ | |||
733 | 51 | property var contact: thumbnail.vcard.contacts[index] | 56 | property var contact: thumbnail.vcard.contacts[index] |
734 | 52 | 57 | ||
735 | 53 | onClicked: { | 58 | onClicked: { |
737 | 54 | mainStack.push(sigleContatPreviewer, {'contact': contact}) | 59 | mainStack.addComponentToCurrentColumnSync(root, sigleContatPreviewer, {'contact': contact}) |
738 | 55 | } | 60 | } |
739 | 56 | } | 61 | } |
740 | 57 | } | 62 | } |
741 | 58 | 63 | ||
742 | === modified file 'src/qml/MMS/PreviewerVideo.qml' | |||
743 | --- src/qml/MMS/PreviewerVideo.qml 2016-02-01 23:18:57 +0000 | |||
744 | +++ src/qml/MMS/PreviewerVideo.qml 2016-02-01 23:18:57 +0000 | |||
745 | @@ -30,6 +30,7 @@ | |||
746 | 30 | title: i18n.tr("Video Preview") | 30 | title: i18n.tr("Video Preview") |
747 | 31 | clip: true | 31 | clip: true |
748 | 32 | 32 | ||
749 | 33 | // FIXME: this won't work correctly in windowed mode | ||
750 | 33 | Component.onCompleted: { | 34 | Component.onCompleted: { |
751 | 34 | application.fullscreen = true | 35 | application.fullscreen = true |
752 | 35 | // Load Video player after toggling fullscreen to reduce flickering | 36 | // Load Video player after toggling fullscreen to reduce flickering |
753 | 36 | 37 | ||
754 | === modified file 'src/qml/MMSDelegate.qml' | |||
755 | --- src/qml/MMSDelegate.qml 2016-02-01 23:18:57 +0000 | |||
756 | +++ src/qml/MMSDelegate.qml 2016-02-01 23:18:57 +0000 | |||
757 | @@ -45,7 +45,7 @@ | |||
758 | 45 | var properties = {} | 45 | var properties = {} |
759 | 46 | properties["attachment"] = attachment.item.attachment | 46 | properties["attachment"] = attachment.item.attachment |
760 | 47 | properties["thumbnail"] = attachment.item | 47 | properties["thumbnail"] = attachment.item |
762 | 48 | mainStack.push(Qt.resolvedUrl(attachment.item.previewer), properties) | 48 | mainStack.addFileToCurrentColumnSync(messages.basePage, Qt.resolvedUrl(attachment.item.previewer), properties) |
763 | 49 | } | 49 | } |
764 | 50 | } | 50 | } |
765 | 51 | 51 | ||
766 | 52 | 52 | ||
767 | === modified file 'src/qml/MainPage.qml' | |||
768 | --- src/qml/MainPage.qml 2016-02-01 23:18:57 +0000 | |||
769 | +++ src/qml/MainPage.qml 2016-02-01 23:18:57 +0000 | |||
770 | @@ -23,29 +23,27 @@ | |||
771 | 23 | import Ubuntu.History 0.1 | 23 | import Ubuntu.History 0.1 |
772 | 24 | import "dateUtils.js" as DateUtils | 24 | import "dateUtils.js" as DateUtils |
773 | 25 | 25 | ||
775 | 26 | LocalPageWithBottomEdge { | 26 | Page { |
776 | 27 | id: mainPage | 27 | id: mainPage |
777 | 28 | property alias selectionMode: threadList.isInSelectionMode | 28 | property alias selectionMode: threadList.isInSelectionMode |
778 | 29 | property bool searching: false | 29 | property bool searching: false |
779 | 30 | property bool isEmpty: threadCount == 0 && !threadModel.canFetchMore | ||
780 | 30 | property alias threadCount: threadList.count | 31 | property alias threadCount: threadList.count |
781 | 32 | property alias displayedThreadIndex: threadList.currentIndex | ||
782 | 33 | |||
783 | 34 | property var _messagesPage: null | ||
784 | 31 | 35 | ||
785 | 32 | function startSelection() { | 36 | function startSelection() { |
786 | 33 | threadList.startSelection() | 37 | threadList.startSelection() |
787 | 34 | } | 38 | } |
788 | 35 | 39 | ||
789 | 36 | state: selectionMode ? "select" : searching ? "search" : "default" | ||
790 | 37 | title: selectionMode ? " " : i18n.tr("Messages") | ||
791 | 38 | flickable: null | ||
792 | 39 | |||
793 | 40 | bottomEdgeEnabled: !selectionMode && !searching | ||
794 | 41 | bottomEdgeTitle: i18n.tr("+") | ||
795 | 42 | bottomEdgePageComponent: Messages { active: false } | ||
796 | 43 | |||
797 | 44 | TextField { | 40 | TextField { |
798 | 45 | id: searchField | 41 | id: searchField |
799 | 46 | objectName: "searchField" | 42 | objectName: "searchField" |
800 | 47 | visible: mainPage.searching | 43 | visible: mainPage.searching |
801 | 48 | anchors { | 44 | anchors { |
802 | 45 | top: parent.top | ||
803 | 46 | topMargin: units.gu(1) | ||
804 | 49 | left: parent.left | 47 | left: parent.left |
805 | 50 | right: parent.right | 48 | right: parent.right |
806 | 51 | rightMargin: units.gu(2) | 49 | rightMargin: units.gu(2) |
807 | @@ -60,11 +58,30 @@ | |||
808 | 60 | } | 58 | } |
809 | 61 | } | 59 | } |
810 | 62 | 60 | ||
811 | 61 | header: PageHeader { | ||
812 | 62 | id: pageHeader | ||
813 | 63 | |||
814 | 64 | property alias leadingActions: leadingBar.actions | ||
815 | 65 | property alias trailingActions: trailingBar.actions | ||
816 | 66 | |||
817 | 67 | title: i18n.tr("Messages") | ||
818 | 68 | flickable: threadList | ||
819 | 69 | leadingActionBar { | ||
820 | 70 | id: leadingBar | ||
821 | 71 | } | ||
822 | 72 | |||
823 | 73 | trailingActionBar { | ||
824 | 74 | id: trailingBar | ||
825 | 75 | } | ||
826 | 76 | } | ||
827 | 77 | |||
828 | 63 | states: [ | 78 | states: [ |
830 | 64 | PageHeadState { | 79 | State { |
831 | 80 | id: defaultState | ||
832 | 65 | name: "default" | 81 | name: "default" |
835 | 66 | head: mainPage.head | 82 | when: !searching && !selectionMode |
836 | 67 | actions: [ | 83 | |
837 | 84 | property list<QtObject> trailingActions: [ | ||
838 | 68 | Action { | 85 | Action { |
839 | 69 | objectName: "searchAction" | 86 | objectName: "searchAction" |
840 | 70 | iconName: "search" | 87 | iconName: "search" |
841 | @@ -77,38 +94,76 @@ | |||
842 | 77 | objectName: "settingsAction" | 94 | objectName: "settingsAction" |
843 | 78 | text: i18n.tr("Settings") | 95 | text: i18n.tr("Settings") |
844 | 79 | iconName: "settings" | 96 | iconName: "settings" |
846 | 80 | onTriggered: pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) | 97 | onTriggered: { |
847 | 98 | emptyStack() | ||
848 | 99 | pageStack.addFileToNextColumnSync(mainPage, Qt.resolvedUrl("SettingsPage.qml")) | ||
849 | 100 | } | ||
850 | 101 | }, | ||
851 | 102 | Action { | ||
852 | 103 | objectName: "newMessageAction" | ||
853 | 104 | text: i18n.tr("New message") | ||
854 | 105 | iconName: "add" | ||
855 | 106 | visible: dualPanel | ||
856 | 107 | onTriggered: mainView.bottomEdge.commit() | ||
857 | 81 | } | 108 | } |
858 | 109 | |||
859 | 82 | ] | 110 | ] |
860 | 111 | |||
861 | 112 | PropertyChanges { | ||
862 | 113 | target: pageHeader | ||
863 | 114 | trailingActions: defaultState.trailingActions | ||
864 | 115 | leadingActions: [] | ||
865 | 116 | } | ||
866 | 83 | }, | 117 | }, |
868 | 84 | PageHeadState { | 118 | State { |
869 | 119 | id: searchState | ||
870 | 85 | name: "search" | 120 | name: "search" |
880 | 86 | head: mainPage.head | 121 | when: searching |
881 | 87 | backAction: Action { | 122 | |
882 | 88 | objectName: "cancelSearch" | 123 | property list<QtObject> leadingActions: [ |
883 | 89 | visible: mainPage.searching | 124 | Action { |
884 | 90 | iconName: "back" | 125 | objectName: "cancelSearch" |
885 | 91 | text: i18n.tr("Cancel") | 126 | visible: mainPage.searching |
886 | 92 | onTriggered: { | 127 | iconName: "back" |
887 | 93 | searchField.text = "" | 128 | text: i18n.tr("Cancel") |
888 | 94 | mainPage.searching = false | 129 | onTriggered: { |
889 | 130 | searchField.text = "" | ||
890 | 131 | mainPage.searching = false | ||
891 | 132 | } | ||
892 | 95 | } | 133 | } |
893 | 134 | ] | ||
894 | 135 | |||
895 | 136 | PropertyChanges { | ||
896 | 137 | target: pageHeader | ||
897 | 138 | contents: searchField | ||
898 | 139 | leadingActions: searchState.leadingActions | ||
899 | 140 | trailingActions: [] | ||
900 | 96 | } | 141 | } |
901 | 97 | contents: searchField | ||
902 | 98 | }, | 142 | }, |
912 | 99 | PageHeadState { | 143 | State { |
913 | 100 | name: "select" | 144 | id: selectionState |
914 | 101 | head: mainPage.head | 145 | name: "selection" |
915 | 102 | backAction: Action { | 146 | when: selectionMode |
916 | 103 | objectName: "selectionModeCancelAction" | 147 | |
917 | 104 | iconName: "back" | 148 | property list<QtObject> leadingActions: [ |
918 | 105 | onTriggered: threadList.cancelSelection() | 149 | Action { |
919 | 106 | } | 150 | objectName: "selectionModeCancelAction" |
920 | 107 | actions: [ | 151 | iconName: "back" |
921 | 152 | onTriggered: threadList.cancelSelection() | ||
922 | 153 | } | ||
923 | 154 | ] | ||
924 | 155 | |||
925 | 156 | property list<QtObject> trailingActions: [ | ||
926 | 108 | Action { | 157 | Action { |
927 | 109 | objectName: "selectionModeSelectAllAction" | 158 | objectName: "selectionModeSelectAllAction" |
928 | 110 | iconName: "select" | 159 | iconName: "select" |
930 | 111 | onTriggered: threadList.selectAll() | 160 | onTriggered: { |
931 | 161 | if (threadList.selectedItems.count === threadList.count) { | ||
932 | 162 | threadList.clearSelection() | ||
933 | 163 | } else { | ||
934 | 164 | threadList.selectAll() | ||
935 | 165 | } | ||
936 | 166 | } | ||
937 | 112 | }, | 167 | }, |
938 | 113 | Action { | 168 | Action { |
939 | 114 | objectName: "selectionModeDeleteAction" | 169 | objectName: "selectionModeDeleteAction" |
940 | @@ -117,39 +172,18 @@ | |||
941 | 117 | onTriggered: threadList.endSelection() | 172 | onTriggered: threadList.endSelection() |
942 | 118 | } | 173 | } |
943 | 119 | ] | 174 | ] |
944 | 175 | PropertyChanges { | ||
945 | 176 | target: pageHeader | ||
946 | 177 | title: " " | ||
947 | 178 | leadingActions: selectionState.leadingActions | ||
948 | 179 | trailingActions: selectionState.trailingActions | ||
949 | 180 | } | ||
950 | 120 | } | 181 | } |
951 | 121 | ] | 182 | ] |
952 | 122 | 183 | ||
954 | 123 | Item { | 184 | EmptyState { |
955 | 124 | id: emptyStateScreen | 185 | id: emptyStateScreen |
984 | 125 | anchors.left: parent.left | 186 | visible: mainPage.isEmpty && !mainView.dualPanel |
957 | 126 | anchors.leftMargin: units.gu(6) | ||
958 | 127 | anchors.right: parent.right | ||
959 | 128 | anchors.rightMargin: units.gu(6) | ||
960 | 129 | height: childrenRect.height | ||
961 | 130 | anchors.verticalCenter: parent.verticalCenter | ||
962 | 131 | visible: threadCount == 0 && !threadModel.canFetchMore | ||
963 | 132 | Icon { | ||
964 | 133 | id: emptyStateIcon | ||
965 | 134 | anchors.top: emptyStateScreen.top | ||
966 | 135 | anchors.horizontalCenter: parent.horizontalCenter | ||
967 | 136 | height: units.gu(5) | ||
968 | 137 | width: height | ||
969 | 138 | opacity: 0.3 | ||
970 | 139 | name: "message" | ||
971 | 140 | } | ||
972 | 141 | Label { | ||
973 | 142 | id: emptyStateLabel | ||
974 | 143 | anchors.top: emptyStateIcon.bottom | ||
975 | 144 | anchors.topMargin: units.gu(2) | ||
976 | 145 | anchors.left: parent.left | ||
977 | 146 | anchors.right: parent.right | ||
978 | 147 | text: i18n.tr("Compose a new message by swiping up from the bottom of the screen.") | ||
979 | 148 | color: "#5d5d5d" | ||
980 | 149 | fontSize: "x-large" | ||
981 | 150 | wrapMode: Text.WordWrap | ||
982 | 151 | horizontalAlignment: Text.AlignHCenter | ||
983 | 152 | } | ||
985 | 153 | } | 187 | } |
986 | 154 | 188 | ||
987 | 155 | Component { | 189 | Component { |
988 | @@ -188,8 +222,18 @@ | |||
989 | 188 | clip: true | 222 | clip: true |
990 | 189 | cacheBuffer: threadList.height * 2 | 223 | cacheBuffer: threadList.height * 2 |
991 | 190 | section.property: "eventDate" | 224 | section.property: "eventDate" |
992 | 225 | currentIndex: -1 | ||
993 | 191 | //spacing: searchField.text === "" ? units.gu(-2) : 0 | 226 | //spacing: searchField.text === "" ? units.gu(-2) : 0 |
994 | 192 | section.delegate: searching && searchField.text !== "" ? null : sectionDelegate | 227 | section.delegate: searching && searchField.text !== "" ? null : sectionDelegate |
995 | 228 | header: ListItem.Standard { | ||
996 | 229 | id: newItem | ||
997 | 230 | height: mainView.bottomEdge.status === BottomEdge.Committed ? units.gu(10) : 0 | ||
998 | 231 | text: i18n.tr("New message") | ||
999 | 232 | iconName: "message-new" | ||
1000 | 233 | iconFrame: false | ||
1001 | 234 | selected: true | ||
1002 | 235 | } | ||
1003 | 236 | |||
1004 | 193 | listDelegate: ThreadDelegate { | 237 | listDelegate: ThreadDelegate { |
1005 | 194 | id: threadDelegate | 238 | id: threadDelegate |
1006 | 195 | // FIXME: find a better unique name | 239 | // FIXME: find a better unique name |
1007 | @@ -201,7 +245,15 @@ | |||
1008 | 201 | } | 245 | } |
1009 | 202 | height: units.gu(8) | 246 | height: units.gu(8) |
1010 | 203 | selectionMode: threadList.isInSelectionMode | 247 | selectionMode: threadList.isInSelectionMode |
1012 | 204 | selected: threadList.isSelected(threadDelegate) | 248 | selected: { |
1013 | 249 | if (selectionMode) { | ||
1014 | 250 | return threadList.isSelected(threadDelegate) | ||
1015 | 251 | } else { | ||
1016 | 252 | // FIXME: there might be a better way of doing this | ||
1017 | 253 | return index === threadList.currentIndex && mainView.bottomEdge.status !== BottomEdge.Committed | ||
1018 | 254 | } | ||
1019 | 255 | } | ||
1020 | 256 | |||
1021 | 205 | searchTerm: mainPage.searching ? searchField.text : "" | 257 | searchTerm: mainPage.searching ? searchField.text : "" |
1022 | 206 | onItemClicked: { | 258 | onItemClicked: { |
1023 | 207 | if (threadList.isInSelectionMode) { | 259 | if (threadList.isInSelectionMode) { |
1024 | @@ -222,7 +274,11 @@ | |||
1025 | 222 | if (displayedEvent != null) { | 274 | if (displayedEvent != null) { |
1026 | 223 | properties["scrollToEventId"] = displayedEvent.eventId | 275 | properties["scrollToEventId"] = displayedEvent.eventId |
1027 | 224 | } | 276 | } |
1029 | 225 | mainStack.push(Qt.resolvedUrl("Messages.qml"), properties) | 277 | emptyStack() |
1030 | 278 | mainStack.addComponentToNextColumnSync(mainPage, messagesWithBottomEdge, properties) | ||
1031 | 279 | |||
1032 | 280 | // mark this item as current | ||
1033 | 281 | threadList.currentIndex = index | ||
1034 | 226 | } | 282 | } |
1035 | 227 | } | 283 | } |
1036 | 228 | onItemPressAndHold: { | 284 | onItemPressAndHold: { |
1037 | @@ -242,6 +298,13 @@ | |||
1038 | 242 | mainView.removeThreads(threadsToRemove); | 298 | mainView.removeThreads(threadsToRemove); |
1039 | 243 | } | 299 | } |
1040 | 244 | } | 300 | } |
1041 | 301 | |||
1042 | 302 | Binding { | ||
1043 | 303 | target: threadList | ||
1044 | 304 | property: 'contentY' | ||
1045 | 305 | value: -threadList.headerItem.height | ||
1046 | 306 | when: mainView.composingNewMessage | ||
1047 | 307 | } | ||
1048 | 245 | } | 308 | } |
1049 | 246 | 309 | ||
1050 | 247 | KeyboardRectangle { | 310 | KeyboardRectangle { |
1051 | @@ -252,4 +315,12 @@ | |||
1052 | 252 | flickableItem: threadList | 315 | flickableItem: threadList |
1053 | 253 | align: Qt.AlignTrailing | 316 | align: Qt.AlignTrailing |
1054 | 254 | } | 317 | } |
1055 | 318 | |||
1056 | 319 | Loader { | ||
1057 | 320 | id: bottomEdgeLoader | ||
1058 | 321 | active: !selectionMode && !searching && !mainView.dualPanel | ||
1059 | 322 | sourceComponent: MessagingBottomEdge { | ||
1060 | 323 | parent: mainPage | ||
1061 | 324 | } | ||
1062 | 325 | } | ||
1063 | 255 | } | 326 | } |
1064 | 256 | 327 | ||
1065 | === modified file 'src/qml/Messages.qml' | |||
1066 | --- src/qml/Messages.qml 2016-02-01 23:18:57 +0000 | |||
1067 | +++ src/qml/Messages.qml 2016-02-01 23:18:57 +0000 | |||
1068 | @@ -63,6 +63,19 @@ | |||
1069 | 63 | property QtObject presenceRequest: presenceItem | 63 | property QtObject presenceRequest: presenceItem |
1070 | 64 | property var accountsModel: getAccountsModel() | 64 | property var accountsModel: getAccountsModel() |
1071 | 65 | property alias oskEnabled: keyboard.oskEnabled | 65 | property alias oskEnabled: keyboard.oskEnabled |
1072 | 66 | property bool isReady: false | ||
1073 | 67 | property string firstRecipientAlias: ((contactWatcher.isUnknown && | ||
1074 | 68 | contactWatcher.isInteractive) || | ||
1075 | 69 | contactWatcher.alias === "") ? contactWatcher.identifier : contactWatcher.alias | ||
1076 | 70 | |||
1077 | 71 | |||
1078 | 72 | // When using this view from the bottom edge, we are not in the stack, so we need to push on top of the parent page | ||
1079 | 73 | property var basePage: messages | ||
1080 | 74 | |||
1081 | 75 | property bool startedFromBottomEdge: false | ||
1082 | 76 | |||
1083 | 77 | signal ready | ||
1084 | 78 | signal cancel | ||
1085 | 66 | 79 | ||
1086 | 67 | function getAccountsModel() { | 80 | function getAccountsModel() { |
1087 | 68 | var accounts = [] | 81 | var accounts = [] |
1088 | @@ -221,99 +234,6 @@ | |||
1089 | 221 | return thread | 234 | return thread |
1090 | 222 | } | 235 | } |
1091 | 223 | 236 | ||
1092 | 224 | Connections { | ||
1093 | 225 | target: telepathyHelper | ||
1094 | 226 | onSetupReady: { | ||
1095 | 227 | // force reevaluation | ||
1096 | 228 | messages.account = Qt.binding(getCurrentAccount) | ||
1097 | 229 | messages.phoneAccount = Qt.binding(isPhoneAccount) | ||
1098 | 230 | head.sections.model = Qt.binding(getSectionsModel) | ||
1099 | 231 | head.sections.selectedIndex = Qt.binding(getSelectedIndex) | ||
1100 | 232 | } | ||
1101 | 233 | } | ||
1102 | 234 | |||
1103 | 235 | |||
1104 | 236 | Connections { | ||
1105 | 237 | target: chatManager | ||
1106 | 238 | onChatEntryCreated: { | ||
1107 | 239 | // TODO: track using chatId and not participants | ||
1108 | 240 | if (accountId == account.accountId && | ||
1109 | 241 | firstParticipant && participants[0] == firstParticipant.identifier) { | ||
1110 | 242 | messages.chatEntry = chatEntry | ||
1111 | 243 | } | ||
1112 | 244 | } | ||
1113 | 245 | onChatsChanged: { | ||
1114 | 246 | for (var i in chatManager.chats) { | ||
1115 | 247 | var chat = chatManager.chats[i] | ||
1116 | 248 | // TODO: track using chatId and not participants | ||
1117 | 249 | if (chat.account.accountId == account.accountId && | ||
1118 | 250 | firstParticipant && chat.participants[0] == firstParticipant.identifier) { | ||
1119 | 251 | messages.chatEntry = chat | ||
1120 | 252 | return | ||
1121 | 253 | } | ||
1122 | 254 | } | ||
1123 | 255 | messages.chatEntry = null | ||
1124 | 256 | } | ||
1125 | 257 | } | ||
1126 | 258 | |||
1127 | 259 | Timer { | ||
1128 | 260 | id: typingTimer | ||
1129 | 261 | interval: 6000 | ||
1130 | 262 | onTriggered: { | ||
1131 | 263 | messages.userTyping = false; | ||
1132 | 264 | } | ||
1133 | 265 | } | ||
1134 | 266 | |||
1135 | 267 | Repeater { | ||
1136 | 268 | model: messages.chatEntry ? messages.chatEntry.chatStates : null | ||
1137 | 269 | Item { | ||
1138 | 270 | function processChatState() { | ||
1139 | 271 | if (modelData.state == ChatEntry.ChannelChatStateComposing) { | ||
1140 | 272 | messages.userTyping = true | ||
1141 | 273 | typingTimer.start() | ||
1142 | 274 | } else { | ||
1143 | 275 | messages.userTyping = false | ||
1144 | 276 | } | ||
1145 | 277 | } | ||
1146 | 278 | Component.onCompleted: processChatState() | ||
1147 | 279 | Connections { | ||
1148 | 280 | target: modelData | ||
1149 | 281 | onStateChanged: processChatState() | ||
1150 | 282 | } | ||
1151 | 283 | } | ||
1152 | 284 | } | ||
1153 | 285 | |||
1154 | 286 | MessagesHeader { | ||
1155 | 287 | id: header | ||
1156 | 288 | width: parent ? parent.width - units.gu(2) : undefined | ||
1157 | 289 | height: units.gu(5) | ||
1158 | 290 | title: messages.title | ||
1159 | 291 | subtitle: { | ||
1160 | 292 | if (userTyping) { | ||
1161 | 293 | return i18n.tr("Typing..") | ||
1162 | 294 | } | ||
1163 | 295 | switch (presenceRequest.type) { | ||
1164 | 296 | case PresenceRequest.PresenceTypeAvailable: | ||
1165 | 297 | return i18n.tr("Online") | ||
1166 | 298 | case PresenceRequest.PresenceTypeOffline: | ||
1167 | 299 | return i18n.tr("Offline") | ||
1168 | 300 | case PresenceRequest.PresenceTypeAway: | ||
1169 | 301 | return i18n.tr("Away") | ||
1170 | 302 | case PresenceRequest.PresenceTypeBusy: | ||
1171 | 303 | return i18n.tr("Busy") | ||
1172 | 304 | default: | ||
1173 | 305 | return "" | ||
1174 | 306 | } | ||
1175 | 307 | } | ||
1176 | 308 | visible: true | ||
1177 | 309 | } | ||
1178 | 310 | |||
1179 | 311 | head { | ||
1180 | 312 | id: head | ||
1181 | 313 | sections.model: getSectionsModel() | ||
1182 | 314 | sections.selectedIndex: getSelectedIndex() | ||
1183 | 315 | } | ||
1184 | 316 | |||
1185 | 317 | function sendMessageNetworkCheck() { | 237 | function sendMessageNetworkCheck() { |
1186 | 318 | if (messages.account.simLocked) { | 238 | if (messages.account.simLocked) { |
1187 | 319 | Qt.inputMethod.hide() | 239 | Qt.inputMethod.hide() |
1188 | @@ -363,7 +283,7 @@ | |||
1189 | 363 | if (event.senderId == "self" && event.accountId != messages.account.accountId) { | 283 | if (event.senderId == "self" && event.accountId != messages.account.accountId) { |
1190 | 364 | var tmpAccount = telepathyHelper.accountForId(event.accountId) | 284 | var tmpAccount = telepathyHelper.accountForId(event.accountId) |
1191 | 365 | if (!tmpAccount || (tmpAccount.type == AccountEntry.MultimediaAccount && messages.account.type == AccountEntry.PhoneAccount)) { | 285 | if (!tmpAccount || (tmpAccount.type == AccountEntry.MultimediaAccount && messages.account.type == AccountEntry.PhoneAccount)) { |
1193 | 366 | // we don't add the information event if the last outgoing message | 286 | // we don't add the information event if the last outgoing message |
1194 | 367 | // was a fallback to a multimedia service | 287 | // was a fallback to a multimedia service |
1195 | 368 | break; | 288 | break; |
1196 | 369 | } | 289 | } |
1197 | @@ -425,7 +345,7 @@ | |||
1198 | 425 | var isSelfContactKnown = account.selfContactId != "" | 345 | var isSelfContactKnown = account.selfContactId != "" |
1199 | 426 | if (isMmsGroupChat && !isSelfContactKnown) { | 346 | if (isMmsGroupChat && !isSelfContactKnown) { |
1200 | 427 | // TODO: inform the user to enter the phone number of the selected sim card manually | 347 | // TODO: inform the user to enter the phone number of the selected sim card manually |
1202 | 428 | // and use it in the telepathy-ofono account as selfContactId. | 348 | // and use it in the telepathy-ofono account as selfContactId. |
1203 | 429 | return false | 349 | return false |
1204 | 430 | } | 350 | } |
1205 | 431 | var fallbackAccountId = chatManager.sendMessage(messages.account.accountId, participantIds, text, attachments, properties) | 351 | var fallbackAccountId = chatManager.sendMessage(messages.account.accountId, participantIds, text, attachments, properties) |
1206 | @@ -448,102 +368,6 @@ | |||
1207 | 448 | return true | 368 | return true |
1208 | 449 | } | 369 | } |
1209 | 450 | 370 | ||
1210 | 451 | PresenceRequest { | ||
1211 | 452 | id: presenceItem | ||
1212 | 453 | accountId: { | ||
1213 | 454 | // if this is a regular sms chat, try requesting the presence on | ||
1214 | 455 | // a multimedia account | ||
1215 | 456 | if (!account) { | ||
1216 | 457 | return "" | ||
1217 | 458 | } | ||
1218 | 459 | if (account.type == AccountEntry.PhoneAccount) { | ||
1219 | 460 | for (var i in telepathyHelper.accounts) { | ||
1220 | 461 | var tmpAccount = telepathyHelper.accounts[i] | ||
1221 | 462 | if (tmpAccount.type == AccountEntry.MultimediaAccount) { | ||
1222 | 463 | return tmpAccount.accountId | ||
1223 | 464 | } | ||
1224 | 465 | } | ||
1225 | 466 | return "" | ||
1226 | 467 | } | ||
1227 | 468 | return account.accountId | ||
1228 | 469 | } | ||
1229 | 470 | // we just request presence on 1-1 chats | ||
1230 | 471 | identifier: participants.length == 1 ? participants[0].identifier : "" | ||
1231 | 472 | } | ||
1232 | 473 | |||
1233 | 474 | // this is necessary to automatically update the view when the | ||
1234 | 475 | // default account changes in system settings | ||
1235 | 476 | Connections { | ||
1236 | 477 | target: mainView | ||
1237 | 478 | onAccountChanged: { | ||
1238 | 479 | if (!messages.phoneAccount) { | ||
1239 | 480 | return | ||
1240 | 481 | } | ||
1241 | 482 | messages.account = mainView.account | ||
1242 | 483 | } | ||
1243 | 484 | } | ||
1244 | 485 | |||
1245 | 486 | ActivityIndicator { | ||
1246 | 487 | id: activityIndicator | ||
1247 | 488 | anchors { | ||
1248 | 489 | verticalCenter: parent.verticalCenter | ||
1249 | 490 | horizontalCenter: parent.horizontalCenter | ||
1250 | 491 | } | ||
1251 | 492 | running: isSearching | ||
1252 | 493 | visible: running | ||
1253 | 494 | } | ||
1254 | 495 | |||
1255 | 496 | flickable: null | ||
1256 | 497 | |||
1257 | 498 | property bool isReady: false | ||
1258 | 499 | signal ready | ||
1259 | 500 | onReady: { | ||
1260 | 501 | isReady = true | ||
1261 | 502 | if (participants.length === 0 && keyboardFocus) | ||
1262 | 503 | multiRecipient.forceFocus() | ||
1263 | 504 | } | ||
1264 | 505 | |||
1265 | 506 | property string firstRecipientAlias: ((contactWatcher.isUnknown && | ||
1266 | 507 | contactWatcher.isInteractive) || | ||
1267 | 508 | contactWatcher.alias === "") ? contactWatcher.identifier : contactWatcher.alias | ||
1268 | 509 | title: { | ||
1269 | 510 | if (selectionMode || participants.length == 0) { | ||
1270 | 511 | return " " | ||
1271 | 512 | } | ||
1272 | 513 | |||
1273 | 514 | if (landscape) { | ||
1274 | 515 | return "" | ||
1275 | 516 | } | ||
1276 | 517 | if (participants.length > 0) { | ||
1277 | 518 | if (participants.length == 1) { | ||
1278 | 519 | return firstRecipientAlias | ||
1279 | 520 | } else { | ||
1280 | 521 | // TRANSLATORS: %1 refers to the number of participants in a group chat | ||
1281 | 522 | return i18n.tr("Group (%1)").arg(participants.length) | ||
1282 | 523 | } | ||
1283 | 524 | } | ||
1284 | 525 | return i18n.tr("New Message") | ||
1285 | 526 | } | ||
1286 | 527 | |||
1287 | 528 | Component.onCompleted: { | ||
1288 | 529 | if (messages.accountId !== "") { | ||
1289 | 530 | var account = telepathyHelper.accountForId(messages.accountId) | ||
1290 | 531 | if (account && account.type == AccountEntry.MultimediaAccount) { | ||
1291 | 532 | // fallback the first available phone account | ||
1292 | 533 | if (telepathyHelper.phoneAccounts.length > 0) { | ||
1293 | 534 | messages.accountId = telepathyHelper.phoneAccounts[0].accountId | ||
1294 | 535 | } | ||
1295 | 536 | } | ||
1296 | 537 | } | ||
1297 | 538 | composeBar.addAttachments(sharedAttachmentsTransfer) | ||
1298 | 539 | } | ||
1299 | 540 | |||
1300 | 541 | onActiveChanged: { | ||
1301 | 542 | if (active && (eventModel.count > 0)){ | ||
1302 | 543 | swipeItemDemo.enable() | ||
1303 | 544 | } | ||
1304 | 545 | } | ||
1305 | 546 | |||
1306 | 547 | function updateFilters(accounts, participants, reload, threads) { | 371 | function updateFilters(accounts, participants, reload, threads) { |
1307 | 548 | if (participants.length == 0 || accounts.length == 0) { | 372 | if (participants.length == 0 || accounts.length == 0) { |
1308 | 549 | return null | 373 | return null |
1309 | @@ -603,8 +427,356 @@ | |||
1310 | 603 | return eventModel.markEventAsRead(accountId, threadId, eventId, type); | 427 | return eventModel.markEventAsRead(accountId, threadId, eventId, type); |
1311 | 604 | } | 428 | } |
1312 | 605 | 429 | ||
1313 | 430 | header: PageHeader { | ||
1314 | 431 | id: pageHeader | ||
1315 | 432 | |||
1316 | 433 | property alias leadingActions: leadingBar.actions | ||
1317 | 434 | property alias trailingActions: trailingBar.actions | ||
1318 | 435 | |||
1319 | 436 | property list<QtObject> bottomEdgeLeadingActions: [ | ||
1320 | 437 | Action { | ||
1321 | 438 | id: backAction | ||
1322 | 439 | |||
1323 | 440 | objectName: "cancel" | ||
1324 | 441 | name: "cancel" | ||
1325 | 442 | text: i18n.tr("Cancel") | ||
1326 | 443 | iconName: "down" | ||
1327 | 444 | shortcut: "Esc" | ||
1328 | 445 | onTriggered: { | ||
1329 | 446 | messages.cancel() | ||
1330 | 447 | } | ||
1331 | 448 | } | ||
1332 | 449 | ] | ||
1333 | 450 | |||
1334 | 451 | property list<QtObject> singlePanelLeadingActions: [ | ||
1335 | 452 | Action { | ||
1336 | 453 | id: singlePanelBackAction | ||
1337 | 454 | objectName: "back" | ||
1338 | 455 | name: "cancel" | ||
1339 | 456 | text: i18n.tr("Cancel") | ||
1340 | 457 | iconName: "back" | ||
1341 | 458 | shortcut: "Esc" | ||
1342 | 459 | onTriggered: { | ||
1343 | 460 | // emptyStack will make sure the page gets removed. | ||
1344 | 461 | mainView.emptyStack() | ||
1345 | 462 | } | ||
1346 | 463 | } | ||
1347 | 464 | ] | ||
1348 | 465 | |||
1349 | 466 | title: { | ||
1350 | 467 | if (landscape) { | ||
1351 | 468 | return "" | ||
1352 | 469 | } | ||
1353 | 470 | |||
1354 | 471 | if (participants.length == 1) { | ||
1355 | 472 | return firstRecipientAlias | ||
1356 | 473 | } | ||
1357 | 474 | |||
1358 | 475 | return i18n.tr("New Message") | ||
1359 | 476 | } | ||
1360 | 477 | flickable: null | ||
1361 | 478 | |||
1362 | 479 | Sections { | ||
1363 | 480 | id: sections | ||
1364 | 481 | anchors { | ||
1365 | 482 | left: parent.left | ||
1366 | 483 | leftMargin: units.gu(2) | ||
1367 | 484 | bottom: parent.bottom | ||
1368 | 485 | } | ||
1369 | 486 | model: getSectionsModel() | ||
1370 | 487 | selectedIndex: getSelectedIndex() | ||
1371 | 488 | onSelectedIndexChanged: { | ||
1372 | 489 | if (selectedIndex >= 0) { | ||
1373 | 490 | messages.account = messages.accountsModel[selectedIndex] | ||
1374 | 491 | } | ||
1375 | 492 | } | ||
1376 | 493 | } | ||
1377 | 494 | |||
1378 | 495 | extension: sections.model.length > 1 ? sections : null | ||
1379 | 496 | |||
1380 | 497 | leadingActionBar { | ||
1381 | 498 | id: leadingBar | ||
1382 | 499 | |||
1383 | 500 | states: [ | ||
1384 | 501 | State { | ||
1385 | 502 | name: "bottomEdgeBack" | ||
1386 | 503 | when: startedFromBottomEdge | ||
1387 | 504 | PropertyChanges { | ||
1388 | 505 | target: leadingBar | ||
1389 | 506 | actions: pageHeader.bottomEdgeLeadingActions | ||
1390 | 507 | } | ||
1391 | 508 | }, | ||
1392 | 509 | State { | ||
1393 | 510 | name: "singlePanelBack" | ||
1394 | 511 | when: !mainView.dualPanel && !startedFromBottomEdge | ||
1395 | 512 | PropertyChanges { | ||
1396 | 513 | target: leadingBar | ||
1397 | 514 | actions: pageHeader.singlePanelLeadingActions | ||
1398 | 515 | } | ||
1399 | 516 | } | ||
1400 | 517 | |||
1401 | 518 | ] | ||
1402 | 519 | } | ||
1403 | 520 | |||
1404 | 521 | trailingActionBar { | ||
1405 | 522 | id: trailingBar | ||
1406 | 523 | } | ||
1407 | 524 | } | ||
1408 | 525 | |||
1409 | 526 | states: [ | ||
1410 | 527 | State { | ||
1411 | 528 | id: selectionState | ||
1412 | 529 | name: "selection" | ||
1413 | 530 | when: selectionMode | ||
1414 | 531 | |||
1415 | 532 | property list<QtObject> leadingActions: [ | ||
1416 | 533 | Action { | ||
1417 | 534 | objectName: "selectionModeCancelAction" | ||
1418 | 535 | iconName: "back" | ||
1419 | 536 | onTriggered: messageList.cancelSelection() | ||
1420 | 537 | } | ||
1421 | 538 | ] | ||
1422 | 539 | |||
1423 | 540 | property list<QtObject> trailingActions: [ | ||
1424 | 541 | Action { | ||
1425 | 542 | objectName: "selectionModeSelectAllAction" | ||
1426 | 543 | iconName: "select" | ||
1427 | 544 | onTriggered: { | ||
1428 | 545 | if (messageList.selectedItems.count === messageList.count) { | ||
1429 | 546 | messageList.clearSelection() | ||
1430 | 547 | } else { | ||
1431 | 548 | messageList.selectAll() | ||
1432 | 549 | } | ||
1433 | 550 | } | ||
1434 | 551 | }, | ||
1435 | 552 | Action { | ||
1436 | 553 | objectName: "selectionModeDeleteAction" | ||
1437 | 554 | enabled: messageList.selectedItems.count > 0 | ||
1438 | 555 | iconName: "delete" | ||
1439 | 556 | onTriggered: messageList.endSelection() | ||
1440 | 557 | } | ||
1441 | 558 | ] | ||
1442 | 559 | |||
1443 | 560 | PropertyChanges { | ||
1444 | 561 | target: pageHeader | ||
1445 | 562 | title: " " | ||
1446 | 563 | leadingActions: selectionState.leadingActions | ||
1447 | 564 | trailingActions: selectionState.trailingActions | ||
1448 | 565 | } | ||
1449 | 566 | }, | ||
1450 | 567 | State { | ||
1451 | 568 | id: groupChatState | ||
1452 | 569 | name: "groupChat" | ||
1453 | 570 | when: groupChat | ||
1454 | 571 | |||
1455 | 572 | property list<QtObject> trailingActions: [ | ||
1456 | 573 | Action { | ||
1457 | 574 | objectName: "groupChatAction" | ||
1458 | 575 | iconName: "contact-group" | ||
1459 | 576 | onTriggered: PopupUtils.open(participantsPopover, pageHeader) | ||
1460 | 577 | } | ||
1461 | 578 | ] | ||
1462 | 579 | |||
1463 | 580 | PropertyChanges { | ||
1464 | 581 | target: pageHeader | ||
1465 | 582 | // TRANSLATORS: %1 refers to the number of participants in a group chat | ||
1466 | 583 | title: i18n.tr("Group (%1)").arg(participants.length) | ||
1467 | 584 | contents: headerContents | ||
1468 | 585 | trailingActions: groupChatState.trailingActions | ||
1469 | 586 | } | ||
1470 | 587 | }, | ||
1471 | 588 | State { | ||
1472 | 589 | id: unknownContactState | ||
1473 | 590 | name: "unknownContact" | ||
1474 | 591 | when: participants.length == 1 && contactWatcher.isUnknown | ||
1475 | 592 | |||
1476 | 593 | property list<QtObject> trailingActions: [ | ||
1477 | 594 | Action { | ||
1478 | 595 | objectName: "contactCallAction" | ||
1479 | 596 | visible: participants.length == 1 && contactWatcher.interactive | ||
1480 | 597 | iconName: "call-start" | ||
1481 | 598 | text: i18n.tr("Call") | ||
1482 | 599 | onTriggered: { | ||
1483 | 600 | Qt.inputMethod.hide() | ||
1484 | 601 | // FIXME: support other things than just phone numbers | ||
1485 | 602 | Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) | ||
1486 | 603 | } | ||
1487 | 604 | }, | ||
1488 | 605 | Action { | ||
1489 | 606 | objectName: "addContactAction" | ||
1490 | 607 | visible: contactWatcher.isUnknown && participants.length == 1 && contactWatcher.interactive | ||
1491 | 608 | iconName: "contact-new" | ||
1492 | 609 | text: i18n.tr("Add") | ||
1493 | 610 | onTriggered: { | ||
1494 | 611 | Qt.inputMethod.hide() | ||
1495 | 612 | // FIXME: support other things than just phone numbers | ||
1496 | 613 | mainView.addPhoneToContact(messages, "", contactWatcher.identifier, null, null) | ||
1497 | 614 | } | ||
1498 | 615 | } | ||
1499 | 616 | ] | ||
1500 | 617 | PropertyChanges { | ||
1501 | 618 | target: pageHeader | ||
1502 | 619 | contents: headerContents | ||
1503 | 620 | trailingActions: unknownContactState.trailingActions | ||
1504 | 621 | } | ||
1505 | 622 | }, | ||
1506 | 623 | State { | ||
1507 | 624 | id: newMessageState | ||
1508 | 625 | name: "newMessage" | ||
1509 | 626 | when: participants.length === 0 | ||
1510 | 627 | |||
1511 | 628 | property list<QtObject> trailingActions: [ | ||
1512 | 629 | Action { | ||
1513 | 630 | objectName: "contactList" | ||
1514 | 631 | iconName: "contact" | ||
1515 | 632 | onTriggered: { | ||
1516 | 633 | Qt.inputMethod.hide() | ||
1517 | 634 | mainStack.addFileToCurrentColumnSync(messages.basePage, Qt.resolvedUrl("NewRecipientPage.qml"), {"multiRecipient": multiRecipient}) | ||
1518 | 635 | } | ||
1519 | 636 | } | ||
1520 | 637 | |||
1521 | 638 | ] | ||
1522 | 639 | |||
1523 | 640 | property Item contents: MultiRecipientInput { | ||
1524 | 641 | id: multiRecipient | ||
1525 | 642 | objectName: "multiRecipient" | ||
1526 | 643 | enabled: visible | ||
1527 | 644 | anchors { | ||
1528 | 645 | left: parent ? parent.left : undefined | ||
1529 | 646 | right: parent ? parent.right : undefined | ||
1530 | 647 | rightMargin: units.gu(2) | ||
1531 | 648 | top: parent ? parent.top: undefined | ||
1532 | 649 | topMargin: units.gu(1) | ||
1533 | 650 | } | ||
1534 | 651 | focus: true | ||
1535 | 652 | |||
1536 | 653 | Connections { | ||
1537 | 654 | target: mainView.bottomEdge | ||
1538 | 655 | onStatusChanged: { | ||
1539 | 656 | if (mainView.bottomEdge.status === BottomEdge.Committed) { | ||
1540 | 657 | multiRecipient.forceFocus() | ||
1541 | 658 | } | ||
1542 | 659 | } | ||
1543 | 660 | } | ||
1544 | 661 | } | ||
1545 | 662 | |||
1546 | 663 | PropertyChanges { | ||
1547 | 664 | target: pageHeader | ||
1548 | 665 | title: " " | ||
1549 | 666 | trailingActions: newMessageState.trailingActions | ||
1550 | 667 | contents: newMessageState.contents | ||
1551 | 668 | } | ||
1552 | 669 | }, | ||
1553 | 670 | State { | ||
1554 | 671 | id: knownContactState | ||
1555 | 672 | name: "knownContact" | ||
1556 | 673 | when: participants.length == 1 && !contactWatcher.isUnknown | ||
1557 | 674 | property list<QtObject> trailingActions: [ | ||
1558 | 675 | Action { | ||
1559 | 676 | objectName: "contactCallKnownAction" | ||
1560 | 677 | visible: participants.length == 1 && messages.phoneAccount | ||
1561 | 678 | iconName: "call-start" | ||
1562 | 679 | text: i18n.tr("Call") | ||
1563 | 680 | onTriggered: { | ||
1564 | 681 | Qt.inputMethod.hide() | ||
1565 | 682 | // FIXME: support other things than just phone numbers | ||
1566 | 683 | Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) | ||
1567 | 684 | } | ||
1568 | 685 | }, | ||
1569 | 686 | Action { | ||
1570 | 687 | objectName: "contactProfileAction" | ||
1571 | 688 | visible: !contactWatcher.isUnknown && participants.length == 1 && messages.phoneAccount | ||
1572 | 689 | iconSource: "image://theme/contact" | ||
1573 | 690 | text: i18n.tr("Contact") | ||
1574 | 691 | onTriggered: { | ||
1575 | 692 | mainView.showContactDetails(messages.basePage, contactWatcher.contactId, null, null) | ||
1576 | 693 | } | ||
1577 | 694 | } | ||
1578 | 695 | ] | ||
1579 | 696 | PropertyChanges { | ||
1580 | 697 | target: pageHeader | ||
1581 | 698 | contents: headerContents | ||
1582 | 699 | trailingActions: knownContactState.trailingActions | ||
1583 | 700 | } | ||
1584 | 701 | } | ||
1585 | 702 | ] | ||
1586 | 703 | |||
1587 | 704 | Component.onCompleted: { | ||
1588 | 705 | if (messages.accountId !== "") { | ||
1589 | 706 | var account = telepathyHelper.accountForId(messages.accountId) | ||
1590 | 707 | if (account && account.type == AccountEntry.MultimediaAccount) { | ||
1591 | 708 | // fallback the first available phone account | ||
1592 | 709 | if (telepathyHelper.phoneAccounts.length > 0) { | ||
1593 | 710 | messages.accountId = telepathyHelper.phoneAccounts[0].accountId | ||
1594 | 711 | } | ||
1595 | 712 | } | ||
1596 | 713 | } | ||
1597 | 714 | composeBar.addAttachments(sharedAttachmentsTransfer) | ||
1598 | 715 | } | ||
1599 | 716 | |||
1600 | 717 | Component.onDestruction: { | ||
1601 | 718 | if (!mainView.dualPanel && !startedFromBottomEdge) { | ||
1602 | 719 | mainPage.displayedThreadIndex = -1 | ||
1603 | 720 | } | ||
1604 | 721 | } | ||
1605 | 722 | |||
1606 | 723 | onReady: { | ||
1607 | 724 | isReady = true | ||
1608 | 725 | if (participants.length === 0 && keyboardFocus) | ||
1609 | 726 | multiRecipient.forceFocus() | ||
1610 | 727 | } | ||
1611 | 728 | |||
1612 | 729 | onActiveChanged: { | ||
1613 | 730 | if (active && (eventModel.count > 0)){ | ||
1614 | 731 | swipeItemDemo.enable() | ||
1615 | 732 | } | ||
1616 | 733 | } | ||
1617 | 734 | |||
1618 | 735 | Connections { | ||
1619 | 736 | target: telepathyHelper | ||
1620 | 737 | onSetupReady: { | ||
1621 | 738 | // force reevaluation | ||
1622 | 739 | messages.account = Qt.binding(getCurrentAccount) | ||
1623 | 740 | messages.phoneAccount = Qt.binding(isPhoneAccount) | ||
1624 | 741 | head.sections.model = Qt.binding(getSectionsModel) | ||
1625 | 742 | head.sections.selectedIndex = Qt.binding(getSelectedIndex) | ||
1626 | 743 | } | ||
1627 | 744 | } | ||
1628 | 745 | |||
1629 | 746 | Connections { | ||
1630 | 747 | target: chatManager | ||
1631 | 748 | onChatEntryCreated: { | ||
1632 | 749 | // TODO: track using chatId and not participants | ||
1633 | 750 | if (accountId == account.accountId && | ||
1634 | 751 | firstParticipant && participants[0] == firstParticipant.identifier) { | ||
1635 | 752 | messages.chatEntry = chatEntry | ||
1636 | 753 | } | ||
1637 | 754 | } | ||
1638 | 755 | onChatsChanged: { | ||
1639 | 756 | for (var i in chatManager.chats) { | ||
1640 | 757 | var chat = chatManager.chats[i] | ||
1641 | 758 | // TODO: track using chatId and not participants | ||
1642 | 759 | if (chat.account.accountId == account.accountId && | ||
1643 | 760 | firstParticipant && chat.participants[0] == firstParticipant.identifier) { | ||
1644 | 761 | messages.chatEntry = chat | ||
1645 | 762 | return | ||
1646 | 763 | } | ||
1647 | 764 | } | ||
1648 | 765 | messages.chatEntry = null | ||
1649 | 766 | } | ||
1650 | 767 | } | ||
1651 | 768 | |||
1652 | 769 | // this is necessary to automatically update the view when the | ||
1653 | 770 | // default account changes in system settings | ||
1654 | 606 | Connections { | 771 | Connections { |
1655 | 607 | target: mainView | 772 | target: mainView |
1656 | 773 | onAccountChanged: { | ||
1657 | 774 | if (!messages.phoneAccount) { | ||
1658 | 775 | return | ||
1659 | 776 | } | ||
1660 | 777 | messages.account = mainView.account | ||
1661 | 778 | } | ||
1662 | 779 | |||
1663 | 608 | onApplicationActiveChanged: { | 780 | onApplicationActiveChanged: { |
1664 | 609 | if (mainView.applicationActive) { | 781 | if (mainView.applicationActive) { |
1665 | 610 | for (var i in pendingEventsToMarkAsRead) { | 782 | for (var i in pendingEventsToMarkAsRead) { |
1666 | @@ -616,6 +788,91 @@ | |||
1667 | 616 | } | 788 | } |
1668 | 617 | } | 789 | } |
1669 | 618 | 790 | ||
1670 | 791 | Timer { | ||
1671 | 792 | id: typingTimer | ||
1672 | 793 | interval: 6000 | ||
1673 | 794 | onTriggered: { | ||
1674 | 795 | messages.userTyping = false; | ||
1675 | 796 | } | ||
1676 | 797 | } | ||
1677 | 798 | |||
1678 | 799 | Repeater { | ||
1679 | 800 | model: messages.chatEntry ? messages.chatEntry.chatStates : null | ||
1680 | 801 | Item { | ||
1681 | 802 | function processChatState() { | ||
1682 | 803 | if (modelData.state == ChatEntry.ChannelChatStateComposing) { | ||
1683 | 804 | messages.userTyping = true | ||
1684 | 805 | typingTimer.start() | ||
1685 | 806 | } else { | ||
1686 | 807 | messages.userTyping = false | ||
1687 | 808 | } | ||
1688 | 809 | } | ||
1689 | 810 | Component.onCompleted: processChatState() | ||
1690 | 811 | Connections { | ||
1691 | 812 | target: modelData | ||
1692 | 813 | onStateChanged: processChatState() | ||
1693 | 814 | } | ||
1694 | 815 | } | ||
1695 | 816 | } | ||
1696 | 817 | |||
1697 | 818 | MessagesHeader { | ||
1698 | 819 | id: headerContents | ||
1699 | 820 | width: parent ? parent.width - units.gu(2) : undefined | ||
1700 | 821 | height: units.gu(5) | ||
1701 | 822 | title: pageHeader.title | ||
1702 | 823 | subtitle: { | ||
1703 | 824 | if (userTyping) { | ||
1704 | 825 | return i18n.tr("Typing..") | ||
1705 | 826 | } | ||
1706 | 827 | switch (presenceRequest.type) { | ||
1707 | 828 | case PresenceRequest.PresenceTypeAvailable: | ||
1708 | 829 | return i18n.tr("Online") | ||
1709 | 830 | case PresenceRequest.PresenceTypeOffline: | ||
1710 | 831 | return i18n.tr("Offline") | ||
1711 | 832 | case PresenceRequest.PresenceTypeAway: | ||
1712 | 833 | return i18n.tr("Away") | ||
1713 | 834 | case PresenceRequest.PresenceTypeBusy: | ||
1714 | 835 | return i18n.tr("Busy") | ||
1715 | 836 | default: | ||
1716 | 837 | return "" | ||
1717 | 838 | } | ||
1718 | 839 | } | ||
1719 | 840 | visible: true | ||
1720 | 841 | } | ||
1721 | 842 | |||
1722 | 843 | PresenceRequest { | ||
1723 | 844 | id: presenceItem | ||
1724 | 845 | accountId: { | ||
1725 | 846 | // if this is a regular sms chat, try requesting the presence on | ||
1726 | 847 | // a multimedia account | ||
1727 | 848 | if (!account) { | ||
1728 | 849 | return "" | ||
1729 | 850 | } | ||
1730 | 851 | if (account.type == AccountEntry.PhoneAccount) { | ||
1731 | 852 | for (var i in telepathyHelper.accounts) { | ||
1732 | 853 | var tmpAccount = telepathyHelper.accounts[i] | ||
1733 | 854 | if (tmpAccount.type == AccountEntry.MultimediaAccount) { | ||
1734 | 855 | return tmpAccount.accountId | ||
1735 | 856 | } | ||
1736 | 857 | } | ||
1737 | 858 | return "" | ||
1738 | 859 | } | ||
1739 | 860 | return account.accountId | ||
1740 | 861 | } | ||
1741 | 862 | // we just request presence on 1-1 chats | ||
1742 | 863 | identifier: participants.length == 1 ? participants[0].identifier : "" | ||
1743 | 864 | } | ||
1744 | 865 | |||
1745 | 866 | ActivityIndicator { | ||
1746 | 867 | id: activityIndicator | ||
1747 | 868 | anchors { | ||
1748 | 869 | verticalCenter: parent.verticalCenter | ||
1749 | 870 | horizontalCenter: parent.horizontalCenter | ||
1750 | 871 | } | ||
1751 | 872 | running: isSearching | ||
1752 | 873 | visible: running | ||
1753 | 874 | } | ||
1754 | 875 | |||
1755 | 619 | Component { | 876 | Component { |
1756 | 620 | id: participantsPopover | 877 | id: participantsPopover |
1757 | 621 | 878 | ||
1758 | @@ -678,13 +935,6 @@ | |||
1759 | 678 | } | 935 | } |
1760 | 679 | } | 936 | } |
1761 | 680 | 937 | ||
1762 | 681 | Connections { | ||
1763 | 682 | target: messages.head.sections | ||
1764 | 683 | onSelectedIndexChanged: { | ||
1765 | 684 | messages.account = messages.accountsModel[head.sections.selectedIndex] | ||
1766 | 685 | } | ||
1767 | 686 | } | ||
1768 | 687 | |||
1769 | 688 | Loader { | 938 | Loader { |
1770 | 689 | id: searchListLoader | 939 | id: searchListLoader |
1771 | 690 | 940 | ||
1772 | @@ -696,7 +946,7 @@ | |||
1773 | 696 | visible: source != "" | 946 | visible: source != "" |
1774 | 697 | anchors { | 947 | anchors { |
1775 | 698 | top: parent.top | 948 | top: parent.top |
1777 | 699 | topMargin: units.gu(2) | 949 | topMargin: header.height + units.gu(2) |
1778 | 700 | left: parent.left | 950 | left: parent.left |
1779 | 701 | right: parent.right | 951 | right: parent.right |
1780 | 702 | bottom: composeBar.top | 952 | bottom: composeBar.top |
1781 | @@ -756,156 +1006,6 @@ | |||
1782 | 756 | addressableFields: messages.account ? messages.account.addressableVCardFields : ["tel"] // just to have a fallback there | 1006 | addressableFields: messages.account ? messages.account.addressableVCardFields : ["tel"] // just to have a fallback there |
1783 | 757 | } | 1007 | } |
1784 | 758 | 1008 | ||
1785 | 759 | Action { | ||
1786 | 760 | id: backButton | ||
1787 | 761 | objectName: "backButton" | ||
1788 | 762 | iconName: "back" | ||
1789 | 763 | onTriggered: { | ||
1790 | 764 | if (typeof mainPage !== 'undefined') { | ||
1791 | 765 | mainPage.temporaryProperties = null | ||
1792 | 766 | } | ||
1793 | 767 | mainStack.pop() | ||
1794 | 768 | } | ||
1795 | 769 | } | ||
1796 | 770 | |||
1797 | 771 | states: [ | ||
1798 | 772 | PageHeadState { | ||
1799 | 773 | name: "selection" | ||
1800 | 774 | head: messages.head | ||
1801 | 775 | when: selectionMode | ||
1802 | 776 | |||
1803 | 777 | backAction: Action { | ||
1804 | 778 | objectName: "selectionModeCancelAction" | ||
1805 | 779 | iconName: "back" | ||
1806 | 780 | onTriggered: messageList.cancelSelection() | ||
1807 | 781 | } | ||
1808 | 782 | |||
1809 | 783 | actions: [ | ||
1810 | 784 | Action { | ||
1811 | 785 | objectName: "selectionModeSelectAllAction" | ||
1812 | 786 | iconName: "select" | ||
1813 | 787 | onTriggered: { | ||
1814 | 788 | if (messageList.selectedItems.count === messageList.count) { | ||
1815 | 789 | messageList.clearSelection() | ||
1816 | 790 | } else { | ||
1817 | 791 | messageList.selectAll() | ||
1818 | 792 | } | ||
1819 | 793 | } | ||
1820 | 794 | }, | ||
1821 | 795 | Action { | ||
1822 | 796 | objectName: "selectionModeDeleteAction" | ||
1823 | 797 | enabled: messageList.selectedItems.count > 0 | ||
1824 | 798 | iconName: "delete" | ||
1825 | 799 | onTriggered: messageList.endSelection() | ||
1826 | 800 | } | ||
1827 | 801 | ] | ||
1828 | 802 | }, | ||
1829 | 803 | PageHeadState { | ||
1830 | 804 | name: "groupChat" | ||
1831 | 805 | head: messages.head | ||
1832 | 806 | when: groupChat | ||
1833 | 807 | contents: header | ||
1834 | 808 | backAction: backButton | ||
1835 | 809 | |||
1836 | 810 | actions: [ | ||
1837 | 811 | Action { | ||
1838 | 812 | objectName: "groupChatAction" | ||
1839 | 813 | iconName: "contact-group" | ||
1840 | 814 | onTriggered: PopupUtils.open(participantsPopover, screenTop) | ||
1841 | 815 | } | ||
1842 | 816 | ] | ||
1843 | 817 | }, | ||
1844 | 818 | PageHeadState { | ||
1845 | 819 | name: "unknownContact" | ||
1846 | 820 | head: messages.head | ||
1847 | 821 | when: participants.length == 1 && contactWatcher.isUnknown | ||
1848 | 822 | backAction: backButton | ||
1849 | 823 | contents: header | ||
1850 | 824 | |||
1851 | 825 | actions: [ | ||
1852 | 826 | Action { | ||
1853 | 827 | objectName: "contactCallAction" | ||
1854 | 828 | visible: participants.length == 1 && contactWatcher.interactive | ||
1855 | 829 | iconName: "call-start" | ||
1856 | 830 | text: i18n.tr("Call") | ||
1857 | 831 | onTriggered: { | ||
1858 | 832 | Qt.inputMethod.hide() | ||
1859 | 833 | // FIXME: support other things than just phone numbers | ||
1860 | 834 | Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) | ||
1861 | 835 | } | ||
1862 | 836 | }, | ||
1863 | 837 | Action { | ||
1864 | 838 | objectName: "addContactAction" | ||
1865 | 839 | visible: contactWatcher.isUnknown && participants.length == 1 && contactWatcher.interactive | ||
1866 | 840 | iconName: "contact-new" | ||
1867 | 841 | text: i18n.tr("Add") | ||
1868 | 842 | onTriggered: { | ||
1869 | 843 | Qt.inputMethod.hide() | ||
1870 | 844 | // FIXME: support other things than just phone numbers | ||
1871 | 845 | mainView.addPhoneToContact("", contactWatcher.identifier, null, null) | ||
1872 | 846 | } | ||
1873 | 847 | } | ||
1874 | 848 | ] | ||
1875 | 849 | }, | ||
1876 | 850 | PageHeadState { | ||
1877 | 851 | name: "newMessage" | ||
1878 | 852 | head: messages.head | ||
1879 | 853 | when: participants.length === 0 && isReady | ||
1880 | 854 | backAction: backButton | ||
1881 | 855 | actions: [ | ||
1882 | 856 | Action { | ||
1883 | 857 | objectName: "contactList" | ||
1884 | 858 | iconName: "contact" | ||
1885 | 859 | onTriggered: { | ||
1886 | 860 | Qt.inputMethod.hide() | ||
1887 | 861 | mainStack.push(Qt.resolvedUrl("NewRecipientPage.qml"), {"multiRecipient": multiRecipient, "parentPage": messages}) | ||
1888 | 862 | } | ||
1889 | 863 | } | ||
1890 | 864 | |||
1891 | 865 | ] | ||
1892 | 866 | |||
1893 | 867 | contents: MultiRecipientInput { | ||
1894 | 868 | id: multiRecipient | ||
1895 | 869 | objectName: "multiRecipient" | ||
1896 | 870 | enabled: visible | ||
1897 | 871 | anchors { | ||
1898 | 872 | left: parent ? parent.left : undefined | ||
1899 | 873 | right: parent ? parent.right : undefined | ||
1900 | 874 | rightMargin: units.gu(2) | ||
1901 | 875 | } | ||
1902 | 876 | } | ||
1903 | 877 | }, | ||
1904 | 878 | PageHeadState { | ||
1905 | 879 | name: "knownContact" | ||
1906 | 880 | head: messages.head | ||
1907 | 881 | when: participants.length == 1 && !contactWatcher.isUnknown | ||
1908 | 882 | backAction: backButton | ||
1909 | 883 | contents: header | ||
1910 | 884 | actions: [ | ||
1911 | 885 | Action { | ||
1912 | 886 | objectName: "contactCallKnownAction" | ||
1913 | 887 | visible: participants.length == 1 && messages.phoneAccount | ||
1914 | 888 | iconName: "call-start" | ||
1915 | 889 | text: i18n.tr("Call") | ||
1916 | 890 | onTriggered: { | ||
1917 | 891 | Qt.inputMethod.hide() | ||
1918 | 892 | // FIXME: support other things than just phone numbers | ||
1919 | 893 | Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) | ||
1920 | 894 | } | ||
1921 | 895 | }, | ||
1922 | 896 | Action { | ||
1923 | 897 | objectName: "contactProfileAction" | ||
1924 | 898 | visible: !contactWatcher.isUnknown && participants.length == 1 && messages.phoneAccount | ||
1925 | 899 | iconSource: "image://theme/contact" | ||
1926 | 900 | text: i18n.tr("Contact") | ||
1927 | 901 | onTriggered: { | ||
1928 | 902 | mainView.showContactDetails(contactWatcher.contactId, null, null) | ||
1929 | 903 | } | ||
1930 | 904 | } | ||
1931 | 905 | ] | ||
1932 | 906 | } | ||
1933 | 907 | ] | ||
1934 | 908 | |||
1935 | 909 | HistoryEventModel { | 1009 | HistoryEventModel { |
1936 | 910 | id: eventModel | 1010 | id: eventModel |
1937 | 911 | type: HistoryThreadModel.EventTypeText | 1011 | type: HistoryThreadModel.EventTypeText |
1938 | @@ -958,7 +1058,7 @@ | |||
1939 | 958 | Item { | 1058 | Item { |
1940 | 959 | id: screenTop | 1059 | id: screenTop |
1941 | 960 | anchors { | 1060 | anchors { |
1943 | 961 | top: parent.top | 1061 | top: pageHeader.bottom |
1944 | 962 | left: parent.left | 1062 | left: parent.left |
1945 | 963 | right: parent.right | 1063 | right: parent.right |
1946 | 964 | } | 1064 | } |
1947 | @@ -1115,4 +1215,6 @@ | |||
1948 | 1115 | } | 1215 | } |
1949 | 1116 | } | 1216 | } |
1950 | 1117 | } | 1217 | } |
1951 | 1218 | |||
1952 | 1219 | // FIXME: we need a bottom edge here somehow | ||
1953 | 1118 | } | 1220 | } |
1954 | 1119 | 1221 | ||
1955 | === modified file 'src/qml/MessagesHeader.qml' | |||
1956 | --- src/qml/MessagesHeader.qml 2016-02-01 23:18:57 +0000 | |||
1957 | +++ src/qml/MessagesHeader.qml 2016-02-01 23:18:57 +0000 | |||
1958 | @@ -26,7 +26,12 @@ | |||
1959 | 26 | property string title: "" | 26 | property string title: "" |
1960 | 27 | property string subtitle: "" | 27 | property string subtitle: "" |
1961 | 28 | 28 | ||
1963 | 29 | height: units.gu(7) | 29 | height: units.gu(8) |
1964 | 30 | |||
1965 | 31 | anchors { | ||
1966 | 32 | top: parent.top | ||
1967 | 33 | topMargin: units.gu(1) | ||
1968 | 34 | } | ||
1969 | 30 | 35 | ||
1970 | 31 | Behavior on height { | 36 | Behavior on height { |
1971 | 32 | UbuntuNumberAnimation {} | 37 | UbuntuNumberAnimation {} |
1972 | @@ -50,7 +55,7 @@ | |||
1973 | 50 | } | 55 | } |
1974 | 51 | verticalAlignment: Text.AlignVCenter | 56 | verticalAlignment: Text.AlignVCenter |
1975 | 52 | 57 | ||
1977 | 53 | font.pixelSize: FontUtils.sizeToPixels("x-large") | 58 | font.pixelSize: FontUtils.sizeToPixels("large") |
1978 | 54 | elide: Text.ElideRight | 59 | elide: Text.ElideRight |
1979 | 55 | text: title | 60 | text: title |
1980 | 56 | } | 61 | } |
1981 | 57 | 62 | ||
1982 | === added file 'src/qml/MessagingBottomEdge.qml' | |||
1983 | --- src/qml/MessagingBottomEdge.qml 1970-01-01 00:00:00 +0000 | |||
1984 | +++ src/qml/MessagingBottomEdge.qml 2016-02-01 23:18:57 +0000 | |||
1985 | @@ -0,0 +1,43 @@ | |||
1986 | 1 | import QtQuick 2.0 | ||
1987 | 2 | import Ubuntu.Components 1.3 | ||
1988 | 3 | |||
1989 | 4 | BottomEdge { | ||
1990 | 5 | id: bottomEdge | ||
1991 | 6 | |||
1992 | 7 | function commitWithProperties(properties) { | ||
1993 | 8 | _realPage = messagesComponent.createObject(null, properties) | ||
1994 | 9 | commit() | ||
1995 | 10 | } | ||
1996 | 11 | |||
1997 | 12 | property var _realPage: null | ||
1998 | 13 | |||
1999 | 14 | height: parent ? parent.height : 0 | ||
2000 | 15 | hint.text: i18n.tr("+") | ||
2001 | 16 | contentComponent: Item { | ||
2002 | 17 | id: pageContent | ||
2003 | 18 | implicitWidth: bottomEdge.width | ||
2004 | 19 | implicitHeight: bottomEdge.height | ||
2005 | 20 | children: bottomEdge._realPage | ||
2006 | 21 | } | ||
2007 | 22 | |||
2008 | 23 | Component.onCompleted: { | ||
2009 | 24 | mainView.bottomEdge = bottomEdge | ||
2010 | 25 | _realPage = messagesComponent.createObject(null) | ||
2011 | 26 | } | ||
2012 | 27 | |||
2013 | 28 | onCollapseCompleted: { | ||
2014 | 29 | _realPage = messagesComponent.createObject(null) | ||
2015 | 30 | } | ||
2016 | 31 | |||
2017 | 32 | Component { | ||
2018 | 33 | id: messagesComponent | ||
2019 | 34 | |||
2020 | 35 | Messages { | ||
2021 | 36 | anchors.fill: parent | ||
2022 | 37 | onCancel: bottomEdge.collapse() | ||
2023 | 38 | basePage: bottomEdge.parent | ||
2024 | 39 | startedFromBottomEdge: true | ||
2025 | 40 | } | ||
2026 | 41 | } | ||
2027 | 42 | |||
2028 | 43 | } | ||
2029 | 0 | 44 | ||
2030 | === modified file 'src/qml/MessagingContactEditorPage.qml' | |||
2031 | --- src/qml/MessagingContactEditorPage.qml 2016-01-06 18:29:47 +0000 | |||
2032 | +++ src/qml/MessagingContactEditorPage.qml 2016-02-01 23:18:57 +0000 | |||
2033 | @@ -52,8 +52,12 @@ | |||
2034 | 52 | 52 | ||
2035 | 53 | onContactSaved: { | 53 | onContactSaved: { |
2036 | 54 | if (root.contactListPage) { | 54 | if (root.contactListPage) { |
2039 | 55 | root.contactListPage.moveListToContact(contact) | 55 | if (root.contactListPage.phoneToAdd !== "") { |
2040 | 56 | root.contactListPage.phoneToAdd = "" | 56 | mainStack.removePage(root.contactListPage) |
2041 | 57 | } else { | ||
2042 | 58 | root.contactListPage.moveListToContact(contact) | ||
2043 | 59 | root.contactListPage.phoneToAdd = "" | ||
2044 | 60 | } | ||
2045 | 57 | } | 61 | } |
2046 | 58 | } | 62 | } |
2047 | 59 | } | 63 | } |
2048 | 60 | 64 | ||
2049 | === modified file 'src/qml/MessagingContactViewPage.qml' | |||
2050 | --- src/qml/MessagingContactViewPage.qml 2016-01-06 18:29:47 +0000 | |||
2051 | +++ src/qml/MessagingContactViewPage.qml 2016-02-01 23:18:57 +0000 | |||
2052 | @@ -42,12 +42,13 @@ | |||
2053 | 42 | var newDetail = Qt.createQmlObject(detailSourceTemplate, contact) | 42 | var newDetail = Qt.createQmlObject(detailSourceTemplate, contact) |
2054 | 43 | if (newDetail) { | 43 | if (newDetail) { |
2055 | 44 | contact.addDetail(newDetail) | 44 | contact.addDetail(newDetail) |
2062 | 45 | pageStack.push(root.contactEditorPageURL, | 45 | mainStack.addPageToCurrentColumn(root, |
2063 | 46 | { model: root.model, | 46 | root.contactEditorPageURL, |
2064 | 47 | contact: contact, | 47 | { model: root.model, |
2065 | 48 | initialFocusSection: "phones", | 48 | contact: contact, |
2066 | 49 | newDetails: [newDetail], | 49 | initialFocusSection: "phones", |
2067 | 50 | contactListPage: root.contactListPage }) | 50 | newDetails: [newDetail], |
2068 | 51 | contactListPage: root.contactListPage }) | ||
2069 | 51 | root.addPhoneToContact = "" | 52 | root.addPhoneToContact = "" |
2070 | 52 | } else { | 53 | } else { |
2071 | 53 | console.warn("Fail to create phone number detail") | 54 | console.warn("Fail to create phone number detail") |
2072 | @@ -61,7 +62,7 @@ | |||
2073 | 61 | iconName: "share" | 62 | iconName: "share" |
2074 | 62 | visible: root.editable | 63 | visible: root.editable |
2075 | 63 | onTriggered: { | 64 | onTriggered: { |
2077 | 64 | pageStack.push(contactShareComponent, | 65 | pageStack.addComponentToCurrentColumnSync(root, contactShareComponent, |
2078 | 65 | { contactModel: root.model, | 66 | { contactModel: root.model, |
2079 | 66 | contacts: [root.contact] }) | 67 | contacts: [root.contact] }) |
2080 | 67 | } | 68 | } |
2081 | @@ -72,7 +73,7 @@ | |||
2082 | 72 | iconName: "edit" | 73 | iconName: "edit" |
2083 | 73 | visible: root.editable | 74 | visible: root.editable |
2084 | 74 | onTriggered: { | 75 | onTriggered: { |
2086 | 75 | pageStack.push(contactEditorPageURL, | 76 | pageStack.addFileToCurrentColumnSync(root, contactEditorPageURL, |
2087 | 76 | { model: root.model, | 77 | { model: root.model, |
2088 | 77 | contact: root.contact, | 78 | contact: root.contact, |
2089 | 78 | contactListPage: root.contactListPage }) | 79 | contactListPage: root.contactListPage }) |
2090 | @@ -122,9 +123,9 @@ | |||
2091 | 122 | } else { | 123 | } else { |
2092 | 123 | Qt.openUrlExternally(("%1:%2").arg(action).arg(detail.value(0))) | 124 | Qt.openUrlExternally(("%1:%2").arg(action).arg(detail.value(0))) |
2093 | 124 | } | 125 | } |
2095 | 125 | pageStack.pop() | 126 | pageStack.removePage(root) |
2096 | 126 | } | 127 | } |
2098 | 127 | onContactRemoved: pageStack.pop() | 128 | onContactRemoved: pageStack.removePage(root) |
2099 | 128 | onContactFetched: { | 129 | onContactFetched: { |
2100 | 129 | root.contact = contact | 130 | root.contact = contact |
2101 | 130 | if (root.active && root.addPhoneToContact != "") { | 131 | if (root.active && root.addPhoneToContact != "") { |
2102 | 131 | 132 | ||
2103 | === added file 'src/qml/MessagingPageLayout.qml' | |||
2104 | --- src/qml/MessagingPageLayout.qml 1970-01-01 00:00:00 +0000 | |||
2105 | +++ src/qml/MessagingPageLayout.qml 2016-02-01 23:18:57 +0000 | |||
2106 | @@ -0,0 +1,64 @@ | |||
2107 | 1 | import QtQuick 2.0 | ||
2108 | 2 | import Ubuntu.Components 1.3 | ||
2109 | 3 | |||
2110 | 4 | AdaptivePageLayout { | ||
2111 | 5 | id: layout | ||
2112 | 6 | property var _pagesToRemove: [] | ||
2113 | 7 | |||
2114 | 8 | function deleteInstances() { | ||
2115 | 9 | for (var i in _pagesToRemove) { | ||
2116 | 10 | if (_pagesToRemove[i].destroy) { | ||
2117 | 11 | _pagesToRemove[i].destroy() | ||
2118 | 12 | } | ||
2119 | 13 | } | ||
2120 | 14 | _pagesToRemove = [] | ||
2121 | 15 | } | ||
2122 | 16 | |||
2123 | 17 | function removePage(page) { | ||
2124 | 18 | // check if this page was allocated dynamically and then remove it | ||
2125 | 19 | for (var i in _pagesToRemove) { | ||
2126 | 20 | if (_pagesToRemove[i] == page) { | ||
2127 | 21 | _pagesToRemove[i].destroy() | ||
2128 | 22 | _pagesToRemove.splice(i, 1) | ||
2129 | 23 | break | ||
2130 | 24 | } | ||
2131 | 25 | } | ||
2132 | 26 | removePages(page) | ||
2133 | 27 | } | ||
2134 | 28 | |||
2135 | 29 | function addFileToNextColumnSync(parentObject, resolvedUrl, properties) { | ||
2136 | 30 | if (typeof(properties) === 'undefined') { | ||
2137 | 31 | properties = {} | ||
2138 | 32 | } | ||
2139 | 33 | var page = Qt.createComponent(resolvedUrl).createObject(parentObject, properties) | ||
2140 | 34 | layout.addPageToNextColumn(parentObject, page) | ||
2141 | 35 | _pagesToRemove.push(page) | ||
2142 | 36 | } | ||
2143 | 37 | |||
2144 | 38 | function addFileToCurrentColumnSync(parentObject, resolvedUrl, properties) { | ||
2145 | 39 | if (typeof(properties) === 'undefined') { | ||
2146 | 40 | properties = {} | ||
2147 | 41 | } | ||
2148 | 42 | var page = Qt.createComponent(resolvedUrl).createObject(parentObject, properties) | ||
2149 | 43 | layout.addPageToCurrentColumn(parentObject, page) | ||
2150 | 44 | _pagesToRemove.push(page) | ||
2151 | 45 | } | ||
2152 | 46 | |||
2153 | 47 | function addComponentToNextColumnSync(parentObject, component, properties) { | ||
2154 | 48 | if (typeof(properties) === 'undefined') { | ||
2155 | 49 | properties = {} | ||
2156 | 50 | } | ||
2157 | 51 | var page = component.createObject(parentObject, properties) | ||
2158 | 52 | layout.addPageToNextColumn(parentObject, page) | ||
2159 | 53 | _pagesToRemove.push(page) | ||
2160 | 54 | } | ||
2161 | 55 | |||
2162 | 56 | function addComponentToCurrentColumnSync(parentObject, component, properties) { | ||
2163 | 57 | if (typeof(properties) === 'undefined') { | ||
2164 | 58 | properties = {} | ||
2165 | 59 | } | ||
2166 | 60 | var page = component.createObject(parentObject, properties) | ||
2167 | 61 | layout.addPageToCurrentColumn(parentObject, page) | ||
2168 | 62 | _pagesToRemove.push(page) | ||
2169 | 63 | } | ||
2170 | 64 | } | ||
2171 | 0 | 65 | ||
2172 | === modified file 'src/qml/MultiRecipientInput.qml' | |||
2173 | --- src/qml/MultiRecipientInput.qml 2015-09-14 13:51:27 +0000 | |||
2174 | +++ src/qml/MultiRecipientInput.qml 2016-02-01 23:18:57 +0000 | |||
2175 | @@ -29,7 +29,7 @@ | |||
2176 | 29 | property variant recipients: [] | 29 | property variant recipients: [] |
2177 | 30 | property string searchString: "" | 30 | property string searchString: "" |
2178 | 31 | signal clearSearch() | 31 | signal clearSearch() |
2180 | 32 | style: Theme.createStyleComponent("TextFieldStyle.qml", multiRecipientWidget) | 32 | styleName: "TextFieldStyle" |
2181 | 33 | clip: true | 33 | clip: true |
2182 | 34 | height: contactFlow.height | 34 | height: contactFlow.height |
2183 | 35 | focus: activeFocus | 35 | focus: activeFocus |
2184 | 36 | 36 | ||
2185 | === modified file 'src/qml/NewRecipientPage.qml' | |||
2186 | --- src/qml/NewRecipientPage.qml 2015-11-16 22:16:06 +0000 | |||
2187 | +++ src/qml/NewRecipientPage.qml 2016-02-01 23:18:57 +0000 | |||
2188 | @@ -26,7 +26,6 @@ | |||
2189 | 26 | objectName: "newRecipientPage" | 26 | objectName: "newRecipientPage" |
2190 | 27 | 27 | ||
2191 | 28 | property Item multiRecipient: null | 28 | property Item multiRecipient: null |
2192 | 29 | property Item parentPage: null | ||
2193 | 30 | property string phoneToAdd: "" | 29 | property string phoneToAdd: "" |
2194 | 31 | property QtObject contactIndex: null | 30 | property QtObject contactIndex: null |
2195 | 32 | 31 | ||
2196 | @@ -44,11 +43,39 @@ | |||
2197 | 44 | { | 43 | { |
2198 | 45 | multiRecipient.addRecipient(phoneNumber) | 44 | multiRecipient.addRecipient(phoneNumber) |
2199 | 46 | multiRecipient.forceActiveFocus() | 45 | multiRecipient.forceActiveFocus() |
2205 | 47 | mainStack.pop() | 46 | mainStack.removePage(newRecipientPage) |
2206 | 48 | } | 47 | } |
2207 | 49 | 48 | ||
2208 | 50 | title: i18n.tr("Add recipient") | 49 | header: PageHeader { |
2209 | 51 | 50 | id: pageHeader | |
2210 | 51 | |||
2211 | 52 | property alias leadingActions: leadingBar.actions | ||
2212 | 53 | property alias trailingActions: trailingBar.actions | ||
2213 | 54 | |||
2214 | 55 | title: i18n.tr("Add recipient") | ||
2215 | 56 | leadingActionBar { | ||
2216 | 57 | id: leadingBar | ||
2217 | 58 | } | ||
2218 | 59 | |||
2219 | 60 | trailingActionBar { | ||
2220 | 61 | id: trailingBar | ||
2221 | 62 | actions: [ | ||
2222 | 63 | Action { | ||
2223 | 64 | text: i18n.tr("Back") | ||
2224 | 65 | iconName: "back" | ||
2225 | 66 | onTriggered: { | ||
2226 | 67 | mainStack.removePage(newRecipientPage) | ||
2227 | 68 | newRecipientPage.destroy() | ||
2228 | 69 | } | ||
2229 | 70 | } | ||
2230 | 71 | ] | ||
2231 | 72 | } | ||
2232 | 73 | } | ||
2233 | 74 | |||
2234 | 75 | Sections { | ||
2235 | 76 | id: headerSections | ||
2236 | 77 | model: [i18n.tr("All"), i18n.tr("Favorites")] | ||
2237 | 78 | } | ||
2238 | 52 | TextField { | 79 | TextField { |
2239 | 53 | id: searchField | 80 | id: searchField |
2240 | 54 | 81 | ||
2241 | @@ -69,11 +96,10 @@ | |||
2242 | 69 | 96 | ||
2243 | 70 | state: "default" | 97 | state: "default" |
2244 | 71 | states: [ | 98 | states: [ |
2246 | 72 | PageHeadState { | 99 | State { |
2247 | 73 | id: defaultState | 100 | id: defaultState |
2248 | 74 | |||
2249 | 75 | name: "default" | 101 | name: "default" |
2251 | 76 | actions: [ | 102 | property list<QtObject> trailingActions: [ |
2252 | 77 | Action { | 103 | Action { |
2253 | 78 | text: i18n.tr("Search") | 104 | text: i18n.tr("Search") |
2254 | 79 | iconName: "search" | 105 | iconName: "search" |
2255 | @@ -84,10 +110,11 @@ | |||
2256 | 84 | } | 110 | } |
2257 | 85 | } | 111 | } |
2258 | 86 | ] | 112 | ] |
2259 | 113 | |||
2260 | 87 | PropertyChanges { | 114 | PropertyChanges { |
2264 | 88 | target: newRecipientPage.head | 115 | target: pageHeader |
2265 | 89 | actions: defaultState.actions | 116 | trailingActions: defaultState.trailingActions |
2266 | 90 | sections.model: [i18n.tr("All"), i18n.tr("Favorites")] | 117 | extension: headerSections |
2267 | 91 | } | 118 | } |
2268 | 92 | PropertyChanges { | 119 | PropertyChanges { |
2269 | 93 | target: searchField | 120 | target: searchField |
2270 | @@ -95,27 +122,34 @@ | |||
2271 | 95 | visible: false | 122 | visible: false |
2272 | 96 | } | 123 | } |
2273 | 97 | }, | 124 | }, |
2275 | 98 | PageHeadState { | 125 | State { |
2276 | 99 | id: searchingState | 126 | id: searchingState |
2277 | 100 | |||
2278 | 101 | name: "searching" | 127 | name: "searching" |
2286 | 102 | backAction: Action { | 128 | property list<QtObject> leadingActions: [ |
2287 | 103 | iconName: "back" | 129 | Action { |
2288 | 104 | text: i18n.tr("Cancel") | 130 | iconName: "back" |
2289 | 105 | onTriggered: { | 131 | text: i18n.tr("Cancel") |
2290 | 106 | newRecipientPage.forceActiveFocus() | 132 | onTriggered: { |
2291 | 107 | newRecipientPage.state = "default" | 133 | newRecipientPage.forceActiveFocus() |
2292 | 108 | newRecipientPage.head.sections.selectedIndex = 0 | 134 | newRecipientPage.state = "default" |
2293 | 135 | headerSections.selectedIndex = 0 | ||
2294 | 136 | } | ||
2295 | 109 | } | 137 | } |
2297 | 110 | } | 138 | ] |
2298 | 111 | 139 | ||
2299 | 112 | PropertyChanges { | 140 | PropertyChanges { |
2302 | 113 | target: newRecipientPage.head | 141 | target: pageHeader |
2303 | 114 | backAction: searchingState.backAction | 142 | leadingActions: searchingState.leadingActions |
2304 | 143 | trailingActions: [] | ||
2305 | 115 | contents: searchField | 144 | contents: searchField |
2306 | 116 | } | 145 | } |
2307 | 117 | 146 | ||
2308 | 118 | PropertyChanges { | 147 | PropertyChanges { |
2309 | 148 | target: headerSections | ||
2310 | 149 | visible: false | ||
2311 | 150 | } | ||
2312 | 151 | |||
2313 | 152 | PropertyChanges { | ||
2314 | 119 | target: searchField | 153 | target: searchField |
2315 | 120 | text: "" | 154 | text: "" |
2316 | 121 | visible: true | 155 | visible: true |
2317 | @@ -127,7 +161,7 @@ | |||
2318 | 127 | id: contactList | 161 | id: contactList |
2319 | 128 | objectName: "newRecipientList" | 162 | objectName: "newRecipientList" |
2320 | 129 | anchors { | 163 | anchors { |
2322 | 130 | top: parent.top | 164 | top: pageHeader.bottom |
2323 | 131 | left: parent.left | 165 | left: parent.left |
2324 | 132 | right: parent.right | 166 | right: parent.right |
2325 | 133 | bottom: keyboard.top | 167 | bottom: keyboard.top |
2326 | @@ -139,12 +173,14 @@ | |||
2327 | 139 | filterTerm: searchField.text | 173 | filterTerm: searchField.text |
2328 | 140 | onContactClicked: { | 174 | onContactClicked: { |
2329 | 141 | if (newRecipientPage.phoneToAdd != "") { | 175 | if (newRecipientPage.phoneToAdd != "") { |
2331 | 142 | mainView.addPhoneToContact(contact, | 176 | mainView.addPhoneToContact(newRecipientPage, |
2332 | 177 | contact, | ||
2333 | 143 | newRecipientPage.phoneToAdd, | 178 | newRecipientPage.phoneToAdd, |
2334 | 144 | newRecipientPage, | 179 | newRecipientPage, |
2335 | 145 | contactList.listModel) | 180 | contactList.listModel) |
2336 | 146 | } else { | 181 | } else { |
2338 | 147 | mainView.showContactDetails(contact, | 182 | mainView.showContactDetails(newRecipientPage, |
2339 | 183 | contact, | ||
2340 | 148 | newRecipientPage, | 184 | newRecipientPage, |
2341 | 149 | contactList.listModel) | 185 | contactList.listModel) |
2342 | 150 | } | 186 | } |
2343 | @@ -152,24 +188,20 @@ | |||
2344 | 152 | 188 | ||
2345 | 153 | onAddNewContactClicked: { | 189 | onAddNewContactClicked: { |
2346 | 154 | var newContact = ContactsJS.createEmptyContact(newRecipientPage.phoneToAdd, newRecipientPage) | 190 | var newContact = ContactsJS.createEmptyContact(newRecipientPage.phoneToAdd, newRecipientPage) |
2353 | 155 | pageStack.push(Qt.resolvedUrl("MessagingContactEditorPage.qml"), | 191 | mainStack.addFileToCurrentColumnSync(newRecipientPage, |
2354 | 156 | { model: contactList.listModel, | 192 | Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2355 | 157 | contact: newContact, | 193 | { model: contactList.listModel, |
2356 | 158 | initialFocusSection: (newRecipientPage.phoneToAdd != "" ? "phones" : "name"), | 194 | contact: newContact, |
2357 | 159 | contactListPage: newRecipientPage | 195 | initialFocusSection: (newRecipientPage.phoneToAdd != "" ? "phones" : "name"), |
2358 | 160 | }) | 196 | contactListPage: newRecipientPage }) |
2359 | 161 | } | 197 | } |
2360 | 162 | } | 198 | } |
2361 | 163 | 199 | ||
2362 | 164 | // WORKAROUND: This is necessary to make the header visible from a bottom edge page | ||
2363 | 165 | Component.onCompleted: { | 200 | Component.onCompleted: { |
2364 | 166 | parentPage.active = false | ||
2365 | 167 | if (QTCONTACTS_PRELOAD_VCARD !== "") { | 201 | if (QTCONTACTS_PRELOAD_VCARD !== "") { |
2366 | 168 | contactList.listModel.importContacts("file://" + QTCONTACTS_PRELOAD_VCARD) | 202 | contactList.listModel.importContacts("file://" + QTCONTACTS_PRELOAD_VCARD) |
2367 | 169 | } | 203 | } |
2368 | 170 | } | 204 | } |
2369 | 171 | Component.onDestruction: parentPage.active = true | ||
2370 | 172 | |||
2371 | 173 | onActiveChanged: { | 205 | onActiveChanged: { |
2372 | 174 | if (active && (state === "searching")) { | 206 | if (active && (state === "searching")) { |
2373 | 175 | searchField.forceActiveFocus() | 207 | searchField.forceActiveFocus() |
2374 | 176 | 208 | ||
2375 | === modified file 'src/qml/RegularMessageDelegate.qml' | |||
2376 | --- src/qml/RegularMessageDelegate.qml 2015-08-05 19:31:15 +0000 | |||
2377 | +++ src/qml/RegularMessageDelegate.qml 2016-02-01 23:18:57 +0000 | |||
2378 | @@ -57,7 +57,7 @@ | |||
2379 | 57 | selectionMode: root.isInSelectionMode | 57 | selectionMode: root.isInSelectionMode |
2380 | 58 | accountLabel: { | 58 | accountLabel: { |
2381 | 59 | var account = telepathyHelper.accountForId(accountId) | 59 | var account = telepathyHelper.accountForId(accountId) |
2383 | 60 | if (account.type == AccountEntry.PhoneAccount || account.type == AccountEntry.MultimediaAccount) { | 60 | if (account && (account.type == AccountEntry.PhoneAccount || account.type == AccountEntry.MultimediaAccount)) { |
2384 | 61 | if (multiplePhoneAccounts) { | 61 | if (multiplePhoneAccounts) { |
2385 | 62 | return account.displayName | 62 | return account.displayName |
2386 | 63 | } | 63 | } |
2387 | 64 | 64 | ||
2388 | === modified file 'src/qml/SettingsPage.qml' | |||
2389 | --- src/qml/SettingsPage.qml 2015-09-14 13:51:27 +0000 | |||
2390 | +++ src/qml/SettingsPage.qml 2016-02-01 23:18:57 +0000 | |||
2391 | @@ -35,6 +35,11 @@ | |||
2392 | 35 | schema.id: "com.ubuntu.phone" | 35 | schema.id: "com.ubuntu.phone" |
2393 | 36 | } | 36 | } |
2394 | 37 | 37 | ||
2395 | 38 | header: PageHeader { | ||
2396 | 39 | id: pageHeader | ||
2397 | 40 | title: settingsPage.title | ||
2398 | 41 | } | ||
2399 | 42 | |||
2400 | 38 | Component { | 43 | Component { |
2401 | 39 | id: settingDelegate | 44 | id: settingDelegate |
2402 | 40 | Item { | 45 | Item { |
2403 | @@ -66,9 +71,25 @@ | |||
2404 | 66 | } | 71 | } |
2405 | 67 | 72 | ||
2406 | 68 | ListView { | 73 | ListView { |
2408 | 69 | anchors.fill: parent | 74 | anchors { |
2409 | 75 | top: pageHeader.bottom | ||
2410 | 76 | left: parent.left | ||
2411 | 77 | right: parent.right | ||
2412 | 78 | bottom: parent.bottom | ||
2413 | 79 | } | ||
2414 | 70 | model: settingsModel | 80 | model: settingsModel |
2415 | 71 | delegate: settingDelegate | 81 | delegate: settingDelegate |
2416 | 72 | } | 82 | } |
2417 | 83 | |||
2418 | 84 | Loader { | ||
2419 | 85 | id: messagesBottomEdgeLoader | ||
2420 | 86 | active: mainView.dualPanel | ||
2421 | 87 | sourceComponent: MessagingBottomEdge { | ||
2422 | 88 | id: messagesBottomEdge | ||
2423 | 89 | parent: settingsPage | ||
2424 | 90 | hint.text: "" | ||
2425 | 91 | hint.height: 0 | ||
2426 | 92 | } | ||
2427 | 93 | } | ||
2428 | 73 | } | 94 | } |
2429 | 74 | 95 | ||
2430 | 75 | 96 | ||
2431 | === modified file 'src/qml/messaging-app.qml' | |||
2432 | --- src/qml/messaging-app.qml 2016-02-01 23:18:57 +0000 | |||
2433 | +++ src/qml/messaging-app.qml 2016-02-01 23:18:57 +0000 | |||
2434 | @@ -39,6 +39,10 @@ | |||
2435 | 39 | } | 39 | } |
2436 | 40 | property QtObject account: defaultPhoneAccount() | 40 | property QtObject account: defaultPhoneAccount() |
2437 | 41 | property bool applicationActive: Qt.application.active | 41 | property bool applicationActive: Qt.application.active |
2438 | 42 | property alias mainStack: layout | ||
2439 | 43 | property bool dualPanel: mainStack.columns > 1 | ||
2440 | 44 | property QtObject bottomEdge: null | ||
2441 | 45 | property bool composingNewMessage: bottomEdge.status === BottomEdge.Committed | ||
2442 | 42 | 46 | ||
2443 | 43 | activeFocusOnPress: false | 47 | activeFocusOnPress: false |
2444 | 44 | 48 | ||
2445 | @@ -58,7 +62,7 @@ | |||
2446 | 58 | return null | 62 | return null |
2447 | 59 | } | 63 | } |
2448 | 60 | 64 | ||
2450 | 61 | function showContactDetails(contact, contactListPage, contactsModel) { | 65 | function showContactDetails(currentPage, contact, contactListPage, contactsModel) { |
2451 | 62 | var initialProperties = { "contactListPage": contactListPage, | 66 | var initialProperties = { "contactListPage": contactListPage, |
2452 | 63 | "model": contactsModel} | 67 | "model": contactsModel} |
2453 | 64 | 68 | ||
2454 | @@ -68,21 +72,24 @@ | |||
2455 | 68 | initialProperties['contact'] = contact | 72 | initialProperties['contact'] = contact |
2456 | 69 | } | 73 | } |
2457 | 70 | 74 | ||
2470 | 71 | mainStack.push(Qt.resolvedUrl("MessagingContactViewPage.qml"), | 75 | mainStack.addFileToCurrentColumnSync(currentPage, |
2471 | 72 | initialProperties) | 76 | Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2472 | 73 | } | 77 | initialProperties) |
2473 | 74 | 78 | } | |
2474 | 75 | function addNewContact(phoneNumber, contactListPage) { | 79 | |
2475 | 76 | mainStack.push(Qt.resolvedUrl("MessagingContactEditorPage.qml"), | 80 | function addNewContact(currentPage, phoneNumber, contactListPage) { |
2476 | 77 | { "contactId": contactId, | 81 | mainStack.addFileToCurrentColumnSync(currentPage, |
2477 | 78 | "addPhoneToContact": phoneNumber, | 82 | Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2478 | 79 | "contactListPage": contactListPage }) | 83 | { "contactId": contactId, |
2479 | 80 | } | 84 | "addPhoneToContact": phoneNumber, |
2480 | 81 | 85 | "contactListPage": contactListPage }) | |
2481 | 82 | function addPhoneToContact(contact, phoneNumber, contactListPage, contactsModel) { | 86 | } |
2482 | 87 | |||
2483 | 88 | function addPhoneToContact(currentPage, contact, phoneNumber, contactListPage, contactsModel) { | ||
2484 | 83 | if (contact === "") { | 89 | if (contact === "") { |
2487 | 84 | mainStack.push(Qt.resolvedUrl("NewRecipientPage.qml"), | 90 | mainStack.addFileToCurrentColumnSync(currentPage, |
2488 | 85 | { "phoneToAdd": phoneNumber }) | 91 | Qt.resolvedUrl("NewRecipientPage.qml"), |
2489 | 92 | { "phoneToAdd": phoneNumber }) | ||
2490 | 86 | } else { | 93 | } else { |
2491 | 87 | var initialProperties = { "addPhoneToContact": phoneNumber, | 94 | var initialProperties = { "addPhoneToContact": phoneNumber, |
2492 | 88 | "contactListPage": contactListPage, | 95 | "contactListPage": contactListPage, |
2493 | @@ -92,8 +99,9 @@ | |||
2494 | 92 | } else { | 99 | } else { |
2495 | 93 | initialProperties['contact'] = contact | 100 | initialProperties['contact'] = contact |
2496 | 94 | } | 101 | } |
2499 | 95 | mainStack.push(Qt.resolvedUrl("MessagingContactViewPage.qml"), | 102 | mainStack.addFileToCurrentColumnSync(currentPage, |
2500 | 96 | initialProperties) | 103 | Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2501 | 104 | initialProperties) | ||
2502 | 97 | } | 105 | } |
2503 | 98 | } | 106 | } |
2504 | 99 | 107 | ||
2505 | @@ -119,6 +127,10 @@ | |||
2506 | 119 | threadModel.removeThreads(threads); | 127 | threadModel.removeThreads(threads); |
2507 | 120 | } | 128 | } |
2508 | 121 | 129 | ||
2509 | 130 | function showBottomEdgePage(properties) { | ||
2510 | 131 | bottomEdge.commitWithProperties(properties) | ||
2511 | 132 | } | ||
2512 | 133 | |||
2513 | 122 | Connections { | 134 | Connections { |
2514 | 123 | target: telepathyHelper | 135 | target: telepathyHelper |
2515 | 124 | // restore default bindings if any system settings changed | 136 | // restore default bindings if any system settings changed |
2516 | @@ -141,13 +153,14 @@ | |||
2517 | 141 | Component.onCompleted: { | 153 | Component.onCompleted: { |
2518 | 142 | i18n.domain = "messaging-app" | 154 | i18n.domain = "messaging-app" |
2519 | 143 | i18n.bindtextdomain("messaging-app", i18nDirectory) | 155 | i18n.bindtextdomain("messaging-app", i18nDirectory) |
2520 | 156 | emptyStack() | ||
2521 | 144 | } | 157 | } |
2522 | 145 | 158 | ||
2523 | 146 | Connections { | 159 | Connections { |
2524 | 147 | target: telepathyHelper | 160 | target: telepathyHelper |
2525 | 148 | onSetupReady: { | 161 | onSetupReady: { |
2526 | 149 | if (multiplePhoneAccounts && !telepathyHelper.defaultMessagingAccount && | 162 | if (multiplePhoneAccounts && !telepathyHelper.defaultMessagingAccount && |
2528 | 150 | !settings.mainViewIgnoreFirstTimeDialog && mainStack.depth === 1) { | 163 | !settings.mainViewIgnoreFirstTimeDialog && mainPage.displayedThreadIndex < 0) { |
2529 | 151 | PopupUtils.open(Qt.createComponent("Dialogs/NoDefaultSIMCardDialog.qml").createObject(mainView)) | 164 | PopupUtils.open(Qt.createComponent("Dialogs/NoDefaultSIMCardDialog.qml").createObject(mainView)) |
2530 | 152 | } | 165 | } |
2531 | 153 | } | 166 | } |
2532 | @@ -187,7 +200,7 @@ | |||
2533 | 187 | var properties = {} | 200 | var properties = {} |
2534 | 188 | emptyStack() | 201 | emptyStack() |
2535 | 189 | properties["sharedAttachmentsTransfer"] = transfer | 202 | properties["sharedAttachmentsTransfer"] = transfer |
2537 | 190 | mainStack.currentPage.showBottomEdgePage(Qt.resolvedUrl("Messages.qml"), properties) | 203 | mainView.showBottomEdgePage(properties) |
2538 | 191 | } | 204 | } |
2539 | 192 | } | 205 | } |
2540 | 193 | 206 | ||
2541 | @@ -211,15 +224,21 @@ | |||
2542 | 211 | } | 224 | } |
2543 | 212 | 225 | ||
2544 | 213 | function emptyStack() { | 226 | function emptyStack() { |
2547 | 214 | while (mainStack.depth !== 1 && mainStack.depth !== 0) { | 227 | mainStack.removePage(mainPage) |
2548 | 215 | mainStack.pop() | 228 | layout.deleteInstances() |
2549 | 229 | showEmptyState() | ||
2550 | 230 | } | ||
2551 | 231 | |||
2552 | 232 | function showEmptyState() { | ||
2553 | 233 | if (mainStack.columns > 1) { | ||
2554 | 234 | layout.addComponentToNextColumnSync(mainStack.primaryPage, emptyStatePageComponent) | ||
2555 | 216 | } | 235 | } |
2556 | 217 | } | 236 | } |
2557 | 218 | 237 | ||
2558 | 219 | function startNewMessage() { | 238 | function startNewMessage() { |
2559 | 220 | var properties = {} | 239 | var properties = {} |
2560 | 221 | emptyStack() | 240 | emptyStack() |
2562 | 222 | mainStack.currentPage.showBottomEdgePage(Qt.resolvedUrl("Messages.qml")) | 241 | mainView.showBottomEdgePage(properties) |
2563 | 223 | } | 242 | } |
2564 | 224 | 243 | ||
2565 | 225 | function startChat(identifiers, text, accountId) { | 244 | function startChat(identifiers, text, accountId) { |
2566 | @@ -261,9 +280,26 @@ | |||
2567 | 261 | if (typeof(accountId)!=='undefined') { | 280 | if (typeof(accountId)!=='undefined') { |
2568 | 262 | properties["accountId"] = accountId | 281 | properties["accountId"] = accountId |
2569 | 263 | } | 282 | } |
2570 | 283 | |||
2571 | 264 | emptyStack() | 284 | emptyStack() |
2574 | 265 | mainStack.push(Qt.resolvedUrl("Messages.qml"), properties) | 285 | // FIXME: AdaptivePageLayout takes a really long time to create pages, |
2575 | 266 | } | 286 | // so we create manually and push that |
2576 | 287 | mainStack.addComponentToNextColumnSync(mainPage, messagesWithBottomEdge, properties) | ||
2577 | 288 | } | ||
2578 | 289 | |||
2579 | 290 | InputInfo { | ||
2580 | 291 | id: inputInfo | ||
2581 | 292 | } | ||
2582 | 293 | |||
2583 | 294 | // WORKAROUND: Due the missing feature on SDK, they can not detect if | ||
2584 | 295 | // there is a mouse attached to device or not. And this will cause the | ||
2585 | 296 | // bootom edge component to not work correct on desktop. | ||
2586 | 297 | Binding { | ||
2587 | 298 | target: QuickUtils | ||
2588 | 299 | property: "mouseAttached" | ||
2589 | 300 | value: inputInfo.hasMouse | ||
2590 | 301 | } | ||
2591 | 302 | |||
2592 | 267 | 303 | ||
2593 | 268 | Connections { | 304 | Connections { |
2594 | 269 | target: UriHandler | 305 | target: UriHandler |
2595 | @@ -274,11 +310,60 @@ | |||
2596 | 274 | } | 310 | } |
2597 | 275 | } | 311 | } |
2598 | 276 | 312 | ||
2605 | 277 | 313 | Component { | |
2606 | 278 | PageStack { | 314 | id: messagesWithBottomEdge |
2607 | 279 | id: mainStack | 315 | |
2608 | 280 | 316 | Messages { | |
2609 | 281 | objectName: "mainStack" | 317 | id: messages |
2610 | 282 | Component.onCompleted: mainStack.push(Qt.resolvedUrl("MainPage.qml")) | 318 | height: mainPage.height |
2611 | 319 | |||
2612 | 320 | Component.onCompleted: mainPage._messagesPage = messages | ||
2613 | 321 | Loader { | ||
2614 | 322 | id: messagesBottomEdgeLoader | ||
2615 | 323 | active: mainView.dualPanel | ||
2616 | 324 | sourceComponent: MessagingBottomEdge { | ||
2617 | 325 | id: messagesBottomEdge | ||
2618 | 326 | parent: messages | ||
2619 | 327 | hint.text: "" | ||
2620 | 328 | hint.height: 0 | ||
2621 | 329 | } | ||
2622 | 330 | } | ||
2623 | 331 | } | ||
2624 | 332 | } | ||
2625 | 333 | |||
2626 | 334 | Component { | ||
2627 | 335 | id: emptyStatePageComponent | ||
2628 | 336 | Page { | ||
2629 | 337 | id: emptyStatePage | ||
2630 | 338 | |||
2631 | 339 | EmptyState { | ||
2632 | 340 | labelVisible: mainPage.isEmpty | ||
2633 | 341 | } | ||
2634 | 342 | |||
2635 | 343 | header: PageHeader { } | ||
2636 | 344 | |||
2637 | 345 | Loader { | ||
2638 | 346 | id: bottomEdgeLoader | ||
2639 | 347 | sourceComponent: MessagingBottomEdge { | ||
2640 | 348 | parent: emptyStatePage | ||
2641 | 349 | } | ||
2642 | 350 | } | ||
2643 | 351 | } | ||
2644 | 352 | } | ||
2645 | 353 | |||
2646 | 354 | MessagingPageLayout { | ||
2647 | 355 | id: layout | ||
2648 | 356 | anchors.fill: parent | ||
2649 | 357 | primaryPage: MainPage { | ||
2650 | 358 | id: mainPage | ||
2651 | 359 | } | ||
2652 | 360 | |||
2653 | 361 | onColumnsChanged: { | ||
2654 | 362 | // we only have things to do here in case no thread is selected | ||
2655 | 363 | if (mainPage.displayedThreadIndex < 0) { | ||
2656 | 364 | layout.removePage(mainPage) | ||
2657 | 365 | showEmptyState() | ||
2658 | 366 | } | ||
2659 | 367 | } | ||
2660 | 283 | } | 368 | } |
2661 | 284 | } | 369 | } |
2662 | 285 | 370 | ||
2663 | === modified file 'tests/autopilot/messaging_app/emulators.py' | |||
2664 | --- tests/autopilot/messaging_app/emulators.py 2015-11-04 19:57:45 +0000 | |||
2665 | +++ tests/autopilot/messaging_app/emulators.py 2016-02-01 23:18:57 +0000 | |||
2666 | @@ -78,14 +78,14 @@ | |||
2667 | 78 | 78 | ||
2668 | 79 | def click_header_action(self, action): | 79 | def click_header_action(self, action): |
2669 | 80 | """Click the action 'action' on the header""" | 80 | """Click the action 'action' on the header""" |
2671 | 81 | self.get_header().click_action_button(action) | 81 | action = self.wait_select_single(objectName='%s_button' % action) |
2672 | 82 | self.pointing_device.click_object(action) | ||
2673 | 82 | 83 | ||
2674 | 83 | # messages page | 84 | # messages page |
2675 | 84 | def get_messages_page(self): | 85 | def get_messages_page(self): |
2676 | 85 | """Return messages with objectName messagesPage""" | 86 | """Return messages with objectName messagesPage""" |
2677 | 86 | 87 | ||
2680 | 87 | return self.wait_select_single("Messages", objectName="messagesPage", | 88 | return self.wait_select_single("Messages", objectName="messagesPage") |
2679 | 88 | active=True) | ||
2681 | 89 | 89 | ||
2682 | 90 | def get_newmessage_textfield(self): | 90 | def get_newmessage_textfield(self): |
2683 | 91 | """Return TextField with objectName newPhoneNumberField""" | 91 | """Return TextField with objectName newPhoneNumberField""" |
2684 | @@ -116,40 +116,12 @@ | |||
2685 | 116 | 116 | ||
2686 | 117 | return self.get_messages_page().select_single(objectName='sendButton') | 117 | return self.get_messages_page().select_single(objectName='sendButton') |
2687 | 118 | 118 | ||
2688 | 119 | def get_toolbar_back_button(self): | ||
2689 | 120 | """Return toolbar button with objectName back_toolbar_button""" | ||
2690 | 121 | |||
2691 | 122 | return self.select_single( | ||
2692 | 123 | objectName='back_toolbar_button' | ||
2693 | 124 | ) | ||
2694 | 125 | |||
2695 | 126 | def get_toolbar_select_messages_button(self): | ||
2696 | 127 | """Return toolbar button with objectName selectMessagesButton""" | ||
2697 | 128 | |||
2698 | 129 | return self.select_single( | ||
2699 | 130 | objectName='selectMessagesButton' | ||
2700 | 131 | ) | ||
2701 | 132 | |||
2702 | 133 | def get_contact_list_view(self): | 119 | def get_contact_list_view(self): |
2703 | 134 | """Returns the ContactListView object""" | 120 | """Returns the ContactListView object""" |
2704 | 135 | return self.select_single( | 121 | return self.select_single( |
2705 | 136 | objectName='newRecipientList' | 122 | objectName='newRecipientList' |
2706 | 137 | ) | 123 | ) |
2707 | 138 | 124 | ||
2708 | 139 | def get_toolbar_contact_profile_button(self): | ||
2709 | 140 | """Return toolbar button with objectName contactProfileButton""" | ||
2710 | 141 | |||
2711 | 142 | return self.select_single( | ||
2712 | 143 | objectName='contactProfileButton' | ||
2713 | 144 | ) | ||
2714 | 145 | |||
2715 | 146 | def get_toolbar_contact_call_button(self): | ||
2716 | 147 | """Return toolbar button with objectName contactCallButton""" | ||
2717 | 148 | |||
2718 | 149 | return self.select_single( | ||
2719 | 150 | objectName='contactCallButton' | ||
2720 | 151 | ) | ||
2721 | 152 | |||
2722 | 153 | def get_dialog_buttons(self, visible=True): | 125 | def get_dialog_buttons(self, visible=True): |
2723 | 154 | """Return DialogButtons | 126 | """Return DialogButtons |
2724 | 155 | 127 | ||
2725 | @@ -213,7 +185,7 @@ | |||
2726 | 213 | 185 | ||
2727 | 214 | def start_new_message(self): | 186 | def start_new_message(self): |
2728 | 215 | """Reveal the bottom edge page to start composing a new message""" | 187 | """Reveal the bottom edge page to start composing a new message""" |
2730 | 216 | self.get_main_page().reveal_bottom_edge_page() | 188 | self.reveal_bottom_edge_page() |
2731 | 217 | 189 | ||
2732 | 218 | def enable_messages_selection_mode(self): | 190 | def enable_messages_selection_mode(self): |
2733 | 219 | """Enable the selection mode on the messages page by pressing and | 191 | """Enable the selection mode on the messages page by pressing and |
2734 | @@ -338,38 +310,30 @@ | |||
2735 | 338 | 310 | ||
2736 | 339 | def open_settings_page(self): | 311 | def open_settings_page(self): |
2737 | 340 | self.click_threads_header_settings() | 312 | self.click_threads_header_settings() |
2739 | 341 | return self.wait_select_single(SettingsPage) | 313 | settings_page = self.wait_select_single(SettingsPage) |
2740 | 314 | settings_page.active.waitFor(True) | ||
2741 | 315 | return settings_page | ||
2742 | 342 | 316 | ||
2743 | 343 | def get_swipe_item_demo(self): | 317 | def get_swipe_item_demo(self): |
2744 | 344 | return self.wait_select_single( | 318 | return self.wait_select_single( |
2745 | 345 | 'SwipeItemDemo', objectName='swipeItemDemo', parentActive=True) | 319 | 'SwipeItemDemo', objectName='swipeItemDemo', parentActive=True) |
2746 | 346 | 320 | ||
2747 | 347 | |||
2748 | 348 | class PageWithBottomEdge(MainView): | ||
2749 | 349 | """An emulator class that makes it easy to interact with the bottom edge | ||
2750 | 350 | swipe page""" | ||
2751 | 351 | def __init__(self, *args): | ||
2752 | 352 | super(PageWithBottomEdge, self).__init__(*args) | ||
2753 | 353 | |||
2754 | 354 | def reveal_bottom_edge_page(self): | 321 | def reveal_bottom_edge_page(self): |
2755 | 355 | """Bring the bottom edge page to the screen""" | 322 | """Bring the bottom edge page to the screen""" |
2756 | 356 | self.bottomEdgePageLoaded.wait_for(True) | ||
2757 | 357 | try: | 323 | try: |
2763 | 358 | action_item = self.wait_select_single(objectName='bottomEdgeTip') | 324 | start_x = (self.globalRect.x + |
2764 | 359 | start_x = (action_item.globalRect.x + | 325 | (self.globalRect.width * 0.5)) |
2765 | 360 | (action_item.globalRect.width * 0.5)) | 326 | start_y = (self.globalRect.y + self.height) |
2761 | 361 | start_y = (action_item.globalRect.y + | ||
2762 | 362 | (action_item.height * 0.5)) | ||
2766 | 363 | stop_y = start_y - (self.height * 0.7) | 327 | stop_y = start_y - (self.height * 0.7) |
2767 | 364 | self.pointing_device.drag(start_x, start_y, | 328 | self.pointing_device.drag(start_x, start_y, |
2768 | 365 | start_x, stop_y, rate=2) | 329 | start_x, stop_y, rate=2) |
2770 | 366 | self.isReady.wait_for(True) | 330 | self.composingNewMessage.wait_for(True) |
2771 | 367 | except StateNotFoundError: | 331 | except StateNotFoundError: |
2772 | 368 | logger.error('BottomEdge element not found.') | 332 | logger.error('BottomEdge element not found.') |
2773 | 369 | raise | 333 | raise |
2774 | 370 | 334 | ||
2775 | 371 | 335 | ||
2777 | 372 | class MainPage(PageWithBottomEdge): | 336 | class MainPage(MainView): |
2778 | 373 | """Autopilot helper for the Main Page.""" | 337 | """Autopilot helper for the Main Page.""" |
2779 | 374 | 338 | ||
2780 | 375 | def get_thread_count(self): | 339 | def get_thread_count(self): |
2781 | 376 | 340 | ||
2782 | === modified file 'tests/qml/CMakeLists.txt' | |||
2783 | --- tests/qml/CMakeLists.txt 2016-02-01 23:18:57 +0000 | |||
2784 | +++ tests/qml/CMakeLists.txt 2016-02-01 23:18:57 +0000 | |||
2785 | @@ -36,7 +36,8 @@ | |||
2786 | 36 | 36 | ||
2787 | 37 | add_test(${TEST} ${XVFB_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} | 37 | add_test(${TEST} ${XVFB_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} |
2788 | 38 | -input ${CMAKE_CURRENT_SOURCE_DIR} | 38 | -input ${CMAKE_CURRENT_SOURCE_DIR} |
2790 | 39 | -import ${CMAKE_BINARY_DIR}/src) | 39 | -import ${CMAKE_BINARY_DIR}/src |
2791 | 40 | -import ${UNITY8_QML_PATH}) | ||
2792 | 40 | 41 | ||
2793 | 41 | # make qml files visible in QtCreator | 42 | # make qml files visible in QtCreator |
2794 | 42 | file(GLOB_RECURSE NON_COMPILED_FILES *.qml) | 43 | file(GLOB_RECURSE NON_COMPILED_FILES *.qml) |
2795 | 43 | 44 | ||
2796 | === modified file 'tests/qml/tst_MessagesView.qml' | |||
2797 | --- tests/qml/tst_MessagesView.qml 2015-11-19 00:34:45 +0000 | |||
2798 | +++ tests/qml/tst_MessagesView.qml 2016-02-01 23:18:57 +0000 | |||
2799 | @@ -129,16 +129,21 @@ | |||
2800 | 129 | var senderId = "1234567" | 129 | var senderId = "1234567" |
2801 | 130 | var stack = findChild(mainViewLoader, "mainStack") | 130 | var stack = findChild(mainViewLoader, "mainStack") |
2802 | 131 | tryCompare(mainViewLoader.item, 'applicationActive', true) | 131 | tryCompare(mainViewLoader.item, 'applicationActive', true) |
2803 | 132 | tryCompare(stack, 'depth', 1) | ||
2804 | 133 | // if messaging-app has no account set, it will not try to get the thread from history | 132 | // if messaging-app has no account set, it will not try to get the thread from history |
2805 | 134 | // and instead will generate the list of participants, take advantage of that | 133 | // and instead will generate the list of participants, take advantage of that |
2806 | 135 | var account = mainViewLoader.item.account | 134 | var account = mainViewLoader.item.account |
2807 | 136 | mainViewLoader.item.account = null | 135 | mainViewLoader.item.account = null |
2808 | 137 | mainViewLoader.item.startChat(senderId, "") | 136 | mainViewLoader.item.startChat(senderId, "") |
2809 | 138 | mainViewLoader.item.account = account | 137 | mainViewLoader.item.account = account |
2813 | 139 | tryCompare(stack, 'depth', 2) | 138 | var messageList |
2814 | 140 | mainViewLoader.item.applicationActive = false | 139 | while (true) { |
2815 | 141 | var messageList = findChild(mainViewLoader, "messageList") | 140 | messageList = findChild(mainViewLoader, "messageList") |
2816 | 141 | if (messageList) { | ||
2817 | 142 | break | ||
2818 | 143 | } | ||
2819 | 144 | wait(200) | ||
2820 | 145 | } | ||
2821 | 146 | |||
2822 | 142 | messageList.listModel = messagesModel | 147 | messageList.listModel = messagesModel |
2823 | 143 | tryCompare(messageList, 'count', 2) | 148 | tryCompare(messageList, 'count', 2) |
2824 | 144 | compare(messageAcknowledgeSpy.count, 0) | 149 | compare(messageAcknowledgeSpy.count, 0) |
FAILED: Continuous integration, rev:449 jenkins. qa.ubuntu. com/job/ messaging- app-ci/ 685/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 4994/console jenkins. qa.ubuntu. com/job/ messaging- app-vivid- i386-ci/ 198/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 4991/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/messaging- app-ci/ 685/rebuild
http://