Dekko
RecipientField.qml
1 /* Copyright (C) 2016 - 2017 Dan Chapman <dpniel@ubuntu.com>
2 
3  This file is part of Dekko email client for Ubuntu devices
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU General Public License as
7  published by the Free Software Foundation; either version 2 of
8  the License or (at your option) version 3
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 import QtQuick 2.4
19 import QuickFlux 1.0
20 import Lomiri.Components 1.3
21 import Lomiri.Components.Popups 1.3
22 import Dekko.Components 1.0
23 import Dekko.Mail 1.0
24 import Dekko.Mail.API 1.0
25 import Dekko.Mail.Stores.Composer 1.0
26 import Dekko.Lomiri.Constants 1.0
27 import Dekko.Lomiri.Components 1.0
28 import "../delegates"
29 
30 FocusScope {
31  id: field
32 
33  property int recipientType: RecipientType.To
34  property alias text: input.text
35  property alias textDocument: input.textDocument
36  property alias recipientModel: repeater.model
37  property bool isInvalidated: false
38 
39  height: main.height
40 
41  StretchRow {
42  anchors {
43  leftMargin: Style.smallSpacing
44  fill: parent
45  rightMargin: Style.smallSpacing
46  }
47  spacing: Style.smallSpacing
48  // Label to display our To, Cc, Bcc types
49  Label {
50  id: type
51  anchors {
52  left: parent.left
53  top: parent.top
54  leftMargin: Style.smallSpacing
55  topMargin: Style.smallSpacing
56  }
57  text: MailUtils.recipientTypeToString(recipientType) + ":"
58  }
59  // We want the recipient flow to take up all available space so use a stretcher
60  Stretcher {
61  id: main
62  height: flow.height
63  implicitHeight: height
64  // Use a flow here to show the RecipientInfo for already added contacts
65  // We use the flow to position the RecipientInput by placing it as the last item
66  // in the flow and taking up all width. This gives consistent spacing as contacts
67  // are added as well as we only have to resize the parent to flows height and not
68  // flow height + input height. 1 binding less... yay! heh
69  Flow {
70  id: flow
71  anchors {
72  left: parent.left
73  leftMargin: Style.smallSpacing
74  top: parent.top
75  topMargin: recipientModel.count ? (Style.smallSpacing / 2) : Style.smallSpacing
76  right: parent.right
77  }
78  spacing: Style.smallSpacing / 2
79  // Display the already added recipients
80  Repeater {
81  id: repeater
82  delegate: RecipientDelegate {
83  composeMode: true
84  type: recipientType
85  }
86  }
87  Item {
88  width: parent.width
89  height: units.gu(4)
90  clip: true
92  id: input
93  anchors {
94  left: parent.left
95  top: parent.top
96  right: parent.right
97  }
98  placeholderText: qsTr("Enter an address")
99  height: parent.height
100  onActiveFocusChanged: {
101  if (!activeFocus && input.text && !isInvalidated) {
102  Log.logInfo("RecipientFIeld::focusChanged", "Lost focus adding recipient")
103  ComposerActions.addRecipientIfValid(recipientType, input.text)
104  }
105 
106  if (activeFocus && isInvalidated) {
107  isInvalidated = false
108  }
109  }
110 
111  // We don't want to let the return/enter key events
112  // propogate to the textarea as it just moves to a new line
113  // whereas we want it to validate & commit the address to the
114  // recipients model. So we catch and accept the event here.
115  Keys.onReturnPressed: {
116  event.accepted = true
117  Log.logInfo("RecipientField::returnPressed", "Adding recipient");
118  ComposerActions.addRecipientIfValid(recipientType, input.text)
119  }
120  Keys.onEnterPressed: {
121  event.accepted = true
122  Log.logInfo("RecipientField::enterPressed", "Adding recipient");
123  ComposerActions.addRecipientIfValid(recipientType, input.text)
124  }
125  // We also support completion tokens. and is triggered
126  // when an address ends with ';' or ','
127  onCompletionTokenSeen: {
128  Log.logInfo("RecipientField::CompletionTokenSeen", "Adding recipient");
129  ComposerActions.addRecipientIfValid(recipientType, input.text)
130  }
131  }
132  }
133  }
134  }
135  // Finally the input field context actions button
136  HeaderButton {
137  id: ctxt
138  anchors {
139  right: parent.right
140  top: parent.top
141  topMargin: -units.gu(0.5)
142  }
143  visible: !ComposerStore.showCC || !ComposerStore.showBCC
144  height: units.gu(5)
145  width: units.gu(4)
146  implicitWidth: width
147  implicitHeight: height
148  iconSize: units.gu(2.25)
149  iconColor: LomiriColors.ash
150  action: Action {
151  iconSource: Paths.actionIconUrl(Icons.ContextMenuIcon)
152  onTriggered: PopupUtils.open(Qt.resolvedUrl("./RecipientInputContextMenu.qml"),
153  ctxt,
154  {
155  recipientType: field.recipientType
156  })
157  }
158  }
159  }
160  // Divider
161  Line {
162  anchors {
163  left: parent.left
164  bottom: parent.bottom
165  right: parent.right
166  }
167  }
168 
169  AppScript {
170  runWhen: ComposerKeys.addRecipientIfValid
171  script: {
172  if (message.type === recipientType) {
173  ComposerActions.validateAddress(message.type, message.address)
174  } else {
175  exit.bind(this, 0)
176  return;
177  }
178  once(ComposerKeys.validAddress, function(message) {
179  Log.logInfo("RecipientField::validAddress", "Address is valid, adding to recipients")
180  isInvalidated = false
181  ComposerActions.addRecipientFromAddress(message.type, message.address)
182  input.text = ""
183  })
184  // Somethings not right. Show the invalid fields.
185  once(ComposerKeys.invalidAddress, function(message) {
186  Log.logInfo("RecipientField::invalidAddress", "Address invalid: %1".arg(message.address))
187  input.cursorPosition = input.length
188  isInvalidated = true
189  PopupActions.showError(PopupKeys.popupComposer, "Invalid email address: %1".arg(message.address))
190  })
191  }
192  }
193 }
Dekko::Lomiri::Constants::MailUtils
Definition: MailUtils.qml:22
main
Definition: main.qml:18
RecipientInput
Definition: RecipientInput.qml:21
Dekko
Definition: Dekko.qml:30
RecipientDelegate
Definition: RecipientDelegate.qml:23
Dekko::Lomiri::Components::Line
Definition: Line.qml:22
Dekko::Lomiri::Constants::Style
Definition: Style.qml:23
Dekko::Lomiri::Components::HeaderButton
Definition: HeaderButton.qml:23
Dekko::Lomiri::Constants::Style::smallSpacing
int smallSpacing
Definition: Style.qml:33