From 0552c2ed7cb42ca723d41f42d1b6a27ab1dc61b8 Mon Sep 17 00:00:00 2001
From: "VL.Y" <1560781+vladimiry@users.noreply.github.com>
Date: Wed, 12 Jul 2023 23:44:07 +0300
Subject: [PATCH] update @ProtonMail web clients

---
 .../{common-4.patch => common-5.patch}        |  40 +++-
 patches/protonmail/common-6.patch             | 185 +++++++++++++++
 .../protonmail/drop-circular-dependency.patch |  49 ----
 patches/protonmail/meta.json                  |  62 ++---
 ...k-api-arg-4.patch => pack-api-arg-5.patch} |   6 +-
 patches/protonmail/pack-webpack-6.patch       |  44 ----
 patches/protonmail/pack-webpack-8.patch       |  51 +++++
 patches/protonmail/pack-webpack-9.patch       |  53 +++++
 patches/protonmail/proton-account.patch       |  25 +-
 patches/protonmail/proton-calendar.patch      |  13 ++
 patches/protonmail/proton-drive.patch         |  23 +-
 patches/protonmail/proton-mail.patch          | 213 ++++++++++--------
 patches/protonmail/proton-vpn-settings.patch  |  13 ++
 .../{sentry-12.patch => sentry-15.patch}      |  79 +------
 patches/protonmail/sentry-16.patch            | 140 ++++++++++++
 .../protonmail/{url-4.patch => url-5.patch}   |  12 +-
 patches/protonmail/url-6.patch                |  90 ++++++++
 scripts/prepare-webclient/webclients.ts       |   1 +
 .../webview/primary/provider-api/index.ts     |   2 +-
 .../webview/primary/provider-api/model.ts     |   2 +-
 src/shared/const/proton-apps.ts               |  10 +-
 21 files changed, 794 insertions(+), 319 deletions(-)
 rename patches/protonmail/{common-4.patch => common-5.patch} (81%)
 create mode 100644 patches/protonmail/common-6.patch
 delete mode 100644 patches/protonmail/drop-circular-dependency.patch
 rename patches/protonmail/{pack-api-arg-4.patch => pack-api-arg-5.patch} (78%)
 delete mode 100644 patches/protonmail/pack-webpack-6.patch
 create mode 100644 patches/protonmail/pack-webpack-8.patch
 create mode 100644 patches/protonmail/pack-webpack-9.patch
 create mode 100644 patches/protonmail/proton-calendar.patch
 create mode 100644 patches/protonmail/proton-vpn-settings.patch
 rename patches/protonmail/{sentry-12.patch => sentry-15.patch} (58%)
 create mode 100644 patches/protonmail/sentry-16.patch
 rename patches/protonmail/{url-4.patch => url-5.patch} (87%)
 create mode 100644 patches/protonmail/url-6.patch

diff --git a/patches/protonmail/common-4.patch b/patches/protonmail/common-5.patch
similarity index 81%
rename from patches/protonmail/common-4.patch
rename to patches/protonmail/common-5.patch
index d6bf243ba..485c128a2 100644
--- a/patches/protonmail/common-4.patch
+++ b/patches/protonmail/common-5.patch
@@ -36,9 +36,9 @@ index 9aaa78a28..f3d24b47c 100644
 +{
 +    const platform = String(navigator.platform);
 +    const userAgents = {
-+        linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
-+        windows: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
-+        macos: "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
++        linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
++        windows: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
++        macos: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
 +    } as const;
 +    uaParser.setUA(
 +        platform.startsWith("Linux")
@@ -107,7 +107,7 @@ index 36bd0c712..c2fb3681c 100644
      }
 
 diff --git a/applications/mail/src/app/hooks/useShowUpsellBanner.ts b/applications/mail/src/app/hooks/useShowUpsellBanner.ts
-index b76334920b..2501a4d4ad 100644
+index d203f913fa..178153c592 100644
 --- a/applications/mail/src/app/hooks/useShowUpsellBanner.ts
 +++ b/applications/mail/src/app/hooks/useShowUpsellBanner.ts
 @@ -33,6 +33,7 @@ const useShowUpsellBanner = (labelID: string, otherBannerDisplayed: boolean) =>
@@ -116,7 +116,7 @@ index b76334920b..2501a4d4ad 100644
       */
 +    /* <electron-mail-mark> */
      const canDisplayUpsellBanner =
-         !user.hasPaidMail &&
+         user.isFree &&
          Date.now() > threeDaysAfterCreationDate &&
 @@ -40,6 +41,7 @@ const useShowUpsellBanner = (labelID: string, otherBannerDisplayed: boolean) =>
          needToShowUpsellBanner.current &&
@@ -138,18 +138,34 @@ index b76334920b..2501a4d4ad 100644
  };
 
 diff --git a/packages/components/containers/heading/PrivateHeader.tsx b/packages/components/containers/heading/PrivateHeader.tsx
-index 399435457d..7cf035baca 100644
+index a7d2452313..73ad8a6828 100644
 --- a/packages/components/containers/heading/PrivateHeader.tsx
 +++ b/packages/components/containers/heading/PrivateHeader.tsx
-@@ -74,7 +74,10 @@ const PrivateHeader = ({
+@@ -49,7 +49,10 @@ const PrivateHeader = ({
+
              <TopNavbar>
                  <TopNavbarList>
-                     {isNarrow && searchDropdown ? <TopNavbarListItem>{searchDropdown}</TopNavbarListItem> : null}
--                    {upsellButton !== undefined ? upsellButton : <TopNavbarUpsell app={app} />}
+-                    {upsellButton !== undefined ? upsellButton : !hideUpsellButton && <TopNavbarUpsell app={app} />}
 +                    {___ELECTRON_MAIL_PROTON_SUPPRESS_UPSELL_ADS_PLACEHOLDER___
 +                        ? null
-+                        : (upsellButton !== undefined ? upsellButton : <TopNavbarUpsell app={app} />)
++                        : (upsellButton !== undefined ? upsellButton : !hideUpsellButton && <TopNavbarUpsell app={app} />)
 +                    }
                      {feedbackButton ? <TopNavbarListItem noShrink>{feedbackButton}</TopNavbarListItem> : null}
-                     {contactsButton ? <TopNavbarListItem noShrink>{contactsButton}</TopNavbarListItem> : null}
-                     {settingsButton ? <TopNavbarListItem noShrink>{settingsButton}</TopNavbarListItem> : null}
+                     {settingsButton ? (
+                         <TopNavbarListItem noShrink className="no-mobile">
+
+diff --git a/packages/components/containers/api/ApiProvider.js b/packages/components/containers/api/ApiProvider.js
+index 3d1b81941c..6ac2f748fa 100644
+--- a/packages/components/containers/api/ApiProvider.js
++++ b/packages/components/containers/api/ApiProvider.js
+@@ -120,7 +120,9 @@ const ApiProvider = ({ config, onLogout, children, UID, noErrorState }) => {
+                             error.cancel = true;
+                             reject(error);
+                         }}
+-                    />
++                    />,
++                    // trying to force single instance, see https://github.com/vladimiry/ElectronMail/issues/621#issuecomment-1627389416
++                    "HumanVerificationModal_ID",
+                 );
+             });
+         };
diff --git a/patches/protonmail/common-6.patch b/patches/protonmail/common-6.patch
new file mode 100644
index 000000000..2d1d90209
--- /dev/null
+++ b/patches/protonmail/common-6.patch
@@ -0,0 +1,185 @@
+diff --git a/packages/components/containers/unleash/UnleashFlagProvider.tsx b/packages/components/containers/unleash/UnleashFlagProvider.tsx
+index c3e007aa4b..f51a74683e 100644
+--- a/packages/components/containers/unleash/UnleashFlagProvider.tsx
++++ b/packages/components/containers/unleash/UnleashFlagProvider.tsx
+@@ -18,7 +18,7 @@ const UnleashFlagProvider = ({ UID, config, children }: Props) => {
+     const authHeaders = UID ? getUIDHeaders(UID) : undefined;
+     const appVersionHeaders = getAppVersionHeaders(clientId, config.APP_VERSION);
+     const unleashConfig: IConfig = {
+-        url: `${window.location.origin}${config.API_URL}/feature/v2/frontend`,
++        url: `${config.API_URL}/feature/v2/frontend`,
+         clientKey: '-', // set by the server
+         appName: '-', // set by the server
+         refreshInterval: 600, // refreshInterval in seconds, 10 mins
+
+diff --git a/packages/pack/scripts/validate.sh b/packages/pack/scripts/validate.sh
+index 1a2ea64..bae388c 100755
+--- a/packages/pack/scripts/validate.sh
++++ b/packages/pack/scripts/validate.sh
+@@ -58,7 +58,7 @@ function main {
+   fi;
+
+   if [ "$hasSourceMap" -eq 0 ]; then
+-    hasError=true;
++    #hasError=true;
+     echo "[error] no SourceMaps found inside the directory: $OUTPUT_DIR";
+   fi;
+
+diff --git a/packages/pack/bin/protonPack.js b/packages/pack/bin/protonPack.js
+index 55715b89d..c87879ad4 100755
+--- a/packages/pack/bin/protonPack.js
++++ b/packages/pack/bin/protonPack.js
+@@ -81,7 +81,7 @@ addGlobalOptions(program.command('build').description('create an optimized produ
+         const outputPath = path.resolve('./dist');
+         await commandWithLog(`rm -rf ${outputPath}`);
+         await commandWithLog(
+-            `${require.resolve('webpack-cli/bin/cli.js')} --progress --output-path=${outputPath} ${webpackArgs}`,
++            `${require.resolve('webpack-cli/bin/cli.js')} --output-path=${outputPath} ${webpackArgs}`,
+             {
+                 stdio: 'inherit',
+             }
+
+diff --git a/packages/shared/lib/helpers/browser.ts b/packages/shared/lib/helpers/browser.ts
+index 9aaa78a28..f3d24b47c 100644
+--- a/packages/shared/lib/helpers/browser.ts
++++ b/packages/shared/lib/helpers/browser.ts
+@@ -1,6 +1,21 @@
+ import UAParser from 'ua-parser-js';
+
+ const uaParser = new UAParser();
++{
++    const platform = String(navigator.platform);
++    const userAgents = {
++        linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
++        windows: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
++        macos: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
++    } as const;
++    uaParser.setUA(
++        platform.startsWith("Linux")
++            ? userAgents.linux
++            : platform.startsWith("Win")
++                ? userAgents.windows
++                : userAgents.macos
++    );
++}
+ const ua = uaParser.getResult();
+
+ export const hasModulesSupport = () => {
+@@ -89,20 +104,10 @@ export const requireDirectAction = () => isSafari() || isFirefox() || isEdge();
+  * @links { https://mathiasbynens.github.io/rel-noopener/}
+  */
+ export const openNewTab = (url: string) => {
+-    if (isIE11()) {
+-        const otherWindow = window.open();
+-        if (!otherWindow) {
+-            return;
+-        }
+-        otherWindow.opener = null;
+-        otherWindow.location.href = url;
+-        return;
+-    }
+-    const anchor = document.createElement('a');
+-
+-    anchor.setAttribute('rel', 'noreferrer nofollow noopener');
+-    anchor.setAttribute('target', '_blank');
+-    anchor.href = url;
+-
+-    return anchor.click();
++    window.dispatchEvent(
++        new CustomEvent(
++            "electron-mail:packages/shared/lib/helpers/browser.ts:openNewTab",
++            {detail: {url}},
++        ),
++    );
+ };
+
+diff --git a/packages/components/components/link/SettingsLink.tsx b/packages/components/components/link/SettingsLink.tsx
+index 5081c4003..cde37c0cb 100644
+--- a/packages/components/components/link/SettingsLink.tsx
++++ b/packages/components/components/link/SettingsLink.tsx
+@@ -48,7 +48,7 @@ const SettingsLink = ({ path, app, children, ...rest }: Props, ref: Ref<HTMLAnch
+             ref={ref}
+             toApp={APPS.PROTONACCOUNT}
+             // If going to settings for the same app
+-            target={canOpenInSameTab(APP_NAME, settingsApp, toSettingsForApp) ? '_self' : '_blank'}
++            target={canOpenInSameTab(APP_NAME, settingsApp, toSettingsForApp) || app === APPS.PROTONVPN_SETTINGS ? '_self' : '_blank'}
+             {...rest}
+         >
+             {children}
+
+diff --git a/packages/components/helpers/earlyAccessDesynchronization.ts b/packages/components/helpers/earlyAccessDesynchronization.ts
+index 36bd0c712..c2fb3681c 100644
+--- a/packages/components/helpers/earlyAccessDesynchronization.ts
++++ b/packages/components/helpers/earlyAccessDesynchronization.ts
+@@ -42,6 +42,7 @@ export const handleEarlyAccessDesynchronization = ({
+     earlyAccessScope: Feature<Environment> | undefined;
+     appName: APP_NAMES;
+ }) => {
++    return;
+     if (doesNotSupportEarlyAccessVersion()) {
+         return;
+     }
+
+diff --git a/applications/mail/src/app/hooks/useShowUpsellBanner.ts b/applications/mail/src/app/hooks/useShowUpsellBanner.ts
+index d203f913fa..178153c592 100644
+--- a/applications/mail/src/app/hooks/useShowUpsellBanner.ts
++++ b/applications/mail/src/app/hooks/useShowUpsellBanner.ts
+@@ -33,6 +33,7 @@ const useShowUpsellBanner = (labelID: string, otherBannerDisplayed: boolean) =>
+     - No other banner is shown in the message list
+     - If a value is found in the localStorage that should trigger a new display
+      */
++    /* <electron-mail-mark> */
+     const canDisplayUpsellBanner =
+         user.isFree &&
+         Date.now() > threeDaysAfterCreationDate &&
+@@ -40,6 +41,7 @@ const useShowUpsellBanner = (labelID: string, otherBannerDisplayed: boolean) =>
+         needToShowUpsellBanner.current &&
+         !otherBannerDisplayed &&
+         showAgain;
++    /* </electron-mail-mark> */
+
+     const handleDismissBanner = () => {
+         // Set the ref to false so that we hide the banner and update the localStorage value
+@@ -72,6 +74,10 @@ const useShowUpsellBanner = (labelID: string, otherBannerDisplayed: boolean) =>
+         }
+     }, []);
+
++    if (___ELECTRON_MAIL_PROTON_SUPPRESS_UPSELL_ADS_PLACEHOLDER___) {
++        return { canDisplayUpsellBanner: false, needToShowUpsellBanner, handleDismissBanner };
++    }
++
+     return { canDisplayUpsellBanner, needToShowUpsellBanner, handleDismissBanner };
+ };
+
+diff --git a/packages/components/containers/heading/PrivateHeader.tsx b/packages/components/containers/heading/PrivateHeader.tsx
+index a7d2452313..73ad8a6828 100644
+--- a/packages/components/containers/heading/PrivateHeader.tsx
++++ b/packages/components/containers/heading/PrivateHeader.tsx
+@@ -49,7 +49,10 @@ const PrivateHeader = ({
+
+             <TopNavbar>
+                 <TopNavbarList>
+-                    {upsellButton !== undefined ? upsellButton : !hideUpsellButton && <TopNavbarUpsell app={app} />}
++                    {___ELECTRON_MAIL_PROTON_SUPPRESS_UPSELL_ADS_PLACEHOLDER___
++                        ? null
++                        : (upsellButton !== undefined ? upsellButton : !hideUpsellButton && <TopNavbarUpsell app={app} />)
++                    }
+                     {feedbackButton ? <TopNavbarListItem noShrink>{feedbackButton}</TopNavbarListItem> : null}
+                     {settingsButton ? (
+                         <TopNavbarListItem noShrink className="no-mobile">
+
+diff --git a/packages/components/containers/api/ApiProvider.js b/packages/components/containers/api/ApiProvider.js
+index 3d1b81941c..6ac2f748fa 100644
+--- a/packages/components/containers/api/ApiProvider.js
++++ b/packages/components/containers/api/ApiProvider.js
+@@ -120,7 +120,9 @@ const ApiProvider = ({ config, onLogout, children, UID, noErrorState }) => {
+                             error.cancel = true;
+                             reject(error);
+                         }}
+-                    />
++                    />,
++                    // trying to force single instance, see https://github.com/vladimiry/ElectronMail/issues/621#issuecomment-1627389416
++                    "HumanVerificationModal_ID",
+                 );
+             });
+         };
diff --git a/patches/protonmail/drop-circular-dependency.patch b/patches/protonmail/drop-circular-dependency.patch
deleted file mode 100644
index 4f983178c..000000000
--- a/patches/protonmail/drop-circular-dependency.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-diff --git a/packages/components/hooks/usePendingUserInvitations.ts b/packages/components/hooks/usePendingUserInvitations.ts
-index 6044196e1..b35ae94f5 100644
---- a/packages/components/hooks/usePendingUserInvitations.ts
-+++ b/packages/components/hooks/usePendingUserInvitations.ts
-@@ -2,11 +2,11 @@ import { useCallback } from 'react';
-
- import { getInvitations } from '@proton/shared/lib/api/user';
- import { Api, PendingInvitation as PendingUserInvitation } from '@proton/shared/lib/interfaces';
--import { UserInvitationModel } from '@proton/shared/lib/models';
-
- import useApi from './useApi';
- import useCache from './useCache';
- import useCachedModelResult from './useCachedModelResult';
-+import { UserInvitationModelKey } from '@proton/shared/lib/models/userInvitationModel.key';
-
- export const fetchPendingUserInvitations = (api: Api) =>
-     api<{ UserInvitations: PendingUserInvitation[] }>(getInvitations()).then(({ UserInvitations }) => {
-@@ -18,7 +18,7 @@ const usePendingUserInvitations = (): [PendingUserInvitation[] | undefined, bool
-     const cache = useCache();
-
-     const miss = useCallback(() => fetchPendingUserInvitations(api), [api]);
--    return useCachedModelResult(cache, UserInvitationModel.key, miss);
-+    return useCachedModelResult(cache, UserInvitationModelKey, miss);
- };
-
- export default usePendingUserInvitations;
-diff --git a/packages/shared/lib/models/userInvitationModel.js b/packages/shared/lib/models/userInvitationModel.js
-index 1fa9690b7..a9035e06d 100644
---- a/packages/shared/lib/models/userInvitationModel.js
-+++ b/packages/shared/lib/models/userInvitationModel.js
-@@ -1,9 +1,10 @@
- import { fetchPendingUserInvitations } from '@proton/components/hooks/usePendingUserInvitations';
-
- import updateCollection from '../helpers/updateCollection';
-+import { UserInvitationModelKey as key } from './userInvitationModel.key';
-
- export const UserInvitationModel = {
--    key: 'UserInvitations',
-+    key,
-     get: fetchPendingUserInvitations,
-     update: (model, events) => updateCollection({ model, events, itemKey: 'UserInvitation' }),
- };
-diff --git a/packages/shared/lib/models/userInvitationModel.key.js b/packages/shared/lib/models/userInvitationModel.key.js
-new file mode 100644
-index 000000000..c07ff03fa
---- /dev/null
-+++ b/packages/shared/lib/models/userInvitationModel.key.js
-@@ -0,0 +1 @@
-+export const UserInvitationModelKey = 'UserInvitations';
diff --git a/patches/protonmail/meta.json b/patches/protonmail/meta.json
index fdc76b60c..8277c07a0 100644
--- a/patches/protonmail/meta.json
+++ b/patches/protonmail/meta.json
@@ -1,65 +1,67 @@
 {
   "proton-mail": [
-    "common-4.patch",
-    "drop-circular-dependency.patch",
-    "url-4.patch",
+    "common-5.patch",
+    "drop-circular-dependency-2.patch",
+    "url-5.patch",
     "constants-10.patch",
-    "sentry-12.patch",
-    "pack-api-arg-4.patch",
-    "pack-webpack-6.patch",
+    "sentry-15.patch",
+    "pack-api-arg-5.patch",
+    "pack-webpack-8.patch",
     "session-storage-5.patch",
     "link-handler-7.patch",
     "embedded-verification-3.patch",
     "proton-mail.patch"
   ],
   "proton-account": [
-    "common-4.patch",
+    "common-6.patch",
     "drop-circular-dependency-2.patch",
-    "url-4.patch",
+    "url-6.patch",
     "constants-10.patch",
-    "sentry-12.patch",
-    "pack-api-arg-4.patch",
-    "pack-webpack-6.patch",
+    "sentry-16.patch",
+    "pack-api-arg-5.patch",
+    "pack-webpack-9.patch",
     "session-storage-5.patch",
     "link-handler-7.patch",
     "embedded-verification-3.patch",
     "proton-account.patch"
   ],
   "proton-calendar": [
-    "common-4.patch",
-    "drop-circular-dependency.patch",
-    "url-4.patch",
+    "common-5.patch",
+    "drop-circular-dependency-2.patch",
+    "url-5.patch",
     "constants-10.patch",
-    "sentry-12.patch",
-    "pack-api-arg-4.patch",
-    "pack-webpack-6.patch",
+    "sentry-15.patch",
+    "pack-api-arg-5.patch",
+    "pack-webpack-8.patch",
     "session-storage-5.patch",
     "link-handler-7.patch",
-    "embedded-verification-3.patch"
+    "embedded-verification-3.patch",
+    "proton-calendar.patch"
   ],
   "proton-drive": [
-    "common-4.patch",
-    "drop-circular-dependency.patch",
-    "url-4.patch",
+    "common-6.patch",
+    "drop-circular-dependency-2.patch",
+    "url-6.patch",
     "constants-10.patch",
-    "sentry-12.patch",
-    "pack-api-arg-4.patch",
-    "pack-webpack-6.patch",
+    "sentry-15.patch",
+    "pack-api-arg-5.patch",
+    "pack-webpack-8.patch",
     "session-storage-5.patch",
     "link-handler-7.patch",
     "embedded-verification-3.patch",
     "proton-drive.patch"
   ],
   "proton-vpn-settings": [
-    "common-4.patch",
+    "common-6.patch",
     "drop-circular-dependency-2.patch",
-    "url-4.patch",
+    "url-6.patch",
     "constants-10.patch",
-    "sentry-12.patch",
-    "pack-api-arg-4.patch",
-    "pack-webpack-6.patch",
+    "sentry-16.patch",
+    "pack-api-arg-5.patch",
+    "pack-webpack-9.patch",
     "session-storage-5.patch",
     "link-handler-7.patch",
-    "embedded-verification-3.patch"
+    "embedded-verification-3.patch",
+    "proton-vpn-settings.patch"
   ]
 }
diff --git a/patches/protonmail/pack-api-arg-4.patch b/patches/protonmail/pack-api-arg-5.patch
similarity index 78%
rename from patches/protonmail/pack-api-arg-4.patch
rename to patches/protonmail/pack-api-arg-5.patch
index 3a567ba8a..3eb65783d 100644
--- a/patches/protonmail/pack-api-arg-4.patch
+++ b/patches/protonmail/pack-api-arg-5.patch
@@ -1,13 +1,13 @@
 diff --git a/packages/pack/lib/config.js b/packages/pack/lib/config.js
-index b6484f719..a8ebc7fc0 100644
+index 5bb7fe50df..ff19c4f032 100644
 --- a/packages/pack/lib/config.js
 +++ b/packages/pack/lib/config.js
-@@ -129,7 +129,7 @@ const getConfigFile = ({ buildData, appData }) => {
+@@ -131,7 +131,7 @@ const getConfigFile = ({ buildData, appData }) => {
      export const BRANCH = '${buildData.branch}';
      export const DATE_VERSION = '${buildData.date}';
      export const APP_NAME = '${appData.appName}';
 -    export const API_URL =  '${(!appData.apiProxy && appData.api) || '/api'}';
 +    export const API_URL = '${appData.api}';
+     export const SSO_URL = '${appData.sso || ''}';
      export const LOCALES = ${JSON.stringify(LOCALES)};
      export const VERSION_PATH = '${appData.publicPath}assets/version.json';
-     export const SENTRY_DSN = '${appData.sentryDsn}';
diff --git a/patches/protonmail/pack-webpack-6.patch b/patches/protonmail/pack-webpack-6.patch
deleted file mode 100644
index 6b0f2c9b4..000000000
--- a/patches/protonmail/pack-webpack-6.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-diff --git a/packages/pack/webpack.config.js b/packages/pack/webpack.config.js
-index 9fdd56f70..3a7300257 100644
---- a/packages/pack/webpack.config.js
-+++ b/packages/pack/webpack.config.js
-@@ -15,7 +15,7 @@ const getConfig = (env) => {
-         api: env.api,
-         appMode: env.appMode || 'standalone',
-         featureFlags: env.featureFlags || '',
--        writeSRI: env.writeSri !== 'false',
-+        writeSRI: false,
-         browserslist: isProduction
-             ? `> 0.5%, not IE 11, Firefox ESR, Safari 11`
-             : 'last 1 chrome version, last 1 firefox version, last 1 safari version',
-@@ -33,7 +33,20 @@ const getConfig = (env) => {
-         logical: env.logical || false,
-     };
-
--    return {
-+    return (() => {
-+        const file = path.resolve("./proton.config.js");
-+        if (require("fs").existsSync(file)) {
-+            console.log(
-+                /*reset:*/"\x1b[0m" +
-+                /*yellow:*/"\x1b[33m" +
-+                ">>>" +
-+                /*reset:*/"\x1b[0m", +
-+                `Found ${file}, extend the config`,
-+            )
-+            return require(file);
-+        }
-+        return (value) => value;
-+    })()({
-         target: `browserslist:${options.browserslist}`,
-         mode: options.isProduction ? 'production' : 'development',
-         bail: options.isProduction,
-@@ -120,7 +133,7 @@ const getConfig = (env) => {
-                 },
-             }),
-         },
--    };
-+    });
- };
-
- module.exports = getConfig;
diff --git a/patches/protonmail/pack-webpack-8.patch b/patches/protonmail/pack-webpack-8.patch
new file mode 100644
index 000000000..436039980
--- /dev/null
+++ b/patches/protonmail/pack-webpack-8.patch
@@ -0,0 +1,51 @@
+diff --git a/packages/pack/webpack.config.ts b/packages/pack/webpack.config.ts
+index 184720c4f9..e729826f79 100644
+--- a/packages/pack/webpack.config.ts
++++ b/packages/pack/webpack.config.ts
+@@ -1,4 +1,5 @@
+ import path from 'path';
++import fs from 'fs';
+ import webpack from 'webpack';
+ import 'webpack-dev-server';
+ // @ts-ignore
+@@ -23,7 +24,7 @@ const getConfig = (env: any): webpack.Configuration => {
+         api: env.api,
+         appMode: env.appMode || 'standalone',
+         featureFlags: env.featureFlags || '',
+-        writeSRI: env.writeSri !== 'false',
++        writeSRI: false,
+         browserslist: isProduction
+             ? `> 0.5%, not IE 11, Firefox ESR, Safari 11`
+             : 'last 1 chrome version, last 1 firefox version, last 1 safari version',
+@@ -44,7 +45,20 @@ const getConfig = (env: any): webpack.Configuration => {
+
+     const version = options.buildData.version;
+
+-    return {
++    return (() => {
++        const file = path.resolve("./proton.config.js");
++        if (fs.existsSync(file)) {
++            console.log(
++                /*reset:*/"\x1b[0m" +
++                /*yellow:*/"\x1b[33m" +
++                ">>>" +
++                /*reset:*/"\x1b[0m", +
++                    `Found ${file}, extend the config`,
++            )
++            return eval("require")(file);
++        }
++        return (value: webpack.Configuration) => value;
++    })()({
+         target: `browserslist:${options.browserslist}`,
+         mode: isProduction ? 'production' : 'development',
+         bail: isProduction,
+@@ -162,7 +176,7 @@ const getConfig = (env: any): webpack.Configuration => {
+                 ],
+             }),
+         },
+-    };
++    });
+ };
+
+ export default getConfig;
+--
diff --git a/patches/protonmail/pack-webpack-9.patch b/patches/protonmail/pack-webpack-9.patch
new file mode 100644
index 000000000..49ca2df34
--- /dev/null
+++ b/patches/protonmail/pack-webpack-9.patch
@@ -0,0 +1,53 @@
+diff --git a/packages/pack/webpack.config.ts b/packages/pack/webpack.config.ts
+index 3db6a71ef1..ed58652d81 100644
+--- a/packages/pack/webpack.config.ts
++++ b/packages/pack/webpack.config.ts
+@@ -3,6 +3,7 @@ import { Configuration } from 'webpack';
+ import 'webpack-dev-server';
+ // @ts-ignore
+ import { parseResource } from 'webpack/lib/util/identifier';
++import fs from 'fs';
+
+ const { getJsLoaders } = require('./webpack/js.loader');
+ const getCssLoaders = require('./webpack/css.loader');
+@@ -23,7 +24,7 @@ const getConfig = (env: any): Configuration => {
+         api: env.api,
+         appMode: env.appMode || 'standalone',
+         featureFlags: env.featureFlags || '',
+-        writeSRI: env.writeSri !== 'false',
++        writeSRI: false,
+         browserslist: isProduction
+             ? `> 0.5%, not IE 11, Firefox ESR, Safari 11`
+             : 'last 1 chrome version, last 1 firefox version, last 1 safari version',
+@@ -44,7 +45,20 @@ const getConfig = (env: any): Configuration => {
+
+     const version = options.buildData.version;
+
+-    return {
++    return ((): (value: Configuration) => Configuration => {
++        const file = path.resolve("./proton.config.js");
++        if (fs.existsSync(file)) {
++            console.log(
++                /*reset:*/"\x1b[0m" +
++                /*yellow:*/"\x1b[33m" +
++                ">>>" +
++                /*reset:*/"\x1b[0m", +
++                    `Found ${file}, extend the config`,
++            )
++            return eval("require")(file);
++        }
++        return (value: Configuration) => value;
++    })()({
+         target: `browserslist:${options.browserslist}`,
+         mode: isProduction ? 'production' : 'development',
+         bail: isProduction,
+@@ -162,7 +176,7 @@ const getConfig = (env: any): Configuration => {
+                 ],
+             }),
+         },
+-    };
++    });
+ };
+
+ export default getConfig;
+
diff --git a/patches/protonmail/proton-account.patch b/patches/protonmail/proton-account.patch
index 2046bee72..2c4dd3f73 100644
--- a/patches/protonmail/proton-account.patch
+++ b/patches/protonmail/proton-account.patch
@@ -1,3 +1,16 @@
+diff --git a/applications/account/package.json b/applications/account/package.json
+index 3c5c6daff9..25828bb6b0 100644
+--- a/applications/account/package.json
++++ b/applications/account/package.json
+@@ -6,7 +6,6 @@
+     "author": "",
+     "main": "index.js",
+     "scripts": {
+-        "build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" proton-pack build --appMode=sso",
+         "check-types": "tsc",
+         "i18n:upgrade": "proton-i18n extract --verbose && proton-i18n crowdin --verbose",
+         "i18n:validate": "proton-i18n validate lint-functions",
+
 diff --git a/applications/account/src/app/Setup.tsx b/applications/account/src/app/Setup.tsx
 index 8f5e56ef0..a40d9dc17 100644
 --- a/applications/account/src/app/Setup.tsx
@@ -38,15 +51,15 @@ index 8f5e56ef0..a40d9dc17 100644
  export default Setup;
 
 diff --git a/applications/account/src/app/content/MainContainer.tsx b/applications/account/src/app/content/MainContainer.tsx
-index 32a57aa6d..472a7c9c9 100644
+index 575b59dcec..5e774e01a1 100644
 --- a/applications/account/src/app/content/MainContainer.tsx
 +++ b/applications/account/src/app/content/MainContainer.tsx
 @@ -132,7 +132,7 @@ const MainContainer = () => {
+     const loadingFeatures = featuresFlags.some(({ loading }) => loading) || loadingDataRecovery;
+     const recoveryNotification = useRecoveryNotification(false);
 
-     useDeviceRecovery();
-
--    const app = getAppFromPathnameSafe(location.pathname) || DEFAULT_APP;
-+    const app = getAppFromPathnameSafe(window.location.pathname) || DEFAULT_APP;
+-    const appFromPathname = getAppFromPathnameSafe(location.pathname);
++    const appFromPathname = getAppFromPathnameSafe(window.location.pathname);
+     const app = appFromPathname || getToApp(undefined, user);
      const appSlug = getSlugFromApp(app);
 
-     /*
diff --git a/patches/protonmail/proton-calendar.patch b/patches/protonmail/proton-calendar.patch
new file mode 100644
index 000000000..ad43fc44f
--- /dev/null
+++ b/patches/protonmail/proton-calendar.patch
@@ -0,0 +1,13 @@
+diff --git a/applications/calendar/package.json b/applications/calendar/package.json
+index 6d44ef4129..8ff11b06cf 100644
+--- a/applications/calendar/package.json
++++ b/applications/calendar/package.json
+@@ -6,7 +6,6 @@
+     "author": "",
+     "main": "index.js",
+     "scripts": {
+-        "build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" proton-pack build --appMode=sso",
+         "check-types": "tsc",
+         "i18n:upgrade": "proton-i18n extract --verbose && proton-i18n crowdin --verbose",
+         "i18n:validate": "proton-i18n validate lint-functions",
+
diff --git a/patches/protonmail/proton-drive.patch b/patches/protonmail/proton-drive.patch
index 7808c17a7..f7eb1d749 100644
--- a/patches/protonmail/proton-drive.patch
+++ b/patches/protonmail/proton-drive.patch
@@ -1,3 +1,16 @@
+diff --git a/applications/drive/package.json b/applications/drive/package.json
+index 1ff78a9928..d232854a68 100644
+--- a/applications/drive/package.json
++++ b/applications/drive/package.json
+@@ -5,7 +5,6 @@
+     "author": "",
+     "main": "index.ts",
+     "scripts": {
+-        "build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" proton-pack build --appMode=sso",
+         "check-types": "tsc",
+         "i18n:getlatest": "proton-i18n upgrade",
+         "i18n:upgrade": "proton-i18n extract --verbose && proton-i18n crowdin -u --verbose",
+
 diff --git a/applications/drive/src/app/store/_downloads/fileSaver/download.ts b/applications/drive/src/app/store/_downloads/fileSaver/download.ts
 index 7bef98525..0650e8019 100644
 --- a/applications/drive/src/app/store/_downloads/fileSaver/download.ts
@@ -14,14 +27,14 @@ index 7bef98525..0650e8019 100644
  // initialize the download in the browser. The response has headers to
 
 diff --git a/applications/drive/src/app/store/_shares/shareUrl.ts b/applications/drive/src/app/store/_shares/shareUrl.ts
-index 733cf9242..ab5781793 100644
+index 76b907e2a1..a267a2d50c 100644
 --- a/applications/drive/src/app/store/_shares/shareUrl.ts
 +++ b/applications/drive/src/app/store/_shares/shareUrl.ts
-@@ -40,6 +40,6 @@ export const getSharedLink = (sharedURL?: {
+@@ -36,6 +36,6 @@ export const getSharedLink = (sharedURL?: {
 
-     const [generatedPassword] = splitGeneratedAndCustomPassword(sharedURL.Password, sharedURL);
+     const [generatedPassword] = splitGeneratedAndCustomPassword(sharedURL.password, sharedURL);
 
--    const url = sharedURL.PublicUrl ? sharedURL.PublicUrl : `${window.location.origin}/urls/${sharedURL.Token}`;
-+    const url = sharedURL.PublicUrl ? sharedURL.PublicUrl : `https://drive.protonmail.com/urls/${sharedURL.Token}`;
+-    const url = sharedURL.publicUrl ? sharedURL.publicUrl : `${window.location.origin}/urls/${sharedURL.token}`;
++    const url = sharedURL.publicUrl ? sharedURL.publicUrl : `https://drive.protonmail.com/urls/${sharedURL.token}`;
      return `${url}${generatedPassword !== '' ? `#${generatedPassword}` : ''}`;
  };
diff --git a/patches/protonmail/proton-mail.patch b/patches/protonmail/proton-mail.patch
index 310b683c8..1b9f960ee 100644
--- a/patches/protonmail/proton-mail.patch
+++ b/patches/protonmail/proton-mail.patch
@@ -1,3 +1,16 @@
+diff --git a/applications/mail/package.json b/applications/mail/package.json
+index 8087743d3a..ce4cf05ff1 100644
+--- a/applications/mail/package.json
++++ b/applications/mail/package.json
+@@ -6,7 +6,6 @@
+     "author": "",
+     "main": "index.js",
+     "scripts": {
+-        "build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" proton-pack build --appMode=sso",
+         "check-types": "tsc",
+         "i18n:getlatest": "proton-i18n upgrade",
+         "i18n:upgrade": "proton-i18n extract --verbose && proton-i18n crowdin -u --verbose",
+
 diff --git a/packages/shared/lib/api/events.ts b/packages/shared/lib/api/events.ts
 index 519a50349..819bdb911 100644
 --- a/packages/shared/lib/api/events.ts
@@ -15,17 +28,17 @@ index 519a50349..819bdb911 100644
  });
 
 diff --git a/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx b/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx
-index 472046bb0..a818a7d21 100644
+index bbb73f6bc9..584e9324be 100644
 --- a/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx
 +++ b/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx
-@@ -304,6 +304,7 @@ const MailboxContainer = ({
+@@ -470,6 +470,7 @@ const MailboxContainer = ({
                  tabIndex={-1}
                  className="flex-item-fluid flex flex-column flex-nowrap outline-none"
                  data-testid="mailbox"
 +                electron-mail-mailbox-container-component
              >
-                 {showToolbar && (
-                     <Toolbar
+                 <MailHeader
+                     breakpoints={breakpoints}
 
 diff --git a/applications/mail/src/app/helpers/attachment/attachmentLoader.ts b/applications/mail/src/app/helpers/attachment/attachmentLoader.ts
 index c7d094d1b..3667af1eb 100644
@@ -86,10 +99,10 @@ index acfa71607..216229b0f 100644
   * Verify the extracted `signature` of a decryption result against its `decryptedRawContent`
 
 diff --git a/applications/mail/src/app/logic/messages/messagesTypes.ts b/applications/mail/src/app/logic/messages/messagesTypes.ts
-index 0531de00e..665b8d087 100644
+index 29d48870ab..fc19436f55 100644
 --- a/applications/mail/src/app/logic/messages/messagesTypes.ts
 +++ b/applications/mail/src/app/logic/messages/messagesTypes.ts
-@@ -44,6 +44,7 @@ export interface MessageVerification {
+@@ -49,20 +49,24 @@ export interface MessageVerification {
       */
      verificationErrors: Error[] | undefined;
 
@@ -97,10 +110,19 @@ index 0531de00e..665b8d087 100644
      /**
       * Pinned public keys of the sender which can verify, if any
       */
-@@ -58,6 +59,7 @@ export interface MessageVerification {
+     senderPinnedKeys: PublicKeyReference[] | undefined;
++    /* </electron-mail-mark> */
+
+     /**
+      * Sender public keys retrieved from API which can are not pinned
+      */
+     senderPinnableKeys: PublicKeyReference[] | undefined;
+
++    /* <electron-mail-mark> */
+     /**
       * If the sender is in the list of contacts, whether its contact signature has been verified
       */
-     senderVerified: boolean | undefined;
+     pinnedKeysVerified: boolean | undefined;
 +    /* </electron-mail-mark> */
 
      /**
@@ -231,80 +253,6 @@ index 35d7a0aba..352f1c8a3 100644
 
  export default useCache;
 
-diff --git a/applications/mail/src/app/App.tsx b/applications/mail/src/app/App.tsx
-index 50ae8f9a3..c79b724ee 100644
---- a/applications/mail/src/app/App.tsx
-+++ b/applications/mail/src/app/App.tsx
-@@ -22,11 +22,6 @@ newVersionUpdater(config);
- sentry({ config, uid: authentication.getUID(), sessionTracking: getSessionTrackingEnabled() });
- setVcalProdId(getProdId(config));
-
--// If the browser is Chromium based, register automatically the mailto protocol handler
--if ('chrome' in window) {
--    registerMailToProtocolHandler();
--}
--
- const App = () => {
-     const [hasInitialAuth] = useState(() => {
-         return !window.location.pathname.startsWith(G_OAUTH_REDIRECT_PATH);
-
-diff --git a/applications/mail/src/app/EOApp.tsx b/applications/mail/src/app/EOApp.tsx
-index fa8ab5f17..2c5bd2d2c 100644
---- a/applications/mail/src/app/EOApp.tsx
-+++ b/applications/mail/src/app/EOApp.tsx
-@@ -36,11 +36,6 @@ newVersionUpdater(config);
- sentry({ config, sessionTracking: getSessionTrackingEnabled() });
- setVcalProdId(getProdId(config));
-
--// If the browser is Chromium based, register automatically the mailto protocol handler
--if ('chrome' in window) {
--    registerMailToProtocolHandler();
--}
--
- const App = () => {
-     const cacheRef = useRef<Cache<string, any>>();
-     if (!cacheRef.current) {
-
-diff --git a/applications/mail/src/app/components/header/MailDefaultHandlerModal.tsx b/applications/mail/src/app/components/header/MailDefaultHandlerModal.tsx
-index bdabb99e0..a56b8cdcb 100644
---- a/applications/mail/src/app/components/header/MailDefaultHandlerModal.tsx
-+++ b/applications/mail/src/app/components/header/MailDefaultHandlerModal.tsx
-@@ -4,13 +4,10 @@ import { Href } from '@proton/components/components/link';
- import { MAIL_APP_NAME } from '@proton/shared/lib/constants';
- import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
-
--import { registerMailToProtocolHandler } from '../../helpers/url';
--
- const MailDefaultHandlerModal = (props: ModalProps) => {
-     const { onClose } = props;
-
-     const handleAskForPermission = () => {
--        registerMailToProtocolHandler();
-
-         onClose?.();
-     };
-
-diff --git a/applications/mail/src/app/components/header/MailHeader.tsx b/applications/mail/src/app/components/header/MailHeader.tsx
-index d86dc9880..e75180b46 100644
---- a/applications/mail/src/app/components/header/MailHeader.tsx
-+++ b/applications/mail/src/app/components/header/MailHeader.tsx
-@@ -38,7 +38,6 @@ import { setParamsInUrl } from '../../helpers/mailboxUrl';
- import { Breakpoints } from '../../models/utils';
- import MailOnboardingModal from '../onboarding/MailOnboardingModal';
- import ClearBrowserDataModal from './ClearBrowserDataModal';
--import MailDefaultHandlerModal from './MailDefaultHandlerModal';
- import MailSearch from './search/MailSearch';
-
- interface Props {
-@@ -192,7 +191,6 @@ const MailHeader = ({ labelID, elementID, breakpoints, expanded, onToggleExpand
-             <MailViewLayoutModal {...mailViewLayoutProps} />
-             <MailDensityModal {...mailDensityProps} />
-             <MailComposerModeModal {...mailComposerModeProps} />
--            <MailDefaultHandlerModal {...mailDefaultHandlerProps} />
-             <ClearBrowserDataModal {...clearBrowserDataProps} />
-             <RebrandingFeedbackModal {...feedbackModalProps} />
-         </>
-
 diff --git a/applications/mail/src/app/helpers/url.ts b/applications/mail/src/app/helpers/url.ts
 index 2aaa779d3..487f54ff3 100644
 --- a/applications/mail/src/app/helpers/url.ts
@@ -328,22 +276,107 @@ index 2aaa779d3..487f54ff3 100644
 +    // NOOP
  };
 
+diff --git a/applications/mail/src/app/components/drawer/MailQuickSettings.tsx b/applications/mail/src/app/components/drawer/MailQuickSettings.tsx
+index c0a72173cb..5baba25085 100644
+--- a/applications/mail/src/app/components/drawer/MailQuickSettings.tsx
++++ b/applications/mail/src/app/components/drawer/MailQuickSettings.tsx
+@@ -31,7 +31,6 @@ import { useGetStartedChecklist } from 'proton-mail/containers/onboardingCheckli
+
+ import { useEncryptedSearchContext } from '../../containers/EncryptedSearchProvider';
+ import ClearBrowserDataModal from '../header/ClearBrowserDataModal';
+-import MailDefaultHandlerModal from '../header/MailDefaultHandlerModal';
+
+ interface QuickSettingsSelectOption {
+     value: any;
+@@ -328,7 +327,6 @@ const MailQuickSettings = () => {
+                 )}
+             </QuickSettingsButtonSection>
+
+-            <MailDefaultHandlerModal {...mailDefaultHandlerProps} />
+             <ClearBrowserDataModal {...clearBrowserDataProps} />
+             <MailShortcutsModal {...mailShortcutsProps} />
+             <KeyTransparencyDetailsModal {...keyTransparencyDetailsModalProps} />
+
+diff --git a/applications/mail/src/app/components/header/MailDefaultHandlerModal.tsx b/applications/mail/src/app/components/header/MailDefaultHandlerModal.tsx
+deleted file mode 100644
+index 0d29745777..0000000000
+--- a/applications/mail/src/app/components/header/MailDefaultHandlerModal.tsx
++++ /dev/null
+@@ -1,41 +0,0 @@
+-import { c } from 'ttag';
+-
+-import { Button, Href } from '@proton/atoms';
+-import { ModalProps, Prompt } from '@proton/components';
+-import { MAIL_APP_NAME } from '@proton/shared/lib/constants';
+-import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
+-
+-import { registerMailToProtocolHandler } from '../../helpers/url';
+-
+-const MailDefaultHandlerModal = (props: ModalProps) => {
+-    const { onClose } = props;
+-
+-    const handleAskForPermission = () => {
+-        registerMailToProtocolHandler();
+-
+-        onClose?.();
+-    };
+-
+-    return (
+-        <Prompt
+-            title={c('Info').t`Default email application`}
+-            buttons={[
+-                <Button color="norm" onClick={handleAskForPermission}>{c('Action').t`Set as default`}</Button>,
+-                <Button onClick={onClose}>{c('Action').t`Cancel`}</Button>,
+-            ]}
+-            {...props}
+-        >
+-            <span>{c('Info')
+-                .t`Set ${MAIL_APP_NAME} as your default email application for this browser. ${MAIL_APP_NAME} will open automatically when you click an email link.`}</span>
+-            <Href
+-                className="ml-2"
+-                href={getKnowledgeBaseUrl('/set-default-email-handler')}
+-                title="Default mail handler"
+-            >
+-                {c('Info').t`Learn more`}
+-            </Href>
+-        </Prompt>
+-    );
+-};
+-
+-export default MailDefaultHandlerModal;
+
 diff --git a/applications/mail/src/app/components/header/search/MailSearch.tsx b/applications/mail/src/app/components/header/search/MailSearch.tsx
-index 6318e40be..ebbce0086 100644
+index 1736085ad3..5f102e159e 100644
 --- a/applications/mail/src/app/components/header/search/MailSearch.tsx
 +++ b/applications/mail/src/app/components/header/search/MailSearch.tsx
-@@ -11,10 +11,7 @@ import {
+@@ -3,7 +3,6 @@ import { useEffect, useState } from 'react';
+ import { Location } from 'history';
+
+ import {
+-    FeatureCode,
+     TopNavbarListItemSearchButton,
+     generateUID,
+     useAddresses,
+@@ -11,14 +10,11 @@ import {
+     useLabels,
      useMailSettings,
      usePopperAnchor,
+-    useProgressiveRollout,
      useToggle,
 -    useUser,
  } from '@proton/components';
--import { isMobile } from '@proton/shared/lib/helpers/browser';
--import { isPaid } from '@proton/shared/lib/user/helpers';
 
  import { ADVANCED_SEARCH_OVERLAY_CLOSE_EVENT } from '../../../constants';
  import { useEncryptedSearchContext } from '../../../containers/EncryptedSearchProvider';
-@@ -40,7 +37,6 @@ const MailSearch = ({ breakpoints, labelID, location }: Props) => {
+-import { isEncryptedSearchAvailable } from '../../../helpers/encryptedSearch/esUtils';
+ import { extractSearchParameters } from '../../../helpers/mailboxUrl';
+ import { useClickMailContent } from '../../../hooks/useClickMailContent';
+ import { Breakpoints } from '../../../models/utils';
+@@ -39,11 +35,9 @@ interface Props {
+
+ const MailSearch = ({ breakpoints, labelID, location, columnMode }: Props) => {
+     const [uid] = useState(generateUID('advanced-search-overlay'));
+-    const isESUserInterfaceAvailable = useProgressiveRollout(FeatureCode.ESUserInterface);
      const { anchorRef, isOpen, open, close } = usePopperAnchor<HTMLInputElement>();
      const searchParams = extractSearchParameters(location);
      const [searchInputValue, setSearchInputValue] = useState(searchParams.keyword || '');
@@ -351,11 +384,11 @@ index 6318e40be..ebbce0086 100644
      const [, loadingMailSettings] = useMailSettings();
      const [, loadingLabels] = useLabels();
      const [, loadingFolders] = useFolders();
-@@ -49,7 +45,7 @@ const MailSearch = ({ breakpoints, labelID, location }: Props) => {
+@@ -51,7 +45,7 @@ const MailSearch = ({ breakpoints, labelID, location, columnMode }: Props) => {
+     const { getESDBStatus, cacheIndexedDB, closeDropdown } = useEncryptedSearchContext();
      const { dropdownOpened } = getESDBStatus();
      const esState = useEncryptedSearchToggleState(isOpen);
-
--    const showEncryptedSearch = !isMobile() && !!isPaid(user);
+-    const showEncryptedSearch = isEncryptedSearchAvailable(user, isESUserInterfaceAvailable);
 +    const showEncryptedSearch = false;
 
      // Show more from inside AdvancedSearch to persist the state when the overlay is closed
diff --git a/patches/protonmail/proton-vpn-settings.patch b/patches/protonmail/proton-vpn-settings.patch
new file mode 100644
index 000000000..ea0b42b05
--- /dev/null
+++ b/patches/protonmail/proton-vpn-settings.patch
@@ -0,0 +1,13 @@
+diff --git a/applications/vpn-settings/package.json b/applications/vpn-settings/package.json
+index 2232dbdee5..b12e610cf8 100644
+--- a/applications/vpn-settings/package.json
++++ b/applications/vpn-settings/package.json
+@@ -6,7 +6,6 @@
+     "author": "",
+     "main": "index.js",
+     "scripts": {
+-        "build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" proton-pack build --appMode=standalone --logical",
+         "check-types": "tsc",
+         "i18n:upgrade": "proton-i18n extract --verbose && proton-i18n crowdin --verbose",
+         "i18n:validate": "proton-i18n validate lint-functions",
+
diff --git a/patches/protonmail/sentry-12.patch b/patches/protonmail/sentry-15.patch
similarity index 58%
rename from patches/protonmail/sentry-12.patch
rename to patches/protonmail/sentry-15.patch
index 8a8a8e851..a9b59f619 100644
--- a/patches/protonmail/sentry-12.patch
+++ b/patches/protonmail/sentry-15.patch
@@ -1,9 +1,10 @@
 diff --git a/packages/shared/lib/helpers/sentry.ts b/packages/shared/lib/helpers/sentry.ts
-index 731fc8566..6d6a3fb2a 100644
+index 503abd48a4..296afc18f4 100644
 --- a/packages/shared/lib/helpers/sentry.ts
 +++ b/packages/shared/lib/helpers/sentry.ts
-@@ -1,14 +1,9 @@
+@@ -1,15 +1,10 @@
  import {
+     BrowserOptions,
      captureException,
 -    configureScope,
 -    init,
@@ -17,7 +18,7 @@ index 731fc8566..6d6a3fb2a 100644
  import { getUIDHeaders } from '../fetch/headers';
  import { ProtonConfig } from '../interfaces';
 
-@@ -61,21 +56,6 @@ export const getContentTypeHeaders = (input: RequestInfo | URL): HeadersInit =>
+@@ -67,21 +62,6 @@ export const getContentTypeHeaders = (input: RequestInfo | URL): HeadersInit =>
      return {};
  };
 
@@ -39,10 +40,10 @@ index 731fc8566..6d6a3fb2a 100644
  const isLocalhost = (host: string) => host.startsWith('localhost');
  const isProduction = (host: string) => host.endsWith('.proton.me') || host === VPN_HOSTNAME;
 
-@@ -94,143 +74,10 @@ function main({
-     sessionTracking = false,
-     sentryConfig = getDefaultSentryConfig(config),
+@@ -170,86 +150,10 @@ function main({
      ignore = ({ host }) => isLocalhost(host),
+     denyUrls = getDefaultDenyUrls(),
+     ignoreErrors = getDefaultIgnoreErrors(),
 -}: SentryOptions) {
 -    const { SENTRY_DSN, APP_VERSION } = config;
 -    const { host, release, environment } = sentryConfig;
@@ -77,8 +78,8 @@ index 731fc8566..6d6a3fb2a 100644
 -                return null;
 -            }
 -
--            // Not interested in uncaught API errors
--            if (error instanceof ApiError) {
+-            // Not interested in uncaught API errors, or known errors
+-            if (error instanceof ApiError || error?.trace === false) {
 -                return null;
 -            }
 -
@@ -105,65 +106,8 @@ index 731fc8566..6d6a3fb2a 100644
 -        },
 -        // Some ignoreErrors and denyUrls are taken from this gist: https://gist.github.com/Chocksy/e9b2cdd4afc2aadc7989762c4b8b495a
 -        // This gist is suggested in the Sentry documentation: https://docs.sentry.io/clients/javascript/tips/#decluttering-sentry
--        ignoreErrors: [
--            // Ignore random plugins/extensions
--            'top.GLOBALS',
--            'canvas.contentDocument',
--            'MyApp_RemoveAllHighlights',
--            'atomicFindClose',
--            // See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
--            'conduitPage',
--            // https://bugzilla.mozilla.org/show_bug.cgi?id=1678243
--            'XDR encoding failure',
--            'Request timed out',
--            'No network connection',
--            'Failed to fetch',
--            'NetworkError when attempting to fetch resource.',
--            'webkitExitFullScreen', // Bug in Firefox for iOS.
--            'InactiveSession',
--            'UnhandledRejection', // Happens too often in extensions and we have lints for that, so should be safe to ignore.
--            /chrome-extension/,
--            /moz-extension/,
--            'TransferCancel', // User action to interrupt upload or download in Drive.
--            'UploadConflictError', // User uploading the same file again in Drive.
--            'UploadUserError', // Upload error on user's side in Drive.
--            'ValidationError', // Validation error on user's side in Drive.
--            'ChunkLoadError', // WebPack loading source code.
--            /ResizeObserver loop/, // Chromium bug https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
--            // See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error.html
--            'originalCreateNotification',
--            'http://tt.epicplay.com',
--            "Can't find variable: ZiteReader",
--            'jigsaw is not defined',
--            'ComboSearch is not defined',
--            'http://loading.retry.widdit.com/',
--            // Facebook borked
--            'fb_xd_fragment',
--            // ISP "optimizing" proxy - `Cache-Control: no-transform` seems to reduce this. (thanks @acdha)
--            // See http://stackoverflow.com/questions/4113268/how-to-stop-javascript-injection-from-vodafone-proxy
--            'bmi_SafeAddOnload',
--            'EBCallBackMessageReceived',
--            // Avast extension error
--            '_avast_submit',
--        ],
--        denyUrls: [
--            // Google Adsense
--            /pagead\/js/i,
--            // Facebook flakiness
--            /graph\.facebook\.com/i,
--            // Facebook blocked
--            /connect\.facebook\.net\/en_US\/all\.js/i,
--            // Woopra flakiness
--            /eatdifferent\.com\.woopra-ns\.com/i,
--            /static\.woopra\.com\/js\/woopra\.js/i,
--            // Chrome extensions
--            /extensions\//i,
--            /^chrome:\/\//i,
--            // Other plugins
--            /127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
--            /webappstoolbarba\.texthelp\.com\//i,
--            /metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
--        ],
+-        ignoreErrors,
+-        denyUrls,
 -    });
 -
 -    configureScope((scope) => {
@@ -186,3 +130,4 @@ index 731fc8566..6d6a3fb2a 100644
 +export const captureMessage = (...args: Parameters<typeof sentryCaptureMessage>) => console.log(...args);
 
  export default main;
+--
diff --git a/patches/protonmail/sentry-16.patch b/patches/protonmail/sentry-16.patch
new file mode 100644
index 000000000..63abd1954
--- /dev/null
+++ b/patches/protonmail/sentry-16.patch
@@ -0,0 +1,140 @@
+diff --git a/packages/shared/lib/helpers/sentry.ts b/packages/shared/lib/helpers/sentry.ts
+index 952bbe2061..8186ab1afc 100644
+--- a/packages/shared/lib/helpers/sentry.ts
++++ b/packages/shared/lib/helpers/sentry.ts
+@@ -1,16 +1,9 @@
+ import {
+     BrowserOptions,
+-    Integrations as SentryIntegrations,
+     captureException,
+-    configureScope,
+-    init,
+-    makeFetchTransport,
+-    captureMessage as sentryCaptureMessage,
+ } from '@sentry/browser';
+-import { BrowserTransportOptions } from '@sentry/browser/types/transports/types';
+
+ import { VPN_HOSTNAME } from '../constants';
+-import { ApiError } from '../fetch/ApiError';
+ import { getUIDHeaders } from '../fetch/headers';
+ import { ProtonConfig } from '../interfaces';
+
+@@ -68,21 +61,6 @@ export const getContentTypeHeaders = (input: RequestInfo | URL): HeadersInit =>
+     return {};
+ };
+
+-const sentryFetch = (input: RequestInfo | URL, init?: RequestInit) => {
+-    return globalThis.fetch(input, {
+-        ...init,
+-        headers: {
+-            ...init?.headers,
+-            ...getContentTypeHeaders(input),
+-            ...context.authHeaders,
+-        },
+-    });
+-};
+-
+-const makeProtonFetchTransport = (options: BrowserTransportOptions) => {
+-    return makeFetchTransport(options, sentryFetch);
+-};
+-
+ const isLocalhost = (host: string) => host.startsWith('localhost');
+ const isProduction = (host: string) => host.endsWith('.proton.me') || host === VPN_HOSTNAME;
+
+@@ -171,92 +149,10 @@ function main({
+     ignore = ({ host }) => isLocalhost(host),
+     denyUrls = getDefaultDenyUrls(),
+     ignoreErrors = getDefaultIgnoreErrors(),
+-}: SentryOptions) {
+-    const { SENTRY_DSN, APP_VERSION } = config;
+-    const { host, release, environment } = sentryConfig;
+-
+-    // No need to configure it if we don't load the DSN
+-    if (!SENTRY_DSN || ignore(sentryConfig)) {
+-        return;
+-    }
+-
+-    setUID(uid);
+-
+-    // Assumes SENTRY_DSN is: https://111b3eeaaec34cae8e812df705690a36@sentry/11
+-    // To get https://111b3eeaaec34cae8e812df705690a36@protonmail.com/api/core/v4/reports/sentry/11
+-    const dsn = SENTRY_DSN.replace('sentry', `${host}/api/core/v4/reports/sentry`);
+-
+-    init({
+-        dsn,
+-        release,
+-        environment,
+-        normalizeDepth: 5,
+-        transport: makeProtonFetchTransport,
+-        autoSessionTracking: sessionTracking,
+-        // do not log calls to console.log, console.error, etc.
+-        integrations: [
+-            new SentryIntegrations.Breadcrumbs({
+-                console: false,
+-            }),
+-        ],
+-        // Disable client reports. Client reports are used by sentry to retry events that failed to send on visibility change.
+-        // Unfortunately Sentry does not use the custom transport for those, and thus fails to add the headers the API requires.
+-        sendClientReports: false,
+-        beforeSend(event, hint) {
+-            const error = hint?.originalException as any;
+-            const stack = typeof error === 'string' ? error : error?.stack;
+-            // Filter out broken ferdi errors
+-            if (stack && stack.match(/ferdi|franz/i)) {
+-                return null;
+-            }
++}: SentryOptions) {}
+
+-            // Not interested in uncaught API errors, or known errors
+-            if (error instanceof ApiError || error?.trace === false) {
+-                return null;
+-            }
++export const traceError = (...args: Parameters<typeof captureException>) => console.error(...args);
+
+-            if (!context.enabled) {
+-                return null;
+-            }
+-
+-            // Remove the hash from the request URL and navigation breadcrumbs to avoid
+-            // leaking the search parameters of encrypted searches
+-            if (event.request && event.request.url) {
+-                [event.request.url] = event.request.url.split('#');
+-            }
+-            if (event.breadcrumbs) {
+-                event.breadcrumbs = event.breadcrumbs.map((breadcrumb) => {
+-                    if (breadcrumb.category === 'navigation' && breadcrumb.data) {
+-                        [breadcrumb.data.from] = breadcrumb.data.from.split('#');
+-                        [breadcrumb.data.to] = breadcrumb.data.to.split('#');
+-                    }
+-                    return breadcrumb;
+-                });
+-            }
+-
+-            return event;
+-        },
+-        // Some ignoreErrors and denyUrls are taken from this gist: https://gist.github.com/Chocksy/e9b2cdd4afc2aadc7989762c4b8b495a
+-        // This gist is suggested in the Sentry documentation: https://docs.sentry.io/clients/javascript/tips/#decluttering-sentry
+-        ignoreErrors,
+-        denyUrls,
+-    });
+-
+-    configureScope((scope) => {
+-        scope.setTag('appVersion', APP_VERSION);
+-    });
+-}
+-
+-export const traceError = (...args: Parameters<typeof captureException>) => {
+-    if (!isLocalhost(window.location.host)) {
+-        captureException(...args);
+-    }
+-};
+-
+-export const captureMessage = (...args: Parameters<typeof sentryCaptureMessage>) => {
+-    if (!isLocalhost(window.location.host)) {
+-        sentryCaptureMessage(...args);
+-    }
+-};
++export const captureMessage = (...args: Parameters<typeof captureException>) => console.log(...args);
+
+ export default main;
+--
diff --git a/patches/protonmail/url-4.patch b/patches/protonmail/url-5.patch
similarity index 87%
rename from patches/protonmail/url-4.patch
rename to patches/protonmail/url-5.patch
index f294f7cd6..201d51850 100644
--- a/patches/protonmail/url-4.patch
+++ b/patches/protonmail/url-5.patch
@@ -1,8 +1,8 @@
 diff --git a/packages/shared/lib/helpers/url.ts b/packages/shared/lib/helpers/url.ts
-index 11cd310f6..8019c07f5 100644
+index 0c21c0f042..8ccd5ce82b 100644
 --- a/packages/shared/lib/helpers/url.ts
 +++ b/packages/shared/lib/helpers/url.ts
-@@ -175,48 +175,16 @@ export const getSecondLevelDomain = (hostname: string) => {
+@@ -183,48 +183,16 @@ export const getSecondLevelDomain = (hostname: string) => {
      return hostname.slice(hostname.indexOf('.') + 1);
  };
 
@@ -51,13 +51,13 @@ index 11cd310f6..8019c07f5 100644
 -
  let cache = '';
  export const getStaticURL = (path: string) => {
-     if (window.location.hostname === 'localhost' || getIsDohDomain(window.location.origin)) {
+     if (
 
 diff --git a/packages/shared/lib/fetch/helpers.ts b/packages/shared/lib/fetch/helpers.ts
-index f0b3e2e31..90995e1fc 100644
+index 666b9f56c6..246f0e03b4 100644
 --- a/packages/shared/lib/fetch/helpers.ts
 +++ b/packages/shared/lib/fetch/helpers.ts
-@@ -10,6 +10,7 @@ const appendQueryParams = (url: URL, params: { [key: string]: any }) => {
+@@ -11,6 +11,7 @@ const appendQueryParams = (url: URL, params: { [key: string]: any }) => {
      });
  };
 
@@ -65,7 +65,7 @@ index f0b3e2e31..90995e1fc 100644
  export const createUrl = (urlString: string, params: { [key: string]: any } = {}) => {
      let url: URL;
      if (typeof window !== 'undefined') {
-@@ -20,6 +21,7 @@ export const createUrl = (urlString: string, params: { [key: string]: any } = {}
+@@ -21,6 +22,7 @@ export const createUrl = (urlString: string, params: { [key: string]: any } = {}
      appendQueryParams(url, params);
      return url;
  };
diff --git a/patches/protonmail/url-6.patch b/patches/protonmail/url-6.patch
new file mode 100644
index 000000000..76eb1073e
--- /dev/null
+++ b/patches/protonmail/url-6.patch
@@ -0,0 +1,90 @@
+diff --git a/packages/shared/lib/helpers/url.ts b/packages/shared/lib/helpers/url.ts
+index 576f743891..1105a2ae62 100644
+--- a/packages/shared/lib/helpers/url.ts
++++ b/packages/shared/lib/helpers/url.ts
+@@ -185,63 +185,16 @@ export const getSecondLevelDomain = (hostname: string) => {
+     return hostname.slice(hostname.indexOf('.') + 1);
+ };
+ 
+-export const getRelativeApiHostname = (hostname: string) => {
+-    const idx = hostname.indexOf('.');
+-    const first = hostname.slice(0, idx);
+-    const second = hostname.slice(idx + 1);
+-    return `${first}-api.${second}`;
+-};
+-
+ export const getIsDohDomain = (origin: string) => {
+     return DOH_DOMAINS.some((dohDomain) => origin.endsWith(dohDomain));
+ };
+ 
+-const doesHostnameLookLikeIP = (hostname: string) => {
+-    // Quick helper function to tells us if hostname string seems to be IP address or DNS name.
+-    // Relies on a fact, that no TLD ever will probably end with a digit. So if last char is
+-    // a digit, it's probably an IP.
+-    // IPv6 addresses can end with a letter, so there's additional colon check also.
+-    // Probably no need ever to use slow & complicated IP regexes here, but feel free to change
+-    // whenever we have such util functions available.
+-    // Note: only works on hostnames (no port), not origins (can include port and protocol).
+-    return /\d$/.test(hostname) || hostname.includes(':');
+-};
+-
+ export const getApiSubdomainUrl = (pathname: string) => {
+-    const url = new URL('', window.location.origin);
+-
+-    const usePathPrefix =
+-        url.hostname === 'localhost' || getIsDohDomain(url.origin) || doesHostnameLookLikeIP(url.hostname);
+-    if (usePathPrefix) {
+-        url.pathname = `/api${pathname}`;
+-        return url;
+-    }
+-
+-    url.hostname = getRelativeApiHostname(url.hostname);
++    const url = new URL('/', '___ELECTRON_MAIL_PROTON_API_ENTRY_URL_PLACEHOLDER___');
+     url.pathname = pathname;
+     return url;
+ };
+ 
+-export const getAppUrlFromApiUrl = (apiUrl: string, appName: APP_NAMES) => {
+-    const { subdomain } = APPS_CONFIGURATION[appName];
+-    const url = new URL(apiUrl);
+-    const { hostname } = url;
+-    const index = hostname.indexOf('.');
+-    const tail = hostname.slice(index + 1);
+-    url.pathname = '';
+-    url.hostname = `${subdomain}.${tail}`;
+-    return url;
+-};
+-
+-export const getAppUrlRelativeToOrigin = (origin: string, appName: APP_NAMES) => {
+-    const { subdomain } = APPS_CONFIGURATION[appName];
+-    const url = new URL(origin);
+-    const segments = url.host.split('.');
+-    segments[0] = subdomain;
+-    url.hostname = segments.join('.');
+-    return url;
+-};
+-
+ let cache = '';
+ export const getStaticURL = (path: string) => {
+     if (
+
+diff --git a/packages/shared/lib/fetch/helpers.ts b/packages/shared/lib/fetch/helpers.ts
+index 666b9f56c6..246f0e03b4 100644
+--- a/packages/shared/lib/fetch/helpers.ts
++++ b/packages/shared/lib/fetch/helpers.ts
+@@ -11,6 +11,7 @@ const appendQueryParams = (url: URL, params: { [key: string]: any }) => {
+     });
+ };
+
++/* <electron-mail-mark> */
+ export const createUrl = (urlString: string, params: { [key: string]: any } = {}) => {
+     let url: URL;
+     if (typeof window !== 'undefined') {
+@@ -21,6 +22,7 @@ export const createUrl = (urlString: string, params: { [key: string]: any } = {}
+     appendQueryParams(url, params);
+     return url;
+ };
++/* </electron-mail-mark> */
+
+ export const checkStatus = (response: Response, config: any) => {
+     const { status } = response;
diff --git a/scripts/prepare-webclient/webclients.ts b/scripts/prepare-webclient/webclients.ts
index 43ca01a23..5e03538df 100644
--- a/scripts/prepare-webclient/webclients.ts
+++ b/scripts/prepare-webclient/webclients.ts
@@ -325,6 +325,7 @@ async function executeBuildFlow(
                                 ...process.env,
                                 ...(publicPath && {PUBLIC_PATH: publicPath}),
                                 NODE_ENV: "production",
+                                TS_NODE_PROJECT: "../../tsconfig.webpack.json", // picked "build" task of "applications/<app>/package.json"
                             },
                         },
                     ],
diff --git a/src/electron-preload/webview/primary/provider-api/index.ts b/src/electron-preload/webview/primary/provider-api/index.ts
index 5e11119d3..7d5e483b5 100644
--- a/src/electron-preload/webview/primary/provider-api/index.ts
+++ b/src/electron-preload/webview/primary/provider-api/index.ts
@@ -208,7 +208,7 @@ export const initProviderApi = async (): Promise<ProviderApi> => {
                     ): NoExtraProps<MessageVerification> => {
                         const result = {
                             senderPinnedKeys: encryptionPreferences.pinnedKeys,
-                            senderVerified: Boolean(encryptionPreferences.isContactSignatureVerified),
+                            pinnedKeysVerified: Boolean(encryptionPreferences.isContactSignatureVerified),
                         } as const;
                         // this proxy helps early detecting unexpected/not-yet-reviewed protonmail's "getDecryptedAttachment" behaviour
                         // if/likely-when the behaviour gets changed by protonmail
diff --git a/src/electron-preload/webview/primary/provider-api/model.ts b/src/electron-preload/webview/primary/provider-api/model.ts
index 87a73c1e1..8645b0218 100644
--- a/src/electron-preload/webview/primary/provider-api/model.ts
+++ b/src/electron-preload/webview/primary/provider-api/model.ts
@@ -196,7 +196,7 @@ export interface MessageKeys {
 
 export interface MessageVerification {
     senderPinnedKeys: EncryptionPreferences["pinnedKeys"] | undefined;
-    senderVerified: boolean | undefined;
+    pinnedKeysVerified: boolean | undefined;
 }
 
 export type MessageExtended = NoExtraProps<{
diff --git a/src/shared/const/proton-apps.ts b/src/shared/const/proton-apps.ts
index e27c6359a..12514cb22 100644
--- a/src/shared/const/proton-apps.ts
+++ b/src/shared/const/proton-apps.ts
@@ -23,7 +23,7 @@ export const PROVIDER_REPO_MAP = {
         basePath: "",
         apiSubdomain: "mail-api",
         repoRelativeDistDir: "./dist",
-        tag: "proton-mail@5.0.22.7",
+        tag: "proton-mail@5.0.28.10",
         protonPack: {
             webpackIndexEntryItems: [
                 // immediate
@@ -49,14 +49,14 @@ export const PROVIDER_REPO_MAP = {
         basePath: "account",
         apiSubdomain: "account-api",
         repoRelativeDistDir: "./dist",
-        tag: "proton-account@5.0.31.3",
+        tag: "proton-account@5.0.52.0",
         protonPack: {}
     },
     [PROVIDER_APP_NAMES[2]]: {
         basePath: "calendar",
         apiSubdomain: "calendar-api",
         repoRelativeDistDir: "./dist",
-        tag: "proton-calendar@5.0.10.15",
+        tag: "proton-calendar@5.0.14.6",
         protonPack: {
             webpackIndexEntryItems: [
                 // immediate
@@ -69,14 +69,14 @@ export const PROVIDER_REPO_MAP = {
         basePath: "drive",
         apiSubdomain: "drive-api",
         repoRelativeDistDir: "./dist",
-        tag: "proton-drive@5.0.13.8",
+        tag: "proton-drive@5.0.15.3",
         protonPack: {},
     },
     [PROVIDER_APP_NAMES[4]]: {
         basePath: "account/vpn",
         apiSubdomain: "account-api",
         repoRelativeDistDir: "./dist",
-        tag: "proton-vpn-settings@5.0.30.2",
+        tag: "proton-vpn-settings@5.0.48.1",
         protonPack: {},
     },
 } as const;