diff options
Diffstat (limited to 'components/kube/qml/Kube.qml')
-rw-r--r-- | components/kube/qml/Kube.qml | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/components/kube/qml/Kube.qml b/components/kube/qml/Kube.qml new file mode 100644 index 00000000..79b18a2f --- /dev/null +++ b/components/kube/qml/Kube.qml | |||
@@ -0,0 +1,413 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 Michael Bohlender, <michael.bohlender@kdemail.net> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | ||
18 | |||
19 | |||
20 | import QtQuick 2.7 | ||
21 | import QtQuick.Layouts 1.3 | ||
22 | import QtQuick.Window 2.0 | ||
23 | |||
24 | import QtQuick.Controls 2.0 as Controls2 | ||
25 | import org.kube.framework 1.0 as Kube | ||
26 | |||
27 | Controls2.ApplicationWindow { | ||
28 | id: app | ||
29 | |||
30 | property int sidebarWidth: Kube.Units.gridUnit + Kube.Units.largeSpacing | ||
31 | |||
32 | height: Screen.desktopAvailableHeight * 0.8 | ||
33 | width: Screen.desktopAvailableWidth * 0.8 | ||
34 | visible: true | ||
35 | |||
36 | //Application default font | ||
37 | font.family: Kube.Font.fontFamily | ||
38 | |||
39 | //Application context | ||
40 | property variant currentFolder | ||
41 | onCurrentFolderChanged: { | ||
42 | if (!!currentFolder) { | ||
43 | Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": currentFolder}) | ||
44 | } | ||
45 | } | ||
46 | property variant currentAccount | ||
47 | onCurrentAccountChanged: { | ||
48 | if (!!currentAccount) { | ||
49 | Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": currentAccount}) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | //accountId -> requiresKeyring | ||
54 | Kube.AccountFactory { | ||
55 | id: accountFactory | ||
56 | accountId: !!app.currentAccount ? app.currentAccount : "" | ||
57 | } | ||
58 | |||
59 | //Interval sync | ||
60 | Timer { | ||
61 | id: intervalSync | ||
62 | //5min | ||
63 | interval: 300000 | ||
64 | running: !!app.currentFolder | ||
65 | repeat: true | ||
66 | onTriggered: Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": app.currentFolder}) | ||
67 | } | ||
68 | |||
69 | Kube.StartupCheck { | ||
70 | id: startupCheck | ||
71 | } | ||
72 | |||
73 | //Listener | ||
74 | Kube.Listener { | ||
75 | filter: Kube.Messages.accountSelection | ||
76 | onMessageReceived: app.currentAccount = message.account | ||
77 | } | ||
78 | |||
79 | Kube.Listener { | ||
80 | filter: Kube.Messages.folderSelection | ||
81 | onMessageReceived: app.currentFolder = message.folder | ||
82 | } | ||
83 | |||
84 | Kube.Listener { | ||
85 | filter: Kube.Messages.notification | ||
86 | onMessageReceived: { | ||
87 | notificationPopup.notify(message.message); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | Kube.Listener { | ||
92 | filter: Kube.Messages.reply | ||
93 | onMessageReceived: { | ||
94 | kubeViews.openComposerWithMail(message.mail, false) | ||
95 | } | ||
96 | } | ||
97 | |||
98 | Kube.Listener { | ||
99 | filter: Kube.Messages.edit | ||
100 | onMessageReceived: { | ||
101 | kubeViews.openComposerWithMail(message.mail, true) | ||
102 | } | ||
103 | } | ||
104 | |||
105 | Kube.Listener { | ||
106 | filter: Kube.Messages.compose | ||
107 | onMessageReceived: kubeViews.openComposer(true, message.recipients) | ||
108 | } | ||
109 | |||
110 | Kube.Listener { | ||
111 | filter: Kube.Messages.requestLogin | ||
112 | onMessageReceived: kubeViews.setLoginView() | ||
113 | } | ||
114 | |||
115 | Kube.Listener { | ||
116 | filter: Kube.Messages.requestAccountsConfiguration | ||
117 | onMessageReceived: kubeViews.setAccountsView() | ||
118 | } | ||
119 | |||
120 | //BEGIN Shortcuts | ||
121 | Shortcut { | ||
122 | sequence: StandardKey.Quit | ||
123 | onActivated: Qt.quit() | ||
124 | } | ||
125 | Shortcut { | ||
126 | onActivated: Kube.Fabric.postMessage(Kube.Messages.search, {}) | ||
127 | sequence: StandardKey.Find | ||
128 | } | ||
129 | Shortcut { | ||
130 | onActivated: { | ||
131 | Kube.Fabric.postMessage(Kube.Messages.unlockKeyring, {accountId: app.currentAccount}) | ||
132 | } | ||
133 | sequence: "Ctrl+l" | ||
134 | } | ||
135 | Shortcut { | ||
136 | id: syncShortcut | ||
137 | sequence: StandardKey.Refresh | ||
138 | onActivated: !!app.currentFolder ? Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": app.currentFolder}) : Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": app.currentAccount}) | ||
139 | } | ||
140 | //END Shortcuts | ||
141 | |||
142 | //BEGIN background | ||
143 | Rectangle { | ||
144 | anchors.fill: parent | ||
145 | |||
146 | color: Kube.Colors.backgroundColor | ||
147 | } | ||
148 | //END background | ||
149 | |||
150 | //BEGIN Main content | ||
151 | RowLayout { | ||
152 | id: mainContent | ||
153 | spacing: 0 | ||
154 | anchors.fill: parent | ||
155 | |||
156 | Rectangle { | ||
157 | id: sideBar | ||
158 | |||
159 | anchors { | ||
160 | top: mainContent.top | ||
161 | bottom: mainContent.bottom | ||
162 | } | ||
163 | width: app.sidebarWidth | ||
164 | color: Kube.Colors.textColor | ||
165 | |||
166 | Rectangle { | ||
167 | anchors.right: parent.right | ||
168 | width: 1 | ||
169 | height: parent.height | ||
170 | color: Kube.Colors.viewBackgroundColor | ||
171 | opacity: 0.3 | ||
172 | } | ||
173 | |||
174 | Controls2.ButtonGroup { id: viewButtonGroup } | ||
175 | |||
176 | Column { | ||
177 | anchors { | ||
178 | top: parent.top | ||
179 | topMargin: Kube.Units.smallSpacing | ||
180 | horizontalCenter: parent.horizontalCenter | ||
181 | } | ||
182 | |||
183 | spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing | ||
184 | |||
185 | Kube.IconButton { | ||
186 | id: composerButton | ||
187 | iconName: Kube.Icons.edit_inverted | ||
188 | onClicked: kubeViews.openComposer(false, []) | ||
189 | activeFocusOnTab: true | ||
190 | checkable: true | ||
191 | Controls2.ButtonGroup.group: viewButtonGroup | ||
192 | tooltip: qsTr("composer") | ||
193 | } | ||
194 | |||
195 | Kube.IconButton { | ||
196 | id: mailButton | ||
197 | iconName: Kube.Icons.mail_inverted | ||
198 | onClicked: kubeViews.setMailView() | ||
199 | activeFocusOnTab: true | ||
200 | checkable: true | ||
201 | checked: true | ||
202 | Controls2.ButtonGroup.group: viewButtonGroup | ||
203 | tooltip: qsTr("mails") | ||
204 | } | ||
205 | |||
206 | Kube.IconButton { | ||
207 | id: peopleButton | ||
208 | iconName: Kube.Icons.user_inverted | ||
209 | onClicked: kubeViews.setPeopleView() | ||
210 | activeFocusOnTab: true | ||
211 | checkable: true | ||
212 | Controls2.ButtonGroup.group: viewButtonGroup | ||
213 | tooltip: qsTr("people") | ||
214 | } | ||
215 | } | ||
216 | Column { | ||
217 | anchors { | ||
218 | bottom: parent.bottom | ||
219 | bottomMargin: Kube.Units.smallSpacing | ||
220 | horizontalCenter: parent.horizontalCenter | ||
221 | } | ||
222 | |||
223 | spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing | ||
224 | Kube.Outbox { | ||
225 | height: Kube.Units.gridUnit * 1.5 | ||
226 | width: height | ||
227 | |||
228 | Kube.ToolTip { | ||
229 | text: qsTr("outbox") | ||
230 | visible: parent.hovered | ||
231 | } | ||
232 | } | ||
233 | |||
234 | Kube.IconButton { | ||
235 | id: logButton | ||
236 | iconName: Kube.Icons.info_inverted | ||
237 | onClicked: kubeViews.setLogView() | ||
238 | activeFocusOnTab: true | ||
239 | checkable: true | ||
240 | alert: logView.pendingError | ||
241 | Controls2.ButtonGroup.group: viewButtonGroup | ||
242 | tooltip: qsTr("logview") | ||
243 | } | ||
244 | |||
245 | Kube.IconButton { | ||
246 | id: accountsButton | ||
247 | iconName: Kube.Icons.menu_inverted | ||
248 | onClicked: kubeViews.setAccountsView() | ||
249 | activeFocusOnTab: true | ||
250 | checkable: true | ||
251 | Controls2.ButtonGroup.group: viewButtonGroup | ||
252 | tooltip: qsTr("settings") | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | Controls2.StackView { | ||
257 | id: kubeViews | ||
258 | |||
259 | anchors { | ||
260 | top: mainContent.top | ||
261 | bottom: mainContent.bottom | ||
262 | } | ||
263 | Layout.fillWidth: true | ||
264 | |||
265 | function loginIfNecessary() | ||
266 | { | ||
267 | if (!!app.currentAccount && !Kube.Keyring.isUnlocked(app.currentAccount)) { | ||
268 | if (accountFactory.requiresKeyring) { | ||
269 | setLoginView() | ||
270 | } else { | ||
271 | Kube.Keyring.unlock(app.currentAccount) | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | |||
276 | Kube.Listener { | ||
277 | filter: Kube.Messages.componentDone | ||
278 | onMessageReceived: { | ||
279 | //Return to the mailview if we try to pop everything off | ||
280 | if (kubeViews.depth == 1) { | ||
281 | kubeViews.setMailView() | ||
282 | } else { | ||
283 | kubeViews.pop(Controls2.StackView.Immediate) | ||
284 | } | ||
285 | kubeViews.loginIfNecessary() | ||
286 | } | ||
287 | } | ||
288 | |||
289 | onCurrentItemChanged: { | ||
290 | if (currentItem) { | ||
291 | currentItem.forceActiveFocus() | ||
292 | } | ||
293 | } | ||
294 | |||
295 | Component.onCompleted: { | ||
296 | //Setup the initial item stack | ||
297 | if (!currentItem) { | ||
298 | setMailView() | ||
299 | if (startupCheck.noAccount) { | ||
300 | setAccountsView() | ||
301 | } else { | ||
302 | loginIfNecessary() | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | |||
307 | ///Replace the current view (we can't go back to the old view, and we destroy the old view) | ||
308 | function replaceView(view) { | ||
309 | if (currentItem != view) { | ||
310 | kubeViews.replace(null, view, {}, Controls2.StackView.Immediate) | ||
311 | } | ||
312 | } | ||
313 | |||
314 | ///Push a new view on the stack (the old view remains, and we can go back once done) | ||
315 | function pushView(view, properties) { | ||
316 | kubeViews.push(view, properties, Controls2.StackView.Immediate) | ||
317 | } | ||
318 | |||
319 | //TODO replacing here while a composer is open is destructive | ||
320 | function setPeopleView() { | ||
321 | replaceView(peopleView) | ||
322 | } | ||
323 | |||
324 | function setMailView() { | ||
325 | replaceView(mailView) | ||
326 | } | ||
327 | |||
328 | function setAccountsView() { | ||
329 | pushView(accountsView, {}) | ||
330 | } | ||
331 | |||
332 | function setLogView() { | ||
333 | replaceView(logView) | ||
334 | } | ||
335 | |||
336 | function setLoginView() { | ||
337 | if (currentItem != loginView) { | ||
338 | pushView(loginView, {accountId: currentAccount}) | ||
339 | } | ||
340 | } | ||
341 | |||
342 | function openComposer(newMessage, recipients) { | ||
343 | pushView(composerView, {newMessage: newMessage, recipients: recipients}) | ||
344 | } | ||
345 | |||
346 | function openComposerWithMail(mail, openAsDraft) { | ||
347 | pushView(composerView, {message: mail, loadAsDraft: openAsDraft}) | ||
348 | } | ||
349 | |||
350 | |||
351 | //These items are not visible until pushed onto the stack, so we keep them in resources instead of items | ||
352 | resources: [ | ||
353 | //Not components so we maintain state | ||
354 | MailView { | ||
355 | id: mailView | ||
356 | anchors.fill: parent | ||
357 | Controls2.StackView.onActivated: mailButton.checked = true | ||
358 | Controls2.StackView.onDeactivated: mailButton.checked = false | ||
359 | }, | ||
360 | PeopleView { | ||
361 | id: peopleView | ||
362 | anchors.fill: parent | ||
363 | Controls2.StackView.onActivated: peopleButton.checked = true | ||
364 | Controls2.StackView.onDeactivated: peopleButton.checked = false | ||
365 | }, | ||
366 | //Not a component because otherwise we can't log stuff | ||
367 | LogView { | ||
368 | id: logView | ||
369 | anchors.fill: parent | ||
370 | Controls2.StackView.onActivated: logButton.checked = true | ||
371 | Controls2.StackView.onDeactivated: logButton.checked = false | ||
372 | } | ||
373 | ] | ||
374 | //A component so it's always destroyed when we're done | ||
375 | Component { | ||
376 | id: composerView | ||
377 | ComposerView { | ||
378 | anchors.fill: parent | ||
379 | Controls2.StackView.onActivated: composerButton.checked = true | ||
380 | Controls2.StackView.onDeactivated: composerButton.checked = false | ||
381 | } | ||
382 | } | ||
383 | Component { | ||
384 | id: accountsView | ||
385 | AccountsView { | ||
386 | anchors.fill: parent | ||
387 | Controls2.StackView.onActivated: accountsButton.checked = true | ||
388 | Controls2.StackView.onDeactivated: accountsButton.checked = false | ||
389 | } | ||
390 | } | ||
391 | Component { | ||
392 | id: loginView | ||
393 | LoginView { | ||
394 | anchors.fill: parent | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | //END Main content | ||
400 | |||
401 | //BEGIN Notification | ||
402 | Kube.NotificationPopup { | ||
403 | id: notificationPopup | ||
404 | |||
405 | anchors { | ||
406 | left: parent.left | ||
407 | leftMargin: app.sidebarWidth - 3 // so it does not align with the border | ||
408 | bottom: parent.bottom | ||
409 | bottomMargin: Kube.Units.gridUnit * 4 | ||
410 | } | ||
411 | } | ||
412 | //END Notification | ||
413 | } | ||