mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-02 05:50:34 +03:00
363 lines
11 KiB
QML
363 lines
11 KiB
QML
import QtQuick 2.9
|
|
import QtQuick.Controls 2.2
|
|
import QtQuick.Layouts 1.3
|
|
import QtQuick.Controls.Material 2.2
|
|
import Qt.labs.settings 1.0
|
|
import Qt.labs.platform 1.1
|
|
import QtQuick.Window 2.2
|
|
import QtQml 2.12
|
|
|
|
ApplicationWindow {
|
|
|
|
id: app
|
|
|
|
width: 300
|
|
height: 502
|
|
minimumWidth: 300
|
|
minimumHeight: 196
|
|
visible: false
|
|
|
|
flags: Qt.Window | Qt.WindowFullscreenButtonHint | Qt.WindowTitleHint
|
|
| Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint
|
|
| Qt.WindowCloseButtonHint | Qt.WindowFullscreenButtonHint
|
|
|
|
Accessible.ignored: true
|
|
|
|
readonly property string yubicoGreen: "#9aca3c"
|
|
readonly property string yubicoWhite: "#ffffff"
|
|
readonly property string yubicoRed: Material.color(Material.Red, Material.Shade600)
|
|
|
|
property string primaryColor: isDark() ? "#ffffff" : "#303030"
|
|
|
|
readonly property string defaultBackground: isDark() ? "#303030" : "#f7f8f9"
|
|
readonly property string defaultElevated: isDark() ? "#383838" : "#ffffff"
|
|
readonly property string defaultImageOverlay: isDark() ? "#565656" : "#dddddd"
|
|
readonly property string defaultForeground: isDark() ? "#fafafa" : "#565656"
|
|
|
|
property string formUnderline: isDark() ? "#737373" : "#d8d8d8"
|
|
property string formText: isDark() ? "#f0f0f0" : "#606060"
|
|
property string formPlaceholderText: isDark() ? "#808080" : "#b0b0b0"
|
|
property string formImageOverlay: isDark() ? "#d8d8d8" : "#727272"
|
|
property string formStepBackground: isDark() ? "#565656" : "#bbbbbb"
|
|
property string formHighlightItem: isDark() ? "#4a4a4a" : "#e9e9e9"
|
|
|
|
property string toolTipForeground: app.isDark() ? "#fafafa" : "#fbfbfb"
|
|
property string toolTipBackground: app.isDark() ? "#4a4a4a" : "#7f7f7f"
|
|
|
|
property var fullEmphasis: 1.0
|
|
property var highEmphasis: 0.87
|
|
property var lowEmphasis: 0.60
|
|
property var disabledEmphasis: 0.38
|
|
|
|
property var cardSelectedEmphasis: 0.08
|
|
property var cardHoveredEmphasis: 0.05
|
|
property var cardNormalEmphasis: 0.03
|
|
|
|
property var currentCredentialCard
|
|
property string iconFavorite: "#f7bd0c"
|
|
|
|
Material.theme: settings.theme
|
|
Material.primary: yubicoGreen
|
|
Material.accent: yubicoGreen
|
|
Material.foreground: defaultForeground
|
|
Material.background: defaultBackground
|
|
|
|
header: StyledToolBar {
|
|
id: toolBar
|
|
}
|
|
|
|
// Don't refresh credentials when window is minimized or hidden
|
|
// See http://doc.qt.io/qt-5/qwindow.html#Visibility-enum
|
|
property bool isInForeground: visibility != 3 && visibility != 0
|
|
onIsInForegroundChanged: {
|
|
(poller.running = isInForeground || settings.closeToTray)
|
|
}
|
|
Component.onCompleted: {
|
|
updateTrayVisibility()
|
|
ensureMinimumWindowSize()
|
|
ensureValidWindowPosition()
|
|
app.visible = !(settings.closeToTray && settings.hideOnLaunch)
|
|
}
|
|
|
|
Component.onDestruction: saveScreenLayout()
|
|
|
|
FontLoader {
|
|
id: robotoRegular;
|
|
source: "../fonts/Roboto-Regular.ttf"
|
|
}
|
|
|
|
FontLoader {
|
|
id: robotoBold;
|
|
source: "../fonts/Roboto-Bold.ttf"
|
|
}
|
|
|
|
FontLoader {
|
|
id: robotoItalic;
|
|
source: "../fonts/Roboto-Italic.ttf"
|
|
}
|
|
|
|
FontLoader {
|
|
id: robotoMedium;
|
|
source: "../fonts/Roboto-Medium.ttf"
|
|
}
|
|
|
|
FontLoader {
|
|
id: robotoLight;
|
|
source: "../fonts/Roboto-Light.ttf"
|
|
}
|
|
|
|
font.family: robotoRegular.name
|
|
|
|
function enableLogging(logLevel) {
|
|
yubiKey.enableLogging(logLevel, null)
|
|
}
|
|
function enableLoggingToFile(logLevel, logFile) {
|
|
yubiKey.enableLogging(logLevel, logFile)
|
|
}
|
|
function disableLogging() {
|
|
yubiKey.disableLogging()
|
|
}
|
|
|
|
function isDark() {
|
|
return app.Material.theme === Material.Dark
|
|
}
|
|
|
|
function saveScreenLayout() {
|
|
settings.desktopAvailableWidth = Screen.desktopAvailableWidth
|
|
settings.desktopAvailableHeight = Screen.desktopAvailableHeight
|
|
}
|
|
|
|
function ensureMinimumWindowSize() {
|
|
app.width = width < minimumWidth ? minimumWidth : width
|
|
app.height = height < minimumHeight ? minimumHeight : height
|
|
}
|
|
|
|
function ensureValidWindowPosition() {
|
|
// If we have the same desktop dimensions as last time, use the saved position.
|
|
// If not, put the window in the middle of the screen.
|
|
var savedScreenLayout = (settings.desktopAvailableWidth === Screen.desktopAvailableWidth)
|
|
&& (settings.desktopAvailableHeight === Screen.desktopAvailableHeight)
|
|
app.x = (savedScreenLayout) ? settings.x : Screen.width / 2 - app.width / 2
|
|
app.y = (savedScreenLayout) ? settings.y : Screen.height / 2 - app.height / 2
|
|
|
|
function isWindowPositionInsideSomeMonitor() {
|
|
for (var i = 0; i < monitorAreas.length; i++) {
|
|
var xMin = monitorAreas[i].xMin
|
|
var xMax = monitorAreas[i].xMax
|
|
var yMin = monitorAreas[i].yMin
|
|
var yMax = monitorAreas[i].yMax
|
|
|
|
if (app.x > xMin && app.x < xMax) {
|
|
if (app.y > yMin && app.y < yMax) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// If app.x and app.y are outside of the available screen geometry,
|
|
// put the app in the middle of the screen.
|
|
if (!isWindowPositionInsideSomeMonitor()) {
|
|
app.x = Screen.width / 2 - app.width / 2
|
|
app.y = Screen.height / 2 - app.height / 2
|
|
}
|
|
|
|
}
|
|
|
|
function updateTrayVisibility() {
|
|
// When the tray option is enabled, closing the last window
|
|
// doesn't actually close the application.
|
|
application.quitOnLastWindowClosed = !settings.closeToTray
|
|
}
|
|
|
|
function calculateFavorite(credential, text) {
|
|
if (credential && credential.touch) {
|
|
sysTrayIcon.showMessage(
|
|
qsTr("Touch required"),
|
|
qsTr("Touch your YubiKey now to generate security code."),
|
|
SystemTrayIcon.NoIcon)
|
|
}
|
|
if (settings.otpMode) {
|
|
yubiKey.otpCalculate(credential, function (resp) {
|
|
if (resp.success) {
|
|
clipBoard.push(resp.code.value)
|
|
sysTrayIcon.showMessage(
|
|
qsTr("Copied to clipboard"),
|
|
"The code for " + text + " is now in the clipboard.",
|
|
SystemTrayIcon.NoIcon)
|
|
} else {
|
|
sysTrayIcon.showMessage(
|
|
"Error",
|
|
"calculate failed: " + resp.error_id,
|
|
SystemTrayIcon.NoIcon)
|
|
console.log("calculate failed:", resp.error_id)
|
|
}
|
|
})
|
|
} else {
|
|
yubiKey.calculate(credential, function (resp) {
|
|
if (resp.success) {
|
|
clipBoard.push(resp.code.value)
|
|
sysTrayIcon.showMessage(
|
|
qsTr("Copied to clipboard"),
|
|
"The code for " + text + " is now in the clipboard.",
|
|
SystemTrayIcon.NoIcon)
|
|
} else {
|
|
sysTrayIcon.showMessage(
|
|
qsTr("Error"),
|
|
"calculate failed: " + resp.error_id,
|
|
SystemTrayIcon.NoIcon)
|
|
console.log("calculate failed:", resp.error_id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
function getFavoriteEntries() {
|
|
var favs = entriesComponent.createObject(app, {
|
|
})
|
|
for (var i = 0; i < entries.count; i++) {
|
|
var entry = entries.get(i)
|
|
if (!!entry.credential && settings.favorites.includes(entry.credential.key)) {
|
|
favs.append(entry)
|
|
}
|
|
}
|
|
return favs
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Copy
|
|
enabled: !!currentCredentialCard
|
|
onActivated: app.currentCredentialCard.calculateCard(true)
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Delete
|
|
enabled: !!currentCredentialCard
|
|
onActivated: app.currentCredentialCard.deleteCard()
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: "Ctrl+D" // This becomes Cmd + D on macOS
|
|
enabled: !!currentCredentialCard
|
|
onActivated: app.currentCredentialCard.toggleFavorite()
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Open
|
|
enabled: !!yubiKey.currentDevice && yubiKey.currentDeviceValidated
|
|
onActivated: yubiKey.scanQr()
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Find
|
|
onActivated: toolBar.searchField.forceActiveFocus()
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Preferences
|
|
onActivated: navigator.goToSettings()
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Quit
|
|
context: Qt.ApplicationShortcut
|
|
onActivated: Qt.quit()
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Close
|
|
onActivated: app.close()
|
|
context: Qt.ApplicationShortcut
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.Italic
|
|
onActivated: navigator.about()
|
|
context: Qt.ApplicationShortcut
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: StandardKey.FullScreen
|
|
onActivated: visibility = visibility
|
|
=== Window.FullScreen ? Window.Windowed : Window.FullScreen
|
|
context: Qt.ApplicationShortcut
|
|
}
|
|
|
|
// This information is stored in the system registry on Windows,
|
|
// and in XML preferences files on macOS. On other Unix systems,
|
|
// in the absence of a standard, INI text files are used.
|
|
// See http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html#details
|
|
Settings {
|
|
id: settings
|
|
|
|
// Can be 0 (off), 6, 7 or 8
|
|
property int slot1digits
|
|
property int slot2digits
|
|
|
|
property bool otpMode
|
|
property bool useCustomReader
|
|
property string customReaderName
|
|
|
|
property bool closeToTray
|
|
property bool hideOnLaunch
|
|
property bool requireTouch: true
|
|
|
|
property int theme: Material.System
|
|
|
|
// Keep track of window and desktop dimensions.
|
|
property alias width: app.width
|
|
property alias height: app.height
|
|
property alias x: app.x
|
|
property alias y: app.y
|
|
|
|
property int desktopAvailableWidth
|
|
property int desktopAvailableHeight
|
|
|
|
property var favorites: []
|
|
|
|
onCloseToTrayChanged: updateTrayVisibility()
|
|
onThemeChanged: {
|
|
app.Material.theme = theme
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: entriesComponent
|
|
EntriesModel {
|
|
}
|
|
}
|
|
|
|
EntriesModel {
|
|
id: entries
|
|
}
|
|
|
|
ClipBoard {
|
|
id: clipBoard
|
|
}
|
|
|
|
Timer {
|
|
id: poller
|
|
triggeredOnStart: true
|
|
interval: 1000
|
|
repeat: true
|
|
running: app.isInForeground || settings.closeToTray
|
|
onTriggered: yubiKey.poll()
|
|
}
|
|
|
|
YubiKey {
|
|
id: yubiKey
|
|
}
|
|
|
|
SystemTray {
|
|
id: sysTrayIcon
|
|
}
|
|
|
|
Navigator {
|
|
id: navigator
|
|
anchors.fill: parent
|
|
focus: true
|
|
Keys.forwardTo: toolBar.searchField
|
|
}
|
|
}
|