diff --git a/.eslintrc.json b/.eslintrc.json
index 9d48db4..eaf1bce 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,6 +1,9 @@
{
"root": true,
"ignorePatterns": ["projects/**/*"],
+ "env": {
+ "es6": true
+ },
"overrides": [
{
"files": ["*.ts"],
diff --git a/package-lock.json b/package-lock.json
index 73612ad..70d59d3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "authleu",
- "version": "1.0.0",
+ "version": "1.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "authleu",
- "version": "1.0.0",
+ "version": "1.1.0",
"dependencies": {
"@angular/animations": "^18.0.0",
"@angular/common": "^18.0.0",
@@ -61,6 +61,7 @@
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
+ "replace-in-file": "^6.3.5",
"typescript": "~5.4.0"
}
},
@@ -15300,6 +15301,93 @@
"jsesc": "bin/jsesc"
}
},
+ "node_modules/replace-in-file": {
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.5.tgz",
+ "integrity": "sha512-arB9d3ENdKva2fxRnSjwBEXfK1npgyci7ZZuwysgAp7ORjHSyxz6oqIjTEv8R0Ydl4Ll7uOAZXL4vbkhGIizCg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "glob": "^7.2.0",
+ "yargs": "^17.2.1"
+ },
+ "bin": {
+ "replace-in-file": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/replace-in-file/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/replace-in-file/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/replace-in-file/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/replace-in-file/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/replace-in-file/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/replace-in-file/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
diff --git a/package.json b/package.json
index 4244fcc..0a04c8b 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"scripts": {
"ng": "ng",
"start": "ng serve",
+ "prebuild": "npm run version:config",
"build": "ng build",
"build:githubpages": "ng build --configuration githubpages --base-href ./",
"watch": "ng build --watch --configuration development",
@@ -15,7 +16,8 @@
"translations:check": "node ./resources/scripts/translations_complete_check.js",
"firebase:deploy:firestore:indexes": "firebase deploy --only firestore:indexes",
"firebase:deploy:hosting": "ng build --prod && firebase deploy --only hosting",
- "firebase:deploy:all": "ng build --prod && firebase deploy"
+ "firebase:deploy:all": "ng build --prod && firebase deploy",
+ "version:config": "node ./resources/scripts/version-config.js"
},
"private": true,
"dependencies": {
@@ -72,6 +74,7 @@
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
+ "replace-in-file": "^6.3.5",
"typescript": "~5.4.0"
}
}
diff --git a/resources/scripts/version-config.js b/resources/scripts/version-config.js
new file mode 100644
index 0000000..b87cce5
--- /dev/null
+++ b/resources/scripts/version-config.js
@@ -0,0 +1,21 @@
+#!/usr/bin/env node
+
+const replacer = require('replace-in-file')
+const package = require('../../package.json')
+
+const buildDate = new Date().toISOString()
+// get git commit hash from GITHUB_SHA environment variable
+const commitHash = process.env.GITHUB_SHA || 'unknown'
+
+const options = {
+ files: 'src/environments/environment*.ts',
+ from: [/%VERSION%/g, /%BUILD_DATE%/g, /%COMMIT_HASH%/g],
+ to: [package.version, buildDate, commitHash],
+}
+
+try {
+ const changes = replacer.sync(options)
+ console.log('Modified files:', JSON.stringify(changes, null, 2))
+} catch (error) {
+ console.error('Error replacing version strings occurred:', error)
+}
diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html
index c0ebf1a..a5a2f32 100644
--- a/src/app/home/home.page.html
+++ b/src/app/home/home.page.html
@@ -5,15 +5,10 @@
-
+
-
-
-
- {{ "CONFIG_MENU.LOCK" | translate }}
-
{{ "CONFIG_MENU.UNLOCK" | translate }}
@@ -30,12 +25,35 @@
{{ "CONFIG_MENU.IMPORT_ACCOUNTS" | translate }}
-
+
+
+
+
+
+
+ {{ "CONFIG_MENU.ENCRYPT_ACCOUNTS" | translate }}
+
+
+ {{ "CONFIG_MENU.PERIODIC_CHECK" | translate }}
+
+
+
+
+
+
+
{{ "CONFIG_MENU.COLOR_MODE" | translate }}
{{ darkModeLabel | translate }}
-
+
diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts
index 82bf1b7..dd5ebbe 100644
--- a/src/app/home/home.page.ts
+++ b/src/app/home/home.page.ts
@@ -11,6 +11,8 @@ import { LocalStorageService } from '../services/local-storage.service';
import { TranslateService } from '@ngx-translate/core';
import { GlobalUtils } from '../utils/global-utils';
import { AccountSelectModalComponent } from '../components/account-select-modal/account-select-modal.component';
+import { AppConfigService } from '../services/app-config.service';
+import { ENCRYPTION_OPTIONS_PASSWORD_KEY, EncryptionOptions } from '../models/encryption-options.model';
@Component({
selector: 'app-home',
@@ -63,6 +65,8 @@ export class HomePage implements OnInit {
isAddAccountModalOpen: boolean = false
isScanActive: boolean = false
isWindowFocused: boolean = true
+ isEncryptionActive: boolean = false
+ shouldPeriodicCheckPassword: boolean = false
private systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)');
private isLandscape: boolean = false
@@ -77,6 +81,7 @@ export class HomePage implements OnInit {
private modalController: ModalController,
private logoService: LogoService,
private storageService: LocalStorageService,
+ private configService: AppConfigService,
private translateService: TranslateService,
private navCtrl: NavController,
formBuilder: FormBuilder
@@ -132,16 +137,12 @@ export class HomePage implements OnInit {
backdropDismiss: false
})
GlobalUtils.hideSplashScreen()
- await loading.present()
- this.accounts$ = await this.accountsService.getAccounts()
- const lastSelectedAccountId: string | undefined = await this.storageService.get('lastSelectedAccountId')
- if (lastSelectedAccountId) {
- const accounts = await firstValueFrom(this.accounts$)
- const lastSelectedAccount = accounts.find(account => account.id === lastSelectedAccountId)
- if (lastSelectedAccount) {
- this.selectAccount(lastSelectedAccount)
- }
+ const encryptionOptions = await this.configService.getEncryptionOptions()
+ if(encryptionOptions) {
+ await this.setupEncryption(encryptionOptions)
}
+ await loading.present()
+ await this.loadAccounts()
await loading.dismiss()
}
@@ -309,6 +310,30 @@ export class HomePage implements OnInit {
}
}
+ async encryptionActiveToggle() {
+ this.isEncryptionActive = !this.isEncryptionActive
+ await this.saveEncryptionOptions()
+ }
+
+ get encryptionMenuSlotLabel(): string {
+ return this.isEncryptionActive ? 'CONFIG_MENU.ENCRYPTION_ACTIVE' : 'CONFIG_MENU.ENCRYPTION_INACTIVE'
+ }
+
+ get encryptionMenuSlotLabelColor(): string {
+ return this.isEncryptionActive ? 'success' : 'danger'
+ }
+
+ periodicCheckToggle() {
+ this.shouldPeriodicCheckPassword = !this.shouldPeriodicCheckPassword
+ }
+
+ async saveEncryptionOptions() {
+ await this.configService.setEncryptionOptions({
+ encryptionActive: this.isEncryptionActive,
+ shouldPerformPeriodicCheck: this.shouldPeriodicCheckPassword
+ })
+ }
+
async lockAccountAction() {
const password = '1490'
const accountSelected = this.selectedAccount
@@ -592,4 +617,71 @@ export class HomePage implements OnInit {
await confirmPrompt.present()
})
}
+
+ private async loadAccounts() {
+ const accounts$ = await this.accountsService.getAccounts()
+ const lastSelectedAccountId: string | undefined = await this.storageService.get('lastSelectedAccountId')
+ if (lastSelectedAccountId) {
+ const accounts = await firstValueFrom(accounts$)
+ const lastSelectedAccount = accounts.find(account => account.id === lastSelectedAccountId)
+ if (lastSelectedAccount) {
+ this.selectAccount(lastSelectedAccount)
+ }
+ }
+ this.accounts$ = accounts$
+ }
+
+ private async setupEncryption(encryptionOptions: EncryptionOptions) {
+ // set page properties
+ this.isEncryptionActive = encryptionOptions.encryptionActive
+ this.shouldPeriodicCheckPassword = encryptionOptions.shouldPerformPeriodicCheck
+
+ if(this.isEncryptionActive) {
+ await this.setupEncryptionPassword()
+ }
+ }
+
+ private async setupEncryptionPassword() {
+ const password = await this.storageService.get(ENCRYPTION_OPTIONS_PASSWORD_KEY)
+ if(!password) {
+ // show password prompt
+ const password = await this.promptPassword()
+ if(password) {
+ await this.storageService.set(ENCRYPTION_OPTIONS_PASSWORD_KEY, password)
+ }
+ }
+ }
+
+ private async promptPassword(): Promise {
+ const title = await firstValueFrom(this.translateService.get('HOME.PASSWORD_PROMPT_TITLE'))
+ const message = await firstValueFrom(this.translateService.get('HOME.PASSWORD_PROMPT_MESSAGE'))
+ const cancelText = await firstValueFrom(this.translateService.get('HOME.PASSWORD_PROMPT_CANCEL'))
+ const okText = await firstValueFrom(this.translateService.get('HOME.PASSWORD_PROMPT_CONFIRM'))
+
+ const alert = await this.alertController.create({
+ header: title,
+ message,
+ inputs: [
+ {
+ name: 'password',
+ type: 'password'
+ }
+ ],
+ buttons: [
+ {
+ text: cancelText,
+ role: 'cancel'
+ }, {
+ text: okText,
+ handler: (data) => {
+ return data.password
+ }
+ }
+ ]
+ })
+ await alert.present()
+
+ const { data } = await alert.onDidDismiss()
+ return data?.values?.password || ''
+ }
}
diff --git a/src/app/models/account2FA.model.ts b/src/app/models/account2FA.model.ts
index 699600c..fbd74a6 100644
--- a/src/app/models/account2FA.model.ts
+++ b/src/app/models/account2FA.model.ts
@@ -20,6 +20,7 @@ export interface IAccount2FA {
export interface IAccount2FAProvider {
getAccounts(): Promise>;
addAccount(account: Account2FA): Promise;
+ updateAccount(account: Account2FA): Promise;
clearCache?(): Promise;
}
diff --git a/src/app/models/encryption-options.model.ts b/src/app/models/encryption-options.model.ts
new file mode 100644
index 0000000..7831a5a
--- /dev/null
+++ b/src/app/models/encryption-options.model.ts
@@ -0,0 +1,7 @@
+export interface EncryptionOptions {
+ encryptionActive: boolean;
+ shouldPerformPeriodicCheck: boolean;
+}
+
+export const ENCRYPTION_OPTIONS_KEY = 'encryptionOptions';
+export const ENCRYPTION_OPTIONS_PASSWORD_KEY = '_eok';
\ No newline at end of file
diff --git a/src/app/services/accounts/account2fa.service.ts b/src/app/services/accounts/account2fa.service.ts
index 4a226ae..29ebca0 100644
--- a/src/app/services/accounts/account2fa.service.ts
+++ b/src/app/services/accounts/account2fa.service.ts
@@ -72,6 +72,16 @@ export class Account2faService {
return accounts$;
}
+ /**
+ * Updates an existing account with new information.
+ *
+ * @param account - The account object containing updated information.
+ * @returns A promise that resolves when the account has been updated.
+ */
+ public async updateAccount(account: Account2FA) {
+ return this.service.updateAccount(account)
+ }
+
public async clearCache() {
if (this.service.clearCache) {
await this.service.clearCache()
@@ -101,12 +111,21 @@ export class Account2faService {
a.remove();
}
+ /**
+ * Adds the given accounts to the account provider.
+ * @param accounts - The array of accounts to import.
+ */
public async importAccounts(accounts: Account2FA[]) {
for (const account of accounts) {
await this.addAccount(account)
}
}
+ /**
+ * Reads accounts from a file and returns them as an array of Account2FA objects.
+ * @param file - The file to read accounts from.
+ * @returns A promise that resolves with the array of accounts.
+ */
public readAccountsFromFile(file: File): Promise {
return new Promise((resolve, reject) => {
const reader = new FileReader()
@@ -139,12 +158,4 @@ export class Account2faService {
reader.readAsText(file)
})
}
-
- useLocalService() {
- this._service = this.localService
- }
-
- useRemoteService() {
- this._service = this.remoteService
- }
}
diff --git a/src/app/services/accounts/local-account2fa.service.ts b/src/app/services/accounts/local-account2fa.service.ts
index 3bcde9b..6147979 100644
--- a/src/app/services/accounts/local-account2fa.service.ts
+++ b/src/app/services/accounts/local-account2fa.service.ts
@@ -43,6 +43,22 @@ export class LocalAccount2faService implements IAccount2FAProvider {
return id
}
+ async updateAccount(account: Account2FA): Promise {
+ await this.loadAccountsFromStorage()
+
+ const existing = this.accounts.find(a => a.id === account.id)
+ if (!existing) {
+ throw new Error('ACCOUNT_NOT_FOUND')
+ }
+
+ const index = this.accounts.indexOf(existing)
+ this.accounts[index] = account
+ this.sortAccounts()
+ await this.localStorage.set('local_accounts', this.accounts)
+
+ this.accountsSubject.next(this.accounts)
+ }
+
private async loadAccountsFromStorage() {
if (this.loaded) {
return
diff --git a/src/app/services/accounts/remote-account2fa.service.ts b/src/app/services/accounts/remote-account2fa.service.ts
index 30d0253..6846dd2 100644
--- a/src/app/services/accounts/remote-account2fa.service.ts
+++ b/src/app/services/accounts/remote-account2fa.service.ts
@@ -40,6 +40,17 @@ export class RemoteAccount2faService implements IAccount2FAProvider {
return id
}
+ async updateAccount(account: Account2FA): Promise {
+ throw new Error('INVALID_SESSION')
+ const userId = await this.authService.getCurrentUserId()
+ if(!userId) {
+ throw new Error('INVALID_SESSION')
+ }
+ const accountCollection = collection(this.firestore, `accounts2fa/${userId}/accounts`)
+ const document = doc(accountCollection, account.id)
+ await setDoc(document, account.typeErased())
+ }
+
public async clearCache() {
await terminate(this.firestore)
await clearIndexedDbPersistence(this.firestore)
diff --git a/src/app/services/app-config.service.ts b/src/app/services/app-config.service.ts
index a9a4a73..d3b7206 100644
--- a/src/app/services/app-config.service.ts
+++ b/src/app/services/app-config.service.ts
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { LocalStorageService } from './local-storage.service';
import { environment } from 'src/environments/environment';
+import { ENCRYPTION_OPTIONS_KEY, EncryptionOptions } from '../models/encryption-options.model';
@Injectable({
providedIn: 'root'
@@ -23,6 +24,14 @@ export class AppConfigService {
return environment.isOfflineEnv;
}
+ async getEncryptionOptions() {
+ return await this.localStorage.get(ENCRYPTION_OPTIONS_KEY);
+ }
+
+ async setEncryptionOptions(options: EncryptionOptions) {
+ await this.localStorage.set(ENCRYPTION_OPTIONS_KEY, options);
+ }
+
static supportsCryptoAPI(): boolean {
return !!(crypto && crypto.subtle)
}
diff --git a/src/app/services/migration.service.ts b/src/app/services/migration.service.ts
new file mode 100644
index 0000000..181ca36
--- /dev/null
+++ b/src/app/services/migration.service.ts
@@ -0,0 +1,9 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class MigrationService {
+
+ constructor() { }
+}
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 9530046..2d5e471 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -12,6 +12,11 @@
"SELECTION_COUNTER": "{{selected}}/{{total}} selected",
"SELECT_ALL": "Select All"
},
+ "ACCOUNT_SERVICE": {
+ "ERROR": {
+ "ACCOUNT_NOT_FOUND": "Account not found"
+ }
+ },
"ACCOUNT_SYNC": {
"ERROR": {
"CORRUPT_BACKUP_FILE": "Corrupted backup file",
@@ -57,10 +62,21 @@
"COLOR_MODE_DARK": "Dark",
"COLOR_MODE_LIGHT": "Light",
"COLOR_MODE_SYSTEM": "System",
+ "ENCRYPTION_ACTIVE": "Active",
+ "ENCRYPTION_INACTIVE": "Inactive",
+ "ENCRYPTION_OPTIONS": "Encryption",
+ "ENCRYPT_ACCOUNTS": "Encrypt Accounts",
"EXPORT_ACCOUNTS": "Export Backup",
"IMPORT_ACCOUNTS": "Import Backup",
"LOGIN": "Sign in",
- "LOGOUT": "Sign out"
+ "LOGOUT": "Sign out",
+ "PERIODIC_CHECK": "Periodic Password Check"
+ },
+ "CRYPTO": {
+ "ALREADY_DECRYPTED": "Data is already decrypted",
+ "ALREADY_ENCRYPTED": "Data is already encrypted",
+ "CRYPTO_API_NOT_SUPPORTED": "Crypto API not supported by the browser",
+ "MISSING_ENCRYPTED_DATA": "Missing encrypted data"
},
"CRYPTO_API_NOT_SUPPORTED": "Crypto API not supported by the browser",
"HOME": {
@@ -70,7 +86,11 @@
"CONFIRM_LOGOUT_YES": "CLEAR LOCAL DATA AND SIGN OUT",
"LOADING_ACCOUNTS": "Loading accounts...",
"LOADING_ACCOUNTS_FILE": "Loading accounts from file...",
- "LOGGING_OUT": "Signing out..."
+ "LOGGING_OUT": "Signing out...",
+ "PASSWORD_PROMPT_CANCEL": "Cancel",
+ "PASSWORD_PROMPT_CONFIRM": "Confirm",
+ "PASSWORD_PROMPT_MESSAGE": "In order to decrypt the accounts, please enter the encryption password",
+ "PASSWORD_PROMPT_TITLE": "Enter the encryption password"
},
"LOGIN": {
"AUTHENTICATING": "Authenticating...",
@@ -96,11 +116,5 @@
"UPDATE_AVAILABLE_BODY": "A new version of the app is available and will be installed automatically to get the latest features and bug fixes.",
"UPDATE_AVAILABLE_HEADER": "Update available",
"UPDATE_NOW": "Update now"
- },
- "CRYPTO": {
- "CRYPTO_API_NOT_SUPPORTED": "Crypto API not supported by the browser",
- "MISSING_ENCRYPTED_DATA": "Missing encrypted data",
- "ALREADY_ENCRYPTED": "Data is already encrypted",
- "ALREADY_DECRYPTED": "Data is already decrypted"
}
}
\ No newline at end of file
diff --git a/src/assets/i18n/pt.json b/src/assets/i18n/pt.json
index b08128b..a0d7989 100644
--- a/src/assets/i18n/pt.json
+++ b/src/assets/i18n/pt.json
@@ -12,6 +12,11 @@
"SELECTION_COUNTER": "{{selected}} de {{total}} selecionada(s)",
"SELECT_ALL": "Selecionar Tudo"
},
+ "ACCOUNT_SERVICE": {
+ "ERROR": {
+ "ACCOUNT_NOT_FOUND": "Conta não encontrada"
+ }
+ },
"ACCOUNT_SYNC": {
"ERROR": {
"CORRUPT_BACKUP_FILE": "Arquivo de backup corrompido",
@@ -57,10 +62,21 @@
"COLOR_MODE_DARK": "Escuro",
"COLOR_MODE_LIGHT": "Claro",
"COLOR_MODE_SYSTEM": "Sistema",
+ "ENCRYPTION_ACTIVE": "Ativo",
+ "ENCRYPTION_INACTIVE": "Inativo",
+ "ENCRYPTION_OPTIONS": "Criptografia",
+ "ENCRYPT_ACCOUNTS": "Criptografar Contas",
"EXPORT_ACCOUNTS": "Exportar Backup",
"IMPORT_ACCOUNTS": "Importar Backup",
"LOGIN": "Entrar",
- "LOGOUT": "Sair"
+ "LOGOUT": "Sair",
+ "PERIODIC_CHECK": "Verificação Periódica de Senha"
+ },
+ "CRYPTO": {
+ "ALREADY_DECRYPTED": "Os dados já estão descriptografados",
+ "ALREADY_ENCRYPTED": "Os dados já estão criptografados",
+ "CRYPTO_API_NOT_SUPPORTED": "API de criptografia não suportada pelo navegador",
+ "MISSING_ENCRYPTED_DATA": "Dados criptografados ausentes"
},
"CRYPTO_API_NOT_SUPPORTED": "API de criptografia não suportada pelo navegador",
"HOME": {
@@ -70,7 +86,11 @@
"CONFIRM_LOGOUT_YES": "LIMPAR DADOS LOCAIS E SAIR",
"LOADING_ACCOUNTS": "Carregando contas...",
"LOADING_ACCOUNTS_FILE": "Carregando arquivo de contas...",
- "LOGGING_OUT": "Saindo..."
+ "LOGGING_OUT": "Saindo...",
+ "PASSWORD_PROMPT_CANCEL": "Cancelar",
+ "PASSWORD_PROMPT_CONFIRM": "Confirmar",
+ "PASSWORD_PROMPT_MESSAGE": "Para descriptografar as contas, insira a senha de criptografia",
+ "PASSWORD_PROMPT_TITLE": "Digite a senha de criptografia"
},
"LOGIN": {
"AUTHENTICATING": "Autenticando...",
@@ -96,11 +116,5 @@
"UPDATE_AVAILABLE_BODY": "Uma nova versão da aplicação está disponível e será instalada automaticamente para obter as últimas funcionalidades e correções de bugs.",
"UPDATE_AVAILABLE_HEADER": "Atualização disponível",
"UPDATE_NOW": "Atualizar agora"
- },
- "CRYPTO": {
- "CRYPTO_API_NOT_SUPPORTED": "API de criptografia não suportada pelo navegador",
- "MISSING_ENCRYPTED_DATA": "Dados criptografados ausentes",
- "ALREADY_ENCRYPTED": "Os dados já estão criptografados",
- "ALREADY_DECRYPTED": "Os dados já estão descriptografados"
}
}
\ No newline at end of file
diff --git a/src/environments/environment.githubpages.ts b/src/environments/environment.githubpages.ts
index ad44d86..63f78e7 100644
--- a/src/environments/environment.githubpages.ts
+++ b/src/environments/environment.githubpages.ts
@@ -10,5 +10,11 @@ export const environment = {
apiKey: "AIzaSyB0JZ7doymz8wuuEnqcyFKrZAouSjZ55bY",
authDomain: "authleu.firebaseapp.com",
messagingSenderId: "946698001868"
- }
+ },
+ versionConfig: {
+ versionNumber: "%VERSION%",
+ buildDate: "%BUILD_DATE%",
+ commitHash: "%COMMIT_HASH%",
+ versionName: "%VERSION%-ghpages"
+ }
};
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index c1ca681..c093852 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -10,5 +10,11 @@ export const environment = {
apiKey: "AIzaSyB0JZ7doymz8wuuEnqcyFKrZAouSjZ55bY",
authDomain: "authleu.firebaseapp.com",
messagingSenderId: "946698001868"
+ },
+ versionConfig: {
+ versionNumber: "%VERSION%",
+ buildDate: "%BUILD_DATE%",
+ commitHash: "%COMMIT_HASH%",
+ versionName: "%VERSION%"
}
};
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index ab62805..1289ee4 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -14,6 +14,12 @@ export const environment = {
apiKey: "AIzaSyB0JZ7doymz8wuuEnqcyFKrZAouSjZ55bY",
authDomain: "authleu.firebaseapp.com",
messagingSenderId: "946698001868"
+ },
+ versionConfig: {
+ versionNumber: "%VERSION%",
+ buildDate: "%BUILD_DATE%",
+ commitHash: "%COMMIT_HASH%",
+ versionName: "%VERSION%-DEV"
}
};
diff --git a/src/global.scss b/src/global.scss
index 98760af..053ab14 100644
--- a/src/global.scss
+++ b/src/global.scss
@@ -44,4 +44,9 @@ ion-toast.width-auto {
display: flex;
align-items: center;
justify-content: center;
+}
+
+ion-popover.wider-popover {
+ --max-width: 300px;
+ --width: 300px;
}
\ No newline at end of file