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.ENCRYPTION_OPTIONS" | translate }} + {{ encryptionMenuSlotLabel | 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