Apply a dumb filter on contacts

This commit is contained in:
Sebastian Wolf 2020-11-24 17:28:41 +01:00
parent 86599b2859
commit 4713fbfba6
14 changed files with 300 additions and 192 deletions

View file

@ -36,6 +36,11 @@ Page {
} }
} }
function resetFocus() {
contactsSearchField.focus = false;
newChatPage.focus = true;
}
SilicaFlickable { SilicaFlickable {
id: newChatContainer id: newChatContainer
contentHeight: newChatPage.height contentHeight: newChatPage.height
@ -57,243 +62,272 @@ Page {
width: newChatPageColumn.width width: newChatPageColumn.width
height: newChatPageColumn.height - newChatPageHeader.height height: newChatPageColumn.height - newChatPageHeader.height
SilicaListView { Column {
id: contactsListView visible: !newChatPage.isLoading
clip: true
width: parent.width width: parent.width
height: parent.height height: parent.height
visible: !newChatPage.isLoading SearchField {
opacity: visible ? 1 : 0 id: contactsSearchField
Behavior on opacity { FadeAnimation {} }
signal newChatInitiated ( int currentIndex )
ViewPlaceholder {
y: Theme.paddingLarge
enabled: contactsListView.count === 0
text: qsTr("You don't have any contacts.")
}
delegate: Item {
id: newChatListItem
width: parent.width width: parent.width
height: contactListItem.height placeholderText: qsTr("Search a contact")
active: !newChatPage.isLoading
PhotoTextsListItem { onTextChanged: contactsModel.applyFilter(text);
id: contactListItem EnterKey.iconSource: "image://theme/icon-m-enter-close"
EnterKey.onClicked: {
opacity: visible ? 1 : 0 resetFocus();
Behavior on opacity { FadeAnimation {} }
pictureThumbnail {
photoData: (typeof display.profile_photo !== "undefined") ? display.profile_photo.small : {}
}
width: parent.width
primaryText.text: Emoji.emojify(Functions.getUserName(display), primaryText.font.pixelSize, "../js/emoji/")
prologSecondaryText.text: "@" + ( display.username !== "" ? display.username : display.id )
tertiaryText {
maximumLineCount: 1
text: Functions.getChatPartnerStatusText(display.status["@type"], display.status.was_online);
}
onClicked: {
contactsListView.newChatInitiated(index);
}
Connections {
target: contactsListView
onNewChatInitiated: {
if (index === currentIndex) {
contactListItem.visible = false;
} else {
contactListItem.visible = true;
}
}
}
} }
Column { }
id: selectChatTypeColumn
visible: !contactListItem.visible SilicaListView {
opacity: visible ? 1 : 0 id: contactsListView
Behavior on opacity { FadeAnimation {} } clip: true
width: parent.width
height: parent.height - contactsSearchField.height
visible: !newChatPage.isLoading
opacity: visible ? 1 : 0
Behavior on opacity { FadeAnimation {} }
signal newChatInitiated ( int currentIndex )
ViewPlaceholder {
y: Theme.paddingLarge
enabled: contactsListView.count === 0
text: qsTr("You don't have any contacts.")
}
delegate: Item {
id: newChatListItem
width: parent.width width: parent.width
height: contactListItem.height height: contactListItem.height
Item { PhotoTextsListItem {
width: parent.width id: contactListItem
height: parent.height - chatTypeSeparator.height
Rectangle { opacity: visible ? 1 : 0
anchors.fill: parent Behavior on opacity { FadeAnimation {} }
opacity: 0.3
color: Theme.overlayBackgroundColor pictureThumbnail {
photoData: (typeof display.profile_photo !== "undefined") ? display.profile_photo.small : {}
}
width: parent.width
primaryText.text: Emoji.emojify(Functions.getUserName(display), primaryText.font.pixelSize, "../js/emoji/")
prologSecondaryText.text: "@" + ( display.username !== "" ? display.username : display.id )
tertiaryText {
maximumLineCount: 1
text: Functions.getChatPartnerStatusText(display.status["@type"], display.status.was_online);
} }
onClicked: {
contactsListView.newChatInitiated(index);
}
Connections {
target: contactsListView
onNewChatInitiated: {
if (index === currentIndex) {
contactListItem.visible = false;
} else {
contactListItem.visible = true;
}
}
}
Connections {
target: contactsSearchField
onFocusChanged: {
if (contactsSearchField.focus) {
contactListItem.visible = true;
}
}
}
}
Column {
id: selectChatTypeColumn
visible: !contactListItem.visible
opacity: visible ? 1 : 0
Behavior on opacity { FadeAnimation {} }
width: parent.width
height: contactListItem.height
Item { Item {
id: privateChatItem width: parent.width
height: parent.height height: parent.height - chatTypeSeparator.height
width: parent.width / 2 + ( Theme.horizontalPageMargin / 2 )
anchors.left: parent.left
anchors.top: parent.top
Rectangle { Rectangle {
id: privateChatHighlightBackground
anchors.fill: parent anchors.fill: parent
color: Theme.highlightBackgroundColor opacity: 0.3
opacity: 0.5 color: Theme.overlayBackgroundColor
visible: false
} }
Row { Item {
width: parent.width id: privateChatItem
height: parent.height - ( 2 * Theme.paddingSmall ) height: parent.height
anchors.verticalCenter: parent.verticalCenter width: parent.width / 2 + ( Theme.horizontalPageMargin / 2 )
anchors.left: parent.left
anchors.top: parent.top
IconButton { Rectangle {
id: privateChatButton id: privateChatHighlightBackground
width: Theme.itemSizeLarge anchors.fill: parent
height: Theme.itemSizeLarge color: Theme.highlightBackgroundColor
icon.source: "image://theme/icon-m-chat" opacity: 0.5
visible: false
}
Row {
width: parent.width
height: parent.height - ( 2 * Theme.paddingSmall )
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
IconButton {
id: privateChatButton
width: Theme.itemSizeLarge
height: Theme.itemSizeLarge
icon.source: "image://theme/icon-m-chat"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
tdLibWrapper.createPrivateChat(display.id);
}
}
Column {
height: parent.height
width: parent.width - privateChatButton.width - Theme.horizontalPageMargin
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.paddingSmall
Text {
id: privateChatHeader
width: parent.width
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.ExtraBold
color: Theme.primaryColor
maximumLineCount: 1
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("Private Chat")
}
Text {
width: parent.width
height: parent.height - privateChatHeader.height - Theme.paddingSmall
font.pixelSize: Theme.fontSizeTiny
color: Theme.secondaryColor
wrapMode: Text.Wrap
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("Transport-encrypted, stored in Telegram Cloud, sharable across devices")
}
}
}
MouseArea {
anchors.fill: parent
onClicked: { onClicked: {
tdLibWrapper.createPrivateChat(display.id); tdLibWrapper.createPrivateChat(display.id);
} }
} onPressed: {
privateChatHighlightBackground.visible = true;
Column {
height: parent.height
width: parent.width - privateChatButton.width - Theme.horizontalPageMargin
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.paddingSmall
Text {
id: privateChatHeader
width: parent.width
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.ExtraBold
color: Theme.primaryColor
maximumLineCount: 1
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("Private Chat")
} }
Text { onReleased: {
width: parent.width privateChatHighlightBackground.visible = false;
height: parent.height - privateChatHeader.height - Theme.paddingSmall
font.pixelSize: Theme.fontSizeTiny
color: Theme.secondaryColor
wrapMode: Text.Wrap
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("Transport-encrypted, stored in Telegram Cloud, sharable across devices")
} }
} }
} }
MouseArea { Item {
anchors.fill: parent id: secretChatItem
onClicked: { height: parent.height
tdLibWrapper.createPrivateChat(display.id); width: parent.width / 2 + ( Theme.horizontalPageMargin / 2 )
anchors.left: privateChatItem.right
anchors.top: parent.top
Rectangle {
id: secretChatHighlightBackground
anchors.fill: parent
color: Theme.highlightBackgroundColor
opacity: 0.5
visible: false
} }
onPressed: {
privateChatHighlightBackground.visible = true;
}
onReleased: {
privateChatHighlightBackground.visible = false;
}
}
}
Item { Row {
id: secretChatItem width: parent.width
height: parent.height height: parent.height - ( 2 * Theme.paddingSmall )
width: parent.width / 2 + ( Theme.horizontalPageMargin / 2 )
anchors.left: privateChatItem.right
anchors.top: parent.top
Rectangle {
id: secretChatHighlightBackground
anchors.fill: parent
color: Theme.highlightBackgroundColor
opacity: 0.5
visible: false
}
Row {
width: parent.width
height: parent.height - ( 2 * Theme.paddingSmall )
anchors.verticalCenter: parent.verticalCenter
IconButton {
id: secretChatButton
width: Theme.itemSizeLarge
height: Theme.itemSizeLarge
icon.source: "image://theme/icon-m-device-lock"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
IconButton {
id: secretChatButton
width: Theme.itemSizeLarge
height: Theme.itemSizeLarge
icon.source: "image://theme/icon-m-device-lock"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
console.log("SECRET CHAT!");
}
}
Column {
height: parent.height
width: parent.width - secretChatButton.width - Theme.horizontalPageMargin
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.paddingSmall
Text {
width: parent.width
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.ExtraBold
color: Theme.primaryColor
maximumLineCount: 1
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("Secret Chat")
}
Text {
width: parent.width
height: parent.height - privateChatHeader.height - Theme.paddingSmall
font.pixelSize: Theme.fontSizeTiny
color: Theme.secondaryColor
wrapMode: Text.Wrap
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("End-to-end-encrypted, accessible on this device only")
}
}
}
MouseArea {
anchors.fill: parent
onClicked: { onClicked: {
console.log("SECRET CHAT!"); console.log("SECRET CHAT!");
} }
} onPressed: {
secretChatHighlightBackground.visible = true;
Column {
height: parent.height
width: parent.width - secretChatButton.width - Theme.horizontalPageMargin
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.paddingSmall
Text {
width: parent.width
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.ExtraBold
color: Theme.primaryColor
maximumLineCount: 1
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("Secret Chat")
} }
Text { onReleased: {
width: parent.width secretChatHighlightBackground.visible = false;
height: parent.height - privateChatHeader.height - Theme.paddingSmall
font.pixelSize: Theme.fontSizeTiny
color: Theme.secondaryColor
wrapMode: Text.Wrap
elide: Text.ElideRight
textFormat: Text.StyledText
text: qsTr("End-to-end-encrypted, accessible on this device only")
} }
} }
} }
MouseArea {
anchors.fill: parent
onClicked: {
console.log("SECRET CHAT!");
}
onPressed: {
secretChatHighlightBackground.visible = true;
}
onReleased: {
secretChatHighlightBackground.visible = false;
}
}
} }
} Separator {
id: chatTypeSeparator
width: parent.width
color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter
}
Separator {
id: chatTypeSeparator
width: parent.width
color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter
} }
} }
VerticalScrollDecorator {}
} }
VerticalScrollDecorator {}
} }
Column { Column {

View file

@ -44,13 +44,13 @@ ContactsModel::ContactsModel(TDLibWrapper *tdLibWrapper, QObject *parent)
int ContactsModel::rowCount(const QModelIndex &) const int ContactsModel::rowCount(const QModelIndex &) const
{ {
return this->contacts.size(); return this->filter.isEmpty() ? this->contacts.size() : this->filteredContacts.size();
} }
QVariant ContactsModel::data(const QModelIndex &index, int role) const QVariant ContactsModel::data(const QModelIndex &index, int role) const
{ {
if (index.isValid() && role == Qt::DisplayRole) { if (index.isValid() && role == Qt::DisplayRole) {
return QVariant(contacts.value(index.row())); return this->filter.isEmpty() ? QVariant(contacts.value(index.row())) : QVariant(filteredContacts.value(index.row())) ;
} }
return QVariant(); return QVariant();
} }
@ -116,3 +116,30 @@ void ContactsModel::hydrateContacts()
LOG("Hydrated contacts:" << this->contacts.size()); LOG("Hydrated contacts:" << this->contacts.size());
std::sort(this->contacts.begin(), this->contacts.end(), compareUsers); std::sort(this->contacts.begin(), this->contacts.end(), compareUsers);
} }
void ContactsModel::applyFilter(const QString &filter)
{
LOG("Applying filter:" << filter);
beginResetModel();
this->filter = filter;
this->filteredContacts.clear();
if (!this->filter.isEmpty()) {
QListIterator<QVariant> contactIterator(this->contacts);
while (contactIterator.hasNext()) {
QVariantMap contact = contactIterator.next().toMap();
if (contact.value(LAST_NAME).toString().contains(this->filter, Qt::CaseInsensitive)) {
this->filteredContacts.append(contact);
continue;
}
if (contact.value(FIRST_NAME).toString().contains(this->filter, Qt::CaseInsensitive)) {
this->filteredContacts.append(contact);
continue;
}
if (contact.value(USERNAME).toString().contains(this->filter, Qt::CaseInsensitive)) {
this->filteredContacts.append(contact);
continue;
}
}
}
endResetModel();
}

View file

@ -35,6 +35,7 @@ public:
virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant data(const QModelIndex &index, int role) const;
Q_INVOKABLE void hydrateContacts(); Q_INVOKABLE void hydrateContacts();
Q_INVOKABLE void applyFilter(const QString &filter);
public slots: public slots:
void handleUsersReceived(const QString &extra, const QVariantList &userIds, int totalUsers); void handleUsersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
@ -42,7 +43,9 @@ public slots:
private: private:
TDLibWrapper *tdLibWrapper; TDLibWrapper *tdLibWrapper;
QVariantList contacts; QVariantList contacts;
QVariantList filteredContacts;
QList<QString> contactIds; QList<QString> contactIds;
QString filter;
}; };
#endif // CONTACTSMODEL_H #endif // CONTACTSMODEL_H

View file

@ -920,6 +920,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -920,6 +920,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -910,6 +910,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -921,6 +921,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -910,6 +910,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -920,6 +920,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -930,6 +930,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -930,6 +930,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -920,6 +920,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -910,6 +910,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -920,6 +920,10 @@
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Search a contact</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>