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 @@
+
0">
+
+
+
+
{{ "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 @@