mirror of
https://github.com/seiichiro0185/sailotp.git
synced 2024-11-14 21:16:42 +00:00
Merge branch 'feat-cover' into develop
This commit is contained in:
commit
2c4ab39b54
4 changed files with 156 additions and 35 deletions
|
@ -29,11 +29,39 @@
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
|
import "../lib/crypto.js" as OTP
|
||||||
|
|
||||||
// Define the Layout of the Active Cover
|
// Define the Layout of the Active Cover
|
||||||
CoverBackground {
|
CoverBackground {
|
||||||
|
id: coverPage
|
||||||
|
|
||||||
// Show the SailOTP Logo
|
property double lastUpdated: 0
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
// Timer runs only when cover is visible and favourite is set
|
||||||
|
running: !Qt.application.active && appWin.coverSecret != ""
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
// get seconds from current Date
|
||||||
|
var curDate = new Date();
|
||||||
|
|
||||||
|
if (lOTP.text == "" || curDate.getSeconds() == 30 || curDate.getSeconds() == 0 || (curDate.getTime() - lastUpdated > 2000)) {
|
||||||
|
appWin.coverOTP = OTP.calcOTP(appWin.coverSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change color of the OTP to red if less than 5 seconds left
|
||||||
|
if (29 - (curDate.getSeconds() % 30) < 5) {
|
||||||
|
lOTP.color = "red"
|
||||||
|
} else {
|
||||||
|
lOTP.color = Theme.highlightColor
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUpdated = curDate.getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the SailOTP Logo
|
||||||
Image {
|
Image {
|
||||||
id: logo
|
id: logo
|
||||||
source: "../sailotp.png"
|
source: "../sailotp.png"
|
||||||
|
@ -42,10 +70,22 @@ CoverBackground {
|
||||||
anchors.topMargin: 48
|
anchors.topMargin: 48
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the Application Name
|
Column {
|
||||||
Label {
|
anchors.top: logo.bottom
|
||||||
id: label
|
anchors.topMargin: 48
|
||||||
anchors.centerIn: parent
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: "SailOTP"
|
|
||||||
|
Label {
|
||||||
|
text: appWin.coverTitle
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: lOTP
|
||||||
|
text: appWin.coverOTP
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: Theme.highlightColor
|
||||||
|
font.pixelSize: Theme.fontSizeExtraLarge
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,13 @@ import "pages"
|
||||||
|
|
||||||
ApplicationWindow
|
ApplicationWindow
|
||||||
{
|
{
|
||||||
|
id: appWin
|
||||||
|
|
||||||
|
// Properties to pass values between MainPage and Cover
|
||||||
|
property string coverTitle: "SailOTP"
|
||||||
|
property string coverSecret: ""
|
||||||
|
property string coverOTP: ""
|
||||||
|
|
||||||
initialPage: Component { MainView { } }
|
initialPage: Component { MainView { } }
|
||||||
cover: Qt.resolvedUrl("cover/CoverPage.qml")
|
cover: Qt.resolvedUrl("cover/CoverPage.qml")
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,20 +29,34 @@
|
||||||
|
|
||||||
.import QtQuick.LocalStorage 2.0 as LS
|
.import QtQuick.LocalStorage 2.0 as LS
|
||||||
|
|
||||||
// Get DB Connection
|
// Get DB Connection, Initialize or Upgrade DB
|
||||||
function getDB() {
|
function getDB() {
|
||||||
return LS.LocalStorage.openDatabaseSync("harbour-sailotp", "1.0", "SailOTP Config Storage", 1000000);
|
try {
|
||||||
}
|
var db = LS.LocalStorage.openDatabaseSync("harbour-sailotp", "", "SailOTP Config Storage", 1000000);
|
||||||
|
|
||||||
// Initialize Table if not exists
|
if (db.version == "") {
|
||||||
function initialize() {
|
// Initialize an empty DB, Create the Table
|
||||||
var db = getDB();
|
db.changeVersion("", "2",
|
||||||
|
function(tx) {
|
||||||
db.transaction(
|
tx.executeSql("CREATE TABLE IF NOT EXISTS OTPStorage(title TEXT, secret TEXT, type TEXT, counter INTEGER, fav INTEGER);");
|
||||||
function(tx) {
|
}
|
||||||
tx.executeSql("CREATE TABLE IF NOT EXISTS OTPStorage(title TEXT, secret TEXT);");
|
);
|
||||||
|
} else if (db.version == "1.0") {
|
||||||
|
// Upgrade DB Schema to Version 2
|
||||||
|
db.changeVersion("1.0", "2",
|
||||||
|
function(tx) {
|
||||||
|
tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN type TEXT DEFAULT 'TOTP';");
|
||||||
|
tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN counter INTEGER DEFAULT 0;");
|
||||||
|
tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN fav INTEGER DEFAULT 0;");
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
)
|
} catch (e) {
|
||||||
|
// DB Failed to open
|
||||||
|
console.log("Could not open DB: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all OTPs into the list model
|
// Get all OTPs into the list model
|
||||||
|
@ -53,7 +67,8 @@ function getOTP() {
|
||||||
function(tx) {
|
function(tx) {
|
||||||
var res = tx.executeSql("select * from OTPStorage;");
|
var res = tx.executeSql("select * from OTPStorage;");
|
||||||
for (var i=0; i < res.rows.length; i++) {
|
for (var i=0; i < res.rows.length; i++) {
|
||||||
mainPage.appendOTP(res.rows.item(i).title, res.rows.item(i).secret);
|
mainPage.appendOTP(res.rows.item(i).title, res.rows.item(i).secret, res.rows.item(i).type, res.rows.item(i).counter, res.rows.item(i).fav);
|
||||||
|
if (res.rows.item(i).fav) mainPage.setCoverOTP(res.rows.item(i).title, res.rows.item(i).secret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -65,7 +80,7 @@ function addOTP(title, secret) {
|
||||||
|
|
||||||
db.transaction(
|
db.transaction(
|
||||||
function(tx) {
|
function(tx) {
|
||||||
tx.executeSql("INSERT INTO OTPStorage VALUES(?, ?);", [title, secret]);
|
tx.executeSql("INSERT INTO OTPStorage VALUES(?, ?, ?, ?, ?);", [title, secret, 'TOTP', 0, 0]);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -81,6 +96,27 @@ function removeOTP(title, secret) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFav(title, secret) {
|
||||||
|
var db = getDB();
|
||||||
|
|
||||||
|
db.transaction(
|
||||||
|
function(tx) {
|
||||||
|
tx.executeSql("UPDATE OTPStorage set fav = 0");
|
||||||
|
tx.executeSql("UPDATE OTPStorage set fav = 1 WHERE title=? and secret=?;", [title, secret]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetFav(title, secret) {
|
||||||
|
var db = getDB();
|
||||||
|
|
||||||
|
db.transaction(
|
||||||
|
function(tx) {
|
||||||
|
tx.executeSql("UPDATE OTPStorage set fav = 0");
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Change an existing OTP
|
// Change an existing OTP
|
||||||
function changeOTP(title, secret, oldtitle, oldsecret) {
|
function changeOTP(title, secret, oldtitle, oldsecret) {
|
||||||
var db = getDB();
|
var db = getDB();
|
||||||
|
|
|
@ -41,18 +41,27 @@ Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This holds the time of the last update of the page as Unix Timestamp (in Milliseconds)
|
// This holds the time of the last update of the page as Unix Timestamp (in Milliseconds)
|
||||||
property double lastUpdated: null
|
property double lastUpdated: 0
|
||||||
|
|
||||||
// Add an entry to the list
|
// Add an entry to the list
|
||||||
function appendOTP(title, secret) {
|
function appendOTP(title, secret, type, counter, fav) {
|
||||||
otpListModel.append({"secret": secret, "title": title, "otp": ""});
|
otpListModel.append({"secret": secret, "title": title, "fav": fav, "otp": ""});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hand favorite over to the cover
|
||||||
|
function setCoverOTP(title, secret) {
|
||||||
|
appWin.coverTitle = title
|
||||||
|
appWin.coverSecret = secret
|
||||||
|
if (secret == "") appWin.coverOTP = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload the List of OTPs from storage
|
// Reload the List of OTPs from storage
|
||||||
function refreshOTPList() {
|
function refreshOTPList() {
|
||||||
|
otpList.visible = false;
|
||||||
otpListModel.clear();
|
otpListModel.clear();
|
||||||
DB.getOTP();
|
DB.getOTP();
|
||||||
refreshOTPValues();
|
refreshOTPValues();
|
||||||
|
otpList.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate new OTPs for every entry
|
// Calculate new OTPs for every entry
|
||||||
|
@ -67,7 +76,6 @@ Page {
|
||||||
if (otpListModel.get(i).otp == "" || seconds == 30 || seconds == 0 || (curDate.getTime() - lastUpdated > 2000)) {
|
if (otpListModel.get(i).otp == "" || seconds == 30 || seconds == 0 || (curDate.getTime() - lastUpdated > 2000)) {
|
||||||
var curOTP = OTP.calcOTP(otpListModel.get(i).secret)
|
var curOTP = OTP.calcOTP(otpListModel.get(i).secret)
|
||||||
otpListModel.setProperty(i, "otp", curOTP);
|
otpListModel.setProperty(i, "otp", curOTP);
|
||||||
console.log("Updating Value ", i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +87,8 @@ Page {
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
interval: 1000
|
interval: 1000
|
||||||
running: Qt.application.active // Timer only runs when App is active
|
// Timer only runs when app is acitive and we have entries
|
||||||
|
running: Qt.application.active && otpListModel.count
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: refreshOTPValues();
|
onTriggered: refreshOTPValues();
|
||||||
}
|
}
|
||||||
|
@ -104,6 +113,8 @@ Page {
|
||||||
maximumValue: 29
|
maximumValue: 29
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 48
|
anchors.topMargin: 48
|
||||||
|
// Only show when there are enries
|
||||||
|
visible: otpListModel.count
|
||||||
}
|
}
|
||||||
|
|
||||||
SilicaListView {
|
SilicaListView {
|
||||||
|
@ -121,37 +132,66 @@ Page {
|
||||||
hintText: "Pull down to add a OTP"
|
hintText: "Pull down to add a OTP"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
delegate: ListItem {
|
delegate: ListItem {
|
||||||
id: otpListItem
|
id: otpListItem
|
||||||
menu: otpContextMenu
|
menu: otpContextMenu
|
||||||
width: otpList.width
|
|
||||||
contentHeight: Theme.itemSizeMedium
|
contentHeight: Theme.itemSizeMedium
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
function remove() {
|
function remove() {
|
||||||
// Show 5s countdown, then delete from DB and List
|
// Show 5s countdown, then delete from DB and List
|
||||||
remorseAction("Deleting", function() { DB.removeOTP(title, secret); otpListModel.remove(index) })
|
remorseAction("Deleting", function() { DB.removeOTP(title, secret); otpListModel.remove(index) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Clipboard.text = otp
|
||||||
|
}
|
||||||
|
|
||||||
ListView.onRemove: animateRemoval()
|
ListView.onRemove: animateRemoval()
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: listRow
|
||||||
|
width: parent.width
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
Label {
|
IconButton {
|
||||||
|
icon.source: fav == 1 ? "image://theme/icon-m-favorite-selected" : "image://theme/icon-m-favorite"
|
||||||
|
anchors.left: parent.left
|
||||||
|
onClicked: {
|
||||||
|
if (fav == 0) {
|
||||||
|
DB.setFav(title, secret)
|
||||||
|
setCoverOTP(title, secret)
|
||||||
|
for (var i=0; i<otpListModel.count; i++) {
|
||||||
|
if (i != index) {
|
||||||
|
otpListModel.setProperty(i, "fav", 0);
|
||||||
|
} else {
|
||||||
|
otpListModel.setProperty(i, "fav", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DB.resetFav(title, secret)
|
||||||
|
setCoverOTP("SailOTP", "")
|
||||||
|
otpListModel.setProperty(index, "fav", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Label {
|
||||||
id: otpLabel
|
id: otpLabel
|
||||||
text: model.title
|
text: model.title
|
||||||
color: Theme.secondaryColor
|
color: Theme.secondaryColor
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: otpValue
|
id: otpValue
|
||||||
anchors.top: otpLabel.bottom
|
|
||||||
text: model.otp
|
text: model.otp
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
color: Theme.highlightColor
|
color: Theme.highlightColor
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,8 +214,6 @@ Page {
|
||||||
VerticalScrollDecorator{}
|
VerticalScrollDecorator{}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Initialize DB (create tables etc..)
|
|
||||||
DB.initialize();
|
|
||||||
// Load list of OTP-Entries
|
// Load list of OTP-Entries
|
||||||
refreshOTPList();
|
refreshOTPList();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue