1 import type { ReactNode } from 'react';
3 import { c } from 'ttag';
5 import { Href } from '@proton/atoms';
6 import SettingsLink from '@proton/components/components/link/SettingsLink';
7 import { PLANS } from '@proton/payments';
8 import type { APP_NAMES } from '@proton/shared/lib/constants';
9 import { APPS, DRIVE_SHORT_APP_NAME, MAIL_SHORT_APP_NAME } from '@proton/shared/lib/constants';
10 import { hasBit } from '@proton/shared/lib/helpers/bitset';
11 import { getPlan } from '@proton/shared/lib/helpers/subscription';
12 import { addUpsellPath, getUpgradePath } from '@proton/shared/lib/helpers/upsell';
13 import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
14 import type { Subscription, UserModel } from '@proton/shared/lib/interfaces';
15 import { UserLockedFlags } from '@proton/shared/lib/interfaces';
16 import { getAppStorage } from '@proton/shared/lib/user/storage';
18 import TopBanner from './TopBanner';
20 const StorageBannerText = ({ app, type, cta }: { app: APP_NAMES; type: UserLockedFlags; cta: ReactNode }) => {
22 case UserLockedFlags.BASE_STORAGE_EXCEEDED:
23 const mailStorage = getAppStorage(MAIL_SHORT_APP_NAME);
24 // Translator: Your Mail storage is full. To send or receive emails, free up space or upgrade for more storage.
25 return c('locked_state_storage_banner: info')
26 .jt`Your ${mailStorage} is full. To send or receive emails, free up space or ${cta}.`;
27 case UserLockedFlags.DRIVE_STORAGE_EXCEEDED:
28 const driveStorage = getAppStorage(DRIVE_SHORT_APP_NAME);
29 // Translator: Your Drive storage is full. To upload or sync files, free up space or upgrade for more storage.
30 return c('locked_state_storage_banner: info')
31 .jt`Your ${driveStorage} is full. To upload or sync files, free up space or ${cta}.`;
32 case UserLockedFlags.STORAGE_EXCEEDED:
33 if (app === APPS.PROTONDRIVE) {
34 // Translator: Your storage is full. To upload or sync files, free up space or upgrade for more storage.
35 return c('locked_state_storage_banner: info')
36 .jt`Your storage is full. To upload or sync files, free up space or ${cta}.`;
38 // Translator: Your storage is full. To send or receive emails, free up space or upgrade for more storage.
39 return c('locked_state_storage_banner: info')
40 .jt`Your storage is full. To send or receive emails, free up space or ${cta}.`;
41 case UserLockedFlags.ORG_ISSUE_FOR_PRIMARY_ADMIN:
42 // Translator: Your subscription has ended. Upgrade to restore full access and to avoid data loss.
43 return c('locked_state_storage_banner: info')
44 .jt`Your subscription has ended. ${cta} and to avoid data loss.`;
45 case UserLockedFlags.ORG_ISSUE_FOR_MEMBER:
46 // Translator: Your account is at risk of deletion. To avoid data loss, ask your admin to upgrade. Learn more
47 return c('locked_state_storage_banner: info')
48 .jt`Your account is at risk of deletion. To avoid data loss, ask your admin to upgrade. ${cta}`;
52 const getCTAText = (type: UserLockedFlags) => {
54 case UserLockedFlags.BASE_STORAGE_EXCEEDED:
55 case UserLockedFlags.DRIVE_STORAGE_EXCEEDED:
56 case UserLockedFlags.STORAGE_EXCEEDED:
57 return c('locked_state_storage_banner: info').t`upgrade for more storage`;
58 case UserLockedFlags.ORG_ISSUE_FOR_PRIMARY_ADMIN:
59 return c('locked_state_storage_banner: info').t`Upgrade to restore full access`;
60 case UserLockedFlags.ORG_ISSUE_FOR_MEMBER:
61 return c('locked_state_storage_banner: info').t`Learn more`;
65 const StorageBannerCTA = ({
73 subscription: Subscription | undefined;
74 upsellRef: string | undefined;
76 type: UserLockedFlags;
78 const ctaText = getCTAText(type);
79 return type === UserLockedFlags.ORG_ISSUE_FOR_MEMBER ? (
80 <Href href={getKnowledgeBaseUrl('/free-plan-limits')}>{ctaText}</Href>
84 className="color-inherit"
85 path={addUpsellPath(getUpgradePath({ user, plan, subscription }), upsellRef)}
92 const getBannerTypeFromLockedFlags = (lcokedFlags: number): UserLockedFlags => {
93 let type = UserLockedFlags.STORAGE_EXCEEDED;
94 if (hasBit(lcokedFlags, UserLockedFlags.ORG_ISSUE_FOR_PRIMARY_ADMIN)) {
95 type = UserLockedFlags.ORG_ISSUE_FOR_PRIMARY_ADMIN;
96 } else if (hasBit(lcokedFlags, UserLockedFlags.ORG_ISSUE_FOR_MEMBER)) {
97 type = UserLockedFlags.ORG_ISSUE_FOR_MEMBER;
99 hasBit(lcokedFlags, UserLockedFlags.BASE_STORAGE_EXCEEDED) &&
100 hasBit(lcokedFlags, UserLockedFlags.DRIVE_STORAGE_EXCEEDED)
102 type = UserLockedFlags.STORAGE_EXCEEDED;
103 } else if (hasBit(lcokedFlags, UserLockedFlags.BASE_STORAGE_EXCEEDED)) {
104 type = UserLockedFlags.BASE_STORAGE_EXCEEDED;
105 } else if (hasBit(lcokedFlags, UserLockedFlags.DRIVE_STORAGE_EXCEEDED)) {
106 type = UserLockedFlags.DRIVE_STORAGE_EXCEEDED;
114 subscription: Subscription | undefined;
115 upsellRef: string | undefined;
119 const LockedStateTopBanner = ({ app, user, subscription, upsellRef, lockedFlags }: Props) => {
120 const planName = getPlan(subscription)?.Name;
122 if (plan === undefined) {
123 plan = app === APPS.PROTONDRIVE ? PLANS.DRIVE : PLANS.MAIL;
126 const type = getBannerTypeFromLockedFlags(lockedFlags);
129 <StorageBannerCTA user={user} subscription={subscription} upsellRef={upsellRef} plan={plan} type={type} />
133 <TopBanner className="bg-danger">
134 <StorageBannerText app={app} type={type} cta={cta} />
139 export default LockedStateTopBanner;