diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 2c107c1c2cb1..8da2f1e71347 100755 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2379,5 +2379,28 @@ }, "sharingNotAcceptedYetDesc2": { "message": "This security code will enable him to validate your identity and let you access shared items." + }, + "profileMigrationTitle": { + "message": "Important information" + }, + "profileMigrationContent": { + "message": "Your Cozy extension will evolve. Your profiles will be deleted after $DATE$. So you have $REMAINING$ to transform your $PROFILES_COUNT$ profiles into contacts and then continue to use the autofill feature.", + "placeholders": { + "date": { + "content": "$1", + "example": "January 1st, 1970" + }, + "remaining": { + "content": "$2", + "example": "15" + }, + "profiles_count": { + "content": "$3", + "example": "12" + } + } + }, + "profileMigrationAction": { + "message": "More details" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 8e211e4d8bea..530d07c01478 100755 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -2427,5 +2427,28 @@ }, "sharingNotAcceptedYetDesc2": { "message": "Ce code de sécurité lui permettra de valider votre identité et ainsi d'accéder aux éléments partagés." + }, + "profileMigrationTitle": { + "message": "Information importante" + }, + "profileMigrationContent": { + "message": "Votre extension Cozy évolue. Les profils seront supprimé le $DATE$. Il vous reste donc $REMAINING$ pour transformer vos $PROFILES_COUNT$ profils en contact et continuer à utiliser la fonction de remplissage automatique.", + "placeholders": { + "date": { + "content": "$1", + "example": "1er janvier 1970" + }, + "remaining": { + "content": "$2", + "example": "15" + }, + "profiles_count": { + "content": "$3", + "example": "12" + } + } + }, + "profileMigrationAction": { + "message": "En savoir plus" } } diff --git a/apps/browser/src/cozy/components/profiles-migration/profiles-migration.component.html b/apps/browser/src/cozy/components/profiles-migration/profiles-migration.component.html new file mode 100644 index 000000000000..aa68f67c3959 --- /dev/null +++ b/apps/browser/src/cozy/components/profiles-migration/profiles-migration.component.html @@ -0,0 +1,14 @@ +
+
+
+
+
{{ "profileMigrationTitle" | i18n }}
+
{{ "profileMigrationContent" | i18n: deadline:remaining:profilesCount }}
+
+
+ +
+
+
diff --git a/apps/browser/src/cozy/components/profiles-migration/profiles-migration.component.ts b/apps/browser/src/cozy/components/profiles-migration/profiles-migration.component.ts new file mode 100644 index 000000000000..1628b6bf07a2 --- /dev/null +++ b/apps/browser/src/cozy/components/profiles-migration/profiles-migration.component.ts @@ -0,0 +1,79 @@ +/* eslint-disable no-console */ +import { Component, Input, OnInit } from "@angular/core"; +/* eslint-disable import/no-duplicates */ +import addDays from "date-fns/addDays"; +import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict"; +import isAfter from "date-fns/isAfter"; +import fr from "date-fns/locale/fr"; + +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { StateService } from "@bitwarden/common/abstractions/state.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; + +const DELAY_IN_DAYS = 15; + +@Component({ + selector: "app-profiles-migration", + templateUrl: "profiles-migration.component.html", +}) +export class ProfilesMigrationComponent implements OnInit { + @Input() profilesCount: number; + remaining: string; + deadline: string; + ready = false; + constructor( + protected cipherService: CipherService, + protected i18nService: I18nService, + protected stateService: StateService + ) {} + + async ngOnInit() { + let cleanDeadline = await this.stateService.getProfilesCleanDeadline(); + + if (!cleanDeadline) { + cleanDeadline = addDays(new Date(), DELAY_IN_DAYS); + this.stateService.setProfilesCleanDeadline(cleanDeadline); + } + + this.deadline = cleanDeadline.toLocaleDateString(); + this.remaining = formatDistanceToNowStrict(cleanDeadline, { + // @ts-expect-error I did not succeed in getting i18nService.translationLocale so I fallback to a private property + locale: this.i18nService.systemLanguage === "fr" ? fr : undefined, + unit: "day", + }); + + const didClean = await this.handleDeadline(cleanDeadline); + + if (!didClean) { + this.ready = true; + } + } + + protected async handleDeadline(deadline: Date) { + if (!isAfter(new Date(), deadline)) { + return false; + } + + try { + const allCiphers = await this.cipherService.getAllDecrypted(); + + for (const cipher of allCiphers) { + if (cipher.type === CipherType.Identity && !cipher.isDeleted) { + await this.cipherService.softDeleteWithServer(cipher.id); + } + } + } catch (err) { + console.log("Error while trying to delete Profiles", err); + return false; + } + + return true; + } + + moreInfo() { + const infoUrl = + "https://support.cozy.io/394-comment-puis-je-parametrer-mon-gestionnaire-de-mot-de-passe/"; + window.open(infoUrl); + } +} diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index c568f8a767c8..5715a7a9c5ed 100755 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -83,6 +83,7 @@ import { IfFlagDirective } from "../cozy/components/flag-conditional/if-flag.dir import { AddGenericComponent } from "../cozy/components/add-generic/add-generic.component"; import { ViewExpirationDateComponent } from "../vault/popup/components/vault/view-expiration-date.component"; import { ContactAvatarComponent } from "../vault/popup/components/vault/contact-avatar.component"; +import { ProfilesMigrationComponent } from "../cozy/components/profiles-migration/profiles-migration.component"; /* eslint-enable */ /* end Cozy imports */ @@ -169,6 +170,7 @@ import { ContactAvatarComponent } from "../vault/popup/components/vault/contact- AddGenericComponent, ViewExpirationDateComponent, ContactAvatarComponent, + ProfilesMigrationComponent, /* end Cozy components */ ], providers: [CurrencyPipe, DatePipe], diff --git a/apps/browser/src/popup/scss/cozy-profiles-migration.scss b/apps/browser/src/popup/scss/cozy-profiles-migration.scss new file mode 100644 index 000000000000..c98f278675b9 --- /dev/null +++ b/apps/browser/src/popup/scss/cozy-profiles-migration.scss @@ -0,0 +1,75 @@ +.profileMigration { + @include themify($themes) { + background-color: themed("backgroundColorAlt"); + } + padding: 16px; + + .alert { + @include themify($themes) { + background-color: change-color(themed("warningColor"), $alpha: 0.12); + } + display: flex; + flex-wrap: wrap; + + padding: 8px 16px; + + border-radius: 8px; + + .alert-icon { + display: flex; + + height: 16px; + width: 16px; + + margin-right: 12px; + margin-top: 9px; + padding: 7px 0; + + font-size: 22px; + + @include themify($themes) { + background-color: themed("warningColor"); + color: themed("warningColor"); + } + } + + .alert-message { + display: flex; + flex-wrap: wrap; + flex: auto; + align-items: center; + + max-width: calc(100% - 28px); + + padding: 8px 0; + + .alert-title { + width: 100%; + + margin-bottom: 0.35em; + margin-top: -2px; + + font-weight: bold; + } + } + + .alert-action { + display: block; + + width: 100%; + + margin-left: auto; + margin-right: -6px; + padding-left: 0; + + text-align: right; + text-transform: uppercase; + + .btn.link { + @include themify($themes) { + color: themed("warningColor") !important; + } + } + } + } +} diff --git a/apps/browser/src/popup/scss/misc.scss b/apps/browser/src/popup/scss/misc.scss index ca20d95a03c7..a930991c34c8 100755 --- a/apps/browser/src/popup/scss/misc.scss +++ b/apps/browser/src/popup/scss/misc.scss @@ -324,6 +324,19 @@ This class is responsible of changing tabs colors : } } +.cozy-icon-info { + background-color: $gray-light; + color: $gray-light; + mask-image: url("cozy-ui/assets/icons/ui/info.svg"); + mask-position: center; + mask-repeat: no-repeat; + mask-size: contain; +} + +.cozy-icon-info::before { + content: "\f071"; // keep height +} + .bwi-lock-f { background-color: $gray-light; mask-image: url("cozy-ui/assets/icons/ui/stack.svg"); diff --git a/apps/browser/src/popup/scss/popup.scss b/apps/browser/src/popup/scss/popup.scss index b66a1018166b..21af2d1306e7 100644 --- a/apps/browser/src/popup/scss/popup.scss +++ b/apps/browser/src/popup/scss/popup.scss @@ -11,4 +11,8 @@ @import "plugins.scss"; @import "environment.scss"; @import "pages.scss"; +// Cozy Customization, Profile migration +//* +@import "cozy-profiles-migration.scss"; +//*/ @import "@angular/cdk/overlay-prebuilt.css"; diff --git a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts index 88182c5a6e0b..9a5985e2e3c3 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts +++ b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts @@ -28,6 +28,7 @@ import { BrowserApi } from "../../../../browser/browserApi"; import { PopupUtilsService } from "../../../../popup/services/popup-utils.service"; /* Cozy imports */ /* eslint-disable */ +import { CozyClientService } from "../../../../popup/services/cozyClient.service"; import { KonnectorsService } from "../../../../popup/services/konnectors.service"; import { HistoryService } from "../../../../popup/services/history.service"; import { deleteCipher } from "./cozy-utils"; @@ -74,7 +75,8 @@ export class AddEditComponent extends BaseAddEditComponent { passwordRepromptService: PasswordRepromptService, logService: LogService, private konnectorsService: KonnectorsService, - private historyService: HistoryService + private historyService: HistoryService, + private cozyClientService: CozyClientService ) { super( cipherService, @@ -328,7 +330,8 @@ export class AddEditComponent extends BaseAddEditComponent { this.i18nService, this.platformUtilsService, this.cipher, - this.stateService + this.stateService, + this.cozyClientService ); if (confirmed) { /* Cozy customization diff --git a/apps/browser/src/vault/popup/components/vault/cozy-utils.ts b/apps/browser/src/vault/popup/components/vault/cozy-utils.ts index b686bcb8df97..6f8235f1d5c2 100644 --- a/apps/browser/src/vault/popup/components/vault/cozy-utils.ts +++ b/apps/browser/src/vault/popup/components/vault/cozy-utils.ts @@ -6,6 +6,8 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { CozyClientService } from "../../../../popup/services/cozyClient.service"; + /** * Cozy custo * This method is extracted from the jslib: @@ -19,7 +21,8 @@ export const deleteCipher = async ( i18nService: I18nService, platformUtilsService: PlatformUtilsService, cipher: CipherView, - stateService: StateService + stateService: StateService, + cozyClientService: CozyClientService ): Promise => { const organizations = await stateService.getOrganizations(); const [cozyOrganization] = Object.values(organizations).filter((org) => org.name === "Cozy"); diff --git a/apps/browser/src/vault/popup/components/vault/current-tab.component.html b/apps/browser/src/vault/popup/components/vault/current-tab.component.html index 07bd0c20b7d5..053d6f81c0c2 100644 --- a/apps/browser/src/vault/popup/components/vault/current-tab.component.html +++ b/apps/browser/src/vault/popup/components/vault/current-tab.component.html @@ -152,7 +152,10 @@

-
+ + +
+

{{ "identities" | i18n }} + + + -

diff --git a/apps/browser/src/vault/popup/components/vault/view.component.html b/apps/browser/src/vault/popup/components/vault/view.component.html index b6a899d07e8c..fc1e37f85ebc 100644 --- a/apps/browser/src/vault/popup/components/vault/view.component.html +++ b/apps/browser/src/vault/popup/components/vault/view.component.html @@ -776,14 +776,17 @@

+ +