Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / components / containers / vpn / OpenVPNConfigurationSection / ConfigsTable.tsx
blob7ee74ba18c67f4a8c68cf25c4f1e7ecea7a02092
1 import { memo } from 'react';
3 import { c } from 'ttag';
5 import { useUser } from '@proton/account/user/hooks';
6 import { Button, ButtonLike } from '@proton/atoms';
7 import Icon from '@proton/components/components/icon/Icon';
8 import SettingsLink from '@proton/components/components/link/SettingsLink';
9 import Table from '@proton/components/components/table/Table';
10 import TableBody from '@proton/components/components/table/TableBody';
11 import TableCell from '@proton/components/components/table/TableCell';
12 import TableRow from '@proton/components/components/table/TableRow';
13 import Tooltip from '@proton/components/components/tooltip/Tooltip';
14 import useApi from '@proton/components/hooks/useApi';
15 import { PLANS } from '@proton/payments';
16 import { getVPNServerConfig } from '@proton/shared/lib/api/vpn';
17 import downloadFile from '@proton/shared/lib/helpers/downloadFile';
18 import type { Logical } from '@proton/shared/lib/vpn/Logical';
19 import clsx from '@proton/utils/clsx';
20 import isTruthy from '@proton/utils/isTruthy';
22 import type { CountryOptions } from '../../../helpers/countries';
23 import Country from './Country';
24 import LoadIndicator from './LoadIndicator';
25 import type { EnhancedLogical } from './interface';
26 import { normalizeName } from './normalizeName';
27 import { isP2PEnabled, isTorEnabled } from './utils';
29 export enum CATEGORY {
30     SECURE_CORE = 'SecureCore',
31     COUNTRY = 'Country',
32     SERVER = 'Server',
33     FREE = 'Free',
36 const PlusBadge = () => (
37     <span className="ml-2">
38         <Tooltip title="Plus">
39             <div className="text-center rounded">P</div>
40         </Tooltip>
41     </span>
44 const ServerDown = () => (
45     <span className="ml-2">
46         <Tooltip title={c('Info').t`Server is currently down`}>
47             <div className="flex inline-flex *:self-center">
48                 <Icon className="color-danger" size={5} name="exclamation-circle" />
49             </div>
50         </Tooltip>
51     </span>
54 export const P2PIcon = () => (
55     <span className="mx-2">
56         <Tooltip title={c('Info').t`P2P`}>
57             <Icon name="arrow-right-arrow-left" size={4.5} className="rounded bg-strong p-1" />
58         </Tooltip>
59     </span>
62 export const TorIcon = () => (
63     <span className="mx-2">
64         <Tooltip title={c('Info').t`Tor`}>
65             <Icon name="brand-tor" size={4.5} className="rounded bg-strong p-1" />
66         </Tooltip>
67     </span>
70 interface Props {
71     servers: EnhancedLogical[];
72     loading?: boolean;
73     protocol?: string;
74     platform: string;
75     category: CATEGORY;
76     selecting?: boolean;
77     onSelect?: (server: Logical) => void;
78     countryOptions: CountryOptions;
81 // TODO: Add icons instead of text for p2p and tor when they are ready
82 const ConfigsTable = ({
83     loading,
84     servers = [],
85     platform,
86     protocol,
87     category,
88     onSelect,
89     selecting,
90     countryOptions,
91 }: Props) => {
92     const api = useApi();
93     const [{ hasPaidVpn }] = useUser();
95     const handleClickDownload =
96         ({ ID, ExitCountry, Tier, Name }: Logical) =>
97         async () => {
98             const buffer = await api(
99                 getVPNServerConfig({
100                     LogicalID: category === CATEGORY.COUNTRY ? undefined : ID,
101                     Platform: platform,
102                     Protocol: protocol,
103                     Country: ExitCountry,
104                 })
105             );
106             const blob = new Blob([buffer], { type: 'application/x-openvpn-profile' });
107             const name = category === CATEGORY.COUNTRY ? ExitCountry.toLowerCase() : normalizeName({ Tier, Name });
108             downloadFile(blob, `${name}.protonvpn.${protocol}.ovpn`);
109         };
111     return (
112         <Table hasActions>
113             <thead>
114                 <tr>
115                     <TableCell
116                         className={clsx(['w-auto', category === CATEGORY.SERVER ? 'md:w-1/4' : 'md:w-1/3'])}
117                         type="header"
118                     >
119                         {[CATEGORY.SERVER, CATEGORY.FREE].includes(category)
120                             ? c('TableHeader').t`Name`
121                             : c('TableHeader').t`Country`}
122                     </TableCell>
123                     {category === CATEGORY.SERVER ? (
124                         <TableCell className="w-auto md:w-1/4" type="header">{c('TableHeader').t`City`}</TableCell>
125                     ) : null}
126                     <TableCell
127                         className={clsx(['w-auto', category === CATEGORY.SERVER ? 'md:w-1/4' : 'md:w-1/3'])}
128                         type="header"
129                     >{c('TableHeader').t`Status`}</TableCell>
130                     <TableCell
131                         className={clsx(['w-auto', category === CATEGORY.SERVER ? 'md:w-1/4' : 'md:w-1/3'])}
132                         type="header"
133                     >{c('TableHeader').t`Action`}</TableCell>
134                 </tr>
135             </thead>
136             <TableBody loading={loading} colSpan={4}>
137                 {servers.map((server) => (
138                     <TableRow
139                         key={server.ID}
140                         cells={[
141                             [CATEGORY.SERVER, CATEGORY.FREE].includes(category) ? (
142                                 server.Name
143                             ) : (
144                                 <Country key="country" server={server} countryOptions={countryOptions} />
145                             ),
146                             category === CATEGORY.SERVER ? (
147                                 <div className="inline-flex *:self-center" key="city">
148                                     {server.City}
149                                 </div>
150                             ) : null,
151                             <div className="inline-flex *:self-center" key="status">
152                                 <LoadIndicator server={server} />
153                                 {server.Tier === 2 && <PlusBadge />}
154                                 {server.Servers?.every(({ Status }) => !Status) && <ServerDown />}
155                                 {isP2PEnabled(server.Features) && <P2PIcon />}
156                                 {isTorEnabled(server.Features) && <TorIcon />}
157                             </div>,
158                             server.isUpgradeRequired ? (
159                                 <Tooltip
160                                     key="download"
161                                     title={
162                                         server.Tier === 2
163                                             ? c('Info').t`Plus or Visionary subscription required`
164                                             : c('Info').t`Basic, Plus or Visionary subscription required`
165                                     }
166                                 >
167                                     <ButtonLike
168                                         as={SettingsLink}
169                                         color="norm"
170                                         size="small"
171                                         path={hasPaidVpn ? `/dashboard?plan=${PLANS.VPN2024}` : '/upgrade'}
172                                     >{c('Action').t`Upgrade`}</ButtonLike>
173                                 </Tooltip>
174                             ) : onSelect ? (
175                                 <Button size="small" onClick={() => onSelect(server)} loading={selecting}>
176                                     {c('Action').t`Create`}
177                                 </Button>
178                             ) : (
179                                 <Button size="small" onClick={handleClickDownload(server)}>
180                                     {c('Action').t`Download`}
181                                 </Button>
182                             ),
183                         ].filter(isTruthy)}
184                     />
185                 ))}
186             </TableBody>
187         </Table>
188     );
191 export default memo(ConfigsTable);