Warning, /utilities/keysmith/src/contents/ui/AccountsOverview.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-License-Identifier: GPL-3.0-or-later
0003  * SPDX-FileCopyrightText: 2020-2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
0004  * SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
0005  */
0006 
0007 import QtQuick
0008 import QtQuick.Layouts
0009 import org.kde.kirigami as Kirigami
0010 
0011 import Keysmith.Application as Application
0012 import Keysmith.Models as Models
0013 
0014 Kirigami.ScrollablePage {
0015     id: root
0016     /*
0017      * Explicitly opt-out of scroll-to-refresh/drag-to-refresh behaviour
0018      * Underlying model implementations don't offer the hooks for that.
0019      */
0020     supportsRefreshing: false
0021     title: i18nc("@title:window", "Accounts")
0022 
0023     property Application.AccountsOverviewViewModel vm
0024 
0025     property string accountErrorMessage: i18nc("generic error shown when adding or updating an account failed", "Failed to update accounts")
0026     property string loadingErrorMessage: i18nc("error message shown when loading accounts from storage failed", "Some accounts failed to load.")
0027     property string errorMessage: loadingErrorMessage
0028 
0029     header: ColumnLayout {
0030         id: column
0031         Layout.margins: 0
0032         spacing: 0
0033         Kirigami.InlineMessage {
0034             id: message
0035             visible: vm.accounts.error // FIXME : should be managed via vm
0036             type: Kirigami.MessageType.Error
0037             text: root.errorMessage // FIXME : should be managed via vm
0038             Layout.fillWidth: true
0039             Layout.margins: Kirigami.Units.smallSpacing
0040             /*
0041              * There is supposed to be a more Kirigami-way to allow the user to dismiss the error message: showCloseButton
0042              * Unfortunately:
0043              *
0044              *  - Kirigami doesn't really offer a direct API for detecting when the close button is clicked.
0045              *    Observing the close button's effect via the visible property works just as well, but it is a bit of a hack.
0046              *  - It results in a rather unattractive vertical sizing problem: the close button is rather big for inline text
0047              *    This makes the internal horizontal spacing look completely out of proportion with the vertical spacing.
0048              *  - The actual click/tap target is only a small fraction of the entire message (banner).
0049              *    In this case, making the entire message click/tap target would be much better.
0050              *
0051              * Solution: add a MouseArea for dismissing the message via click/tap.
0052              */
0053             MouseArea {
0054                 anchors.fill: parent
0055                 onClicked: {
0056                     // FIXME : should be managed via vm
0057                     vm.accounts.error = false;
0058                 }
0059             }
0060         }
0061         Kirigami.Separator {
0062             Layout.fillWidth: true
0063             visible: vm.accounts.error // FIXME : should be managed via vm
0064         }
0065     }
0066 
0067     Component {
0068         id: hotpDelegate
0069         HOTPAccountEntryView {
0070             account: value
0071             onActionTriggered: {
0072                 // FIXME : should be managed via vm
0073                 vm.accounts.error = false;
0074                 root.errorMessage = root.accountErrorMessage;
0075             }
0076         }
0077     }
0078 
0079     Component {
0080         id: totpDelegate
0081         TOTPAccountEntryView {
0082             account: value
0083             onActionTriggered: {
0084                 // FIXME : should be managed via vm
0085                 vm.accounts.error = false;
0086                 root.errorMessage = root.accountErrorMessage;
0087             }
0088         }
0089     }
0090 
0091     ListView {
0092         id: mainList
0093         model: Models.SortedAccountListModel {
0094             sourceModel: vm.accounts
0095         }
0096 
0097         Kirigami.PlaceholderMessage {
0098             anchors.centerIn: parent
0099             width: parent.width - (Kirigami.Units.largeSpacing * 4)
0100             visible: vm.accounts.loaded && mainList.count == 0
0101             text: i18n("No accounts added")
0102             icon.name: "unlock"
0103 
0104             helpfulAction: Kirigami.Action {
0105                 icon.name: "list-add"
0106                 text: i18nc("@action:button add new account, shown instead of overview list when no accounts have been added yet", "Add Account")
0107                 onTriggered: {
0108                     // FIXME : should be managed via vm
0109                     vm.accounts.error = false;
0110                     root.errorMessage = root.accountErrorMessage;
0111                     vm.addNewAccount();
0112                 }
0113             }
0114         }
0115 
0116         /*
0117          * Use a Loader to get a switch-like statement to select an
0118          * appropriate delegate based on properties of the account model.
0119          */
0120         delegate: Loader {
0121             /*
0122              * Fix up width manually.
0123              * It doesn't seem to be propagated correctly by itself otherwise.
0124              */
0125             width: mainList.width
0126 
0127             /*
0128              * The `model` and related properties from the ListView delegate
0129              * context will not survive into the actual delegate components.
0130              * However Loader will also inject properties in *its* context
0131              * which will be observed by those delegate components.
0132              *
0133              * Fill the Loader's context with properties from the model passed
0134              * by ListView, to make these values propagate into delegates.
0135              *
0136              * See also: https://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate
0137              */
0138             property Models.Account value: model.account
0139             property int index: model.index
0140 
0141             sourceComponent: {
0142                 /*
0143                  * Guard against a broken account model.
0144                  * Will simply render nothing at all in that case.
0145                  */
0146                 if (!value) {
0147                     // TODO warn about this
0148                     return null;
0149                 }
0150                 if (value.isHotp) {
0151                     return hotpDelegate;
0152                 }
0153                 if (value.isTotp) {
0154                     return totpDelegate;
0155                 }
0156 
0157                 // TODO warn about this
0158                 return null;
0159             }
0160         }
0161         section {
0162             property: "account.issuer"
0163             delegate: Kirigami.ListSectionHeader {
0164                 width: parent.width
0165                 text: section
0166             }
0167         }
0168     }
0169 
0170     actions: Kirigami.Action {
0171         id: addAction
0172         text: i18nc("@action:button add new account, shown in toolbar", "Add")
0173         icon.name: "list-add"
0174         enabled: vm.actionsEnabled
0175         visible: vm.actionsEnabled
0176         onTriggered: {
0177             // FIXME : should be managed via vm
0178             vm.accounts.error = false;
0179             root.errorMessage = root.accountErrorMessage;
0180             vm.addNewAccount();
0181         }
0182     }
0183 }