sailfish-safe

Sailfish frontend for safe(1)
git clone git://git.z3bra.org/sailfish-safe.git
Log | Files | Refs | README | LICENSE

commit 6e1bec6bceed73deff7ee3f482ee54c32d549422
parent b4631ca0b35322a6ed2d170491e8c39a227c487a
Author: Daniel Vrátil <dvratil@kde.org>
Date:   Thu, 14 Feb 2019 21:13:38 +0100

Implement Search

Diffstat:
Mharbour-passilic.pro | 2++
Mqml/components/GlobalPullDownMenu.qml | 4++++
Aqml/components/PasswordDelegate.qml | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mqml/harbour-passilic.qml | 8++++++++
Mqml/pages/PasswordListPage.qml | 105+++----------------------------------------------------------------------------
Aqml/pages/SearchPage.qml | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/passwordfiltermodel.cpp | 3++-
Mtranslations/harbour-passilic-zh_cn.ts | 15+++++++++++----
Mtranslations/harbour-passilic.ts | 15+++++++++++----
9 files changed, 214 insertions(+), 110 deletions(-)

diff --git a/harbour-passilic.pro b/harbour-passilic.pro @@ -47,6 +47,8 @@ DISTFILES += \ rpm/harbour-passilic.yaml \ translations/*.ts \ harbour-passilic.desktop \ + qml/pages/SearchPage.qml \ + qml/components/PasswordDelegate.qml OTHER_FILES += \ README.md diff --git a/qml/components/GlobalPullDownMenu.qml b/qml/components/GlobalPullDownMenu.qml @@ -24,4 +24,8 @@ PullDownMenu { text: qsTr("About") onClicked: app.pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml")) } + MenuItem { + text: qsTr("Search") + onClicked: app.pageStack.push(searchPage) + } } diff --git a/qml/components/PasswordDelegate.qml b/qml/components/PasswordDelegate.qml @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 Daniel Vrátil <dvratil@kde.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import QtQuick 2.2 +import QtQml.Models 2.2 +import Sailfish.Silica 1.0 +import harbour.passilic 1.0 + +ListItem { + id: listItem + + property var modelData + property var password : null + + signal folderSelected() + + contentHeight: password === null ? Theme.itemSizeSmall : Theme.itemSizeLarge + + Row { + anchors { + fill: parent + leftMargin: Theme.horizontalPageMargin + rightMargin: Theme.horizontalPageMargin + verticalCenter: parent.verticalCenter + topMargin: Theme.paddingMedium + } + + Column { + spacing: Theme.paddingSmall + width: parent.width + + Row { + spacing: Theme.paddingMedium + + Image { + anchors.verticalCenter: parent.verticalCenter + source: "image://theme/" + + ((modelData.type === PasswordsModel.FolderEntry) ? "icon-m-folder" : "icon-m-device-lock") + + "?" + + (listItem.highlighted ? Theme.highlightColor : Theme.primaryColor) + width: Theme.iconSizeSmall + height: width + } + + Label { + id: label + text: modelData.name + } + } + + Row { + visible: password !== null + width: parent.width + + Label { + id: errorLabel + + visible: password !== null && password.hasError + + text: password ? password.error : "" + font.pixelSize: Theme.fontSizeTiny + } + + Label { + id: okLabel + + visible: password !== null && password.valid + + text: qsTr("Password copied to clipboard") + font.pixelSize: Theme.fontSizeTiny + } + } + } + } + + RemorseItem { + id: remorse + + cancelText: qsTr("Expire password") + + // HACK: override RemorseItem._execute() to act as cancel when the timer expires + function _execute(closeAfterExecute) { + cancel() + } + + onCanceled: { + if (listItem.password) { + listItem.password.expirePassword(); + } + } + } + + onClicked: { + if (modelData.type === PasswordsModel.FolderEntry) { + listItem.folderSelected() + } else { + modelData.password.requestPassword() + var dialog = pageStack.push(Qt.resolvedUrl("../pages/PassphraseRequester.qml"), + { "requester": modelData.password }) + dialog.done.connect(function() { + listItem.password = modelData.password + listItem.password.validChanged.connect(function() { + if (listItem.password.valid) { + remorse.execute(listItem, qsTr("Password will expire"), + function() { + if (listItem.password) { + listItem.password.expirePassword(); + } + }, listItem.password.defaultTimeout); + } + }); + }); + } + } +} diff --git a/qml/harbour-passilic.qml b/qml/harbour-passilic.qml @@ -47,6 +47,14 @@ ApplicationWindow } Component { + id: searchPage + + SearchPage { + model: filterModel + } + } + + Component { id: passwordsPage PasswordListPage { diff --git a/qml/pages/PasswordListPage.qml b/qml/pages/PasswordListPage.qml @@ -51,108 +51,11 @@ Page { rootIndex: passwordListPage.rootIndex - delegate: ListItem { - id: listItem + delegate: PasswordDelegate { + modelData: model - property var password : null - - contentHeight: password === null ? Theme.itemSizeSmall : Theme.itemSizeLarge - - Row { - anchors { - fill: parent - leftMargin: Theme.horizontalPageMargin - rightMargin: Theme.horizontalPageMargin - verticalCenter: parent.verticalCenter - topMargin: Theme.paddingMedium - } - - Column { - spacing: Theme.paddingSmall - width: parent.width - - Row { - spacing: Theme.paddingMedium - - Image { - anchors.verticalCenter: parent.verticalCenter - source: "image://theme/" - + ((model.type === PasswordsModel.FolderEntry) ? "icon-m-folder" : "icon-m-device-lock") - + "?" - + (listItem.highlighted ? Theme.highlightColor : Theme.primaryColor) - width: Theme.iconSizeSmall - height: width - } - - Label { - id: label - text: model.name - } - } - - Row { - visible: password !== null - width: parent.width - - Label { - id: errorLabel - - visible: password !== null && password.hasError - - text: password ? password.error : "" - font.pixelSize: Theme.fontSizeTiny - } - - Label { - id: okLabel - - visible: password !== null && password.valid - - text: qsTr("Password copied to clipboard") - font.pixelSize: Theme.fontSizeTiny - } - } - } - } - - RemorseItem { - id: remorse - - cancelText: qsTr("Expire password") - - // HACK: override RemorseItem._execute() to act as cancel when the timer expires - function _execute(closeAfterExecute) { - cancel() - } - - onCanceled: { - if (listItem.password) { - listItem.password.expirePassword(); - } - } - } - - onClicked: { - if (model.type === PasswordsModel.FolderEntry) { - passwordListPage.folderSelected(delegateModel.modelIndex(index), model.name); - } else { - model.password.requestPassword() - var dialog = pageStack.push(Qt.resolvedUrl("PassphraseRequester.qml"), - { "requester": model.password }) - dialog.done.connect(function() { - listItem.password = model.password - listItem.password.validChanged.connect(function() { - if (listItem.password.valid) { - remorse.execute(listItem, qsTr("Password will expire"), - function() { - if (listItem.password) { - listItem.password.expirePassword(); - } - }, listItem.password.defaultTimeout); - } - }); - }); - } + onFolderSelected: { + passwordListPage.folderSelected(delegateModel.modelIndex(index), model.name); } } } diff --git a/qml/pages/SearchPage.qml b/qml/pages/SearchPage.qml @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Daniel Vrátil <dvratil@kde.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import QtQuick 2.2 +import QtQml.Models 2.2 +import Sailfish.Silica 1.0 +import "../components" + +Page { + id: searchPage + + property alias model: listView.model + + SilicaListView { + id: listView + anchors.fill: parent + + header: SearchField { + id: searchField + width: parent.width + focus: true + onTextChanged: model.filter = text + } + + delegate: PasswordDelegate { + modelData: model + } + } +} diff --git a/src/passwordfiltermodel.cpp b/src/passwordfiltermodel.cpp @@ -79,7 +79,8 @@ bool PasswordFilterModel::filterAcceptsRow(int source_row, const QModelIndex &so } if (mFilter.isEmpty()) { - return true; + // nothing matches an empty filter + return false; } const auto path = sourceModel()->data(src_index, PasswordsModel::FullNameRole).toString(); diff --git a/translations/harbour-passilic-zh_cn.ts b/translations/harbour-passilic-zh_cn.ts @@ -34,13 +34,13 @@ <source>About</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>PasswordListPage</name> <message> - <source>Passilic</source> + <source>Search</source> <translation type="unfinished"></translation> </message> +</context> +<context> + <name>PasswordDelegate</name> <message> <source>Password copied to clipboard</source> <translation type="unfinished"></translation> @@ -55,6 +55,13 @@ </message> </context> <context> + <name>PasswordListPage</name> + <message> + <source>Passilic</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>PasswordProvider</name> <message> <source>Failed to decrypt password: GPG is not available</source> diff --git a/translations/harbour-passilic.ts b/translations/harbour-passilic.ts @@ -34,13 +34,13 @@ <source>About</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>PasswordListPage</name> <message> - <source>Passilic</source> + <source>Search</source> <translation type="unfinished"></translation> </message> +</context> +<context> + <name>PasswordDelegate</name> <message> <source>Password copied to clipboard</source> <translation type="unfinished"></translation> @@ -55,6 +55,13 @@ </message> </context> <context> + <name>PasswordListPage</name> + <message> + <source>Passilic</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>PasswordProvider</name> <message> <source>Failed to decrypt password: GPG is not available</source>