diff --git a/src/clients/Element.ts b/src/clients/Element.ts index 0693ede..369c0e9 100644 --- a/src/clients/Element.ts +++ b/src/clients/Element.ts @@ -20,6 +20,7 @@ import { ClientKind, ClientId, Platform, + StoreDistribution, } from './types'; import { LinkKind } from '../parser/types'; import logo from '../imgs/element.svg'; @@ -31,7 +32,7 @@ export const Element: LinkedClient = { logo: logo, homepage: 'https://element.io', maturity: Maturity.STABLE, - description: 'Fully-featured Matrix client for the Web', + description: 'Cross platfom fully-featured Matrix client', platforms: [Platform.Desktop, Platform.Android, Platform.iOS], experimental: false, clientId: ClientId.Element, @@ -59,6 +60,27 @@ export const Element: LinkedClient = { } }, linkSupport: () => true, + installLinks: [ + new StoreDistribution( + 'App Store', + Platform.iOS, + 'https://apps.apple.com/app/vector/id1083446067', + false + ), + new StoreDistribution( + 'Google Play', + Platform.Android, + 'https://play.google.com/store/apps/details?id=im.vector.app', + true + ), + new StoreDistribution( + 'F-Droid', + Platform.Android, + 'https://f-droid.org/fr/packages/im.vector.app/', + false + ), + // TODO desktop clients? + ], }; export const ElementDevelop: LinkedClient = { @@ -94,4 +116,5 @@ export const ElementDevelop: LinkedClient = { } }, linkSupport: () => true, + installLinks: [], }; diff --git a/src/clients/Fractal.tsx b/src/clients/Fractal.tsx index bd0f068..f9ea9a2 100644 --- a/src/clients/Fractal.tsx +++ b/src/clients/Fractal.tsx @@ -69,6 +69,7 @@ const Fractal: TextClient = { }, description: 'Fractal is a Matrix Client written in Rust', + installLinks: [], }; export default Fractal; diff --git a/src/clients/Nheko.tsx b/src/clients/Nheko.tsx index 1195716..f9201d3 100644 --- a/src/clients/Nheko.tsx +++ b/src/clients/Nheko.tsx @@ -86,6 +86,7 @@ const Nheko: TextClient = { }, description: 'A native desktop app for Matrix that feels more like a mainstream chat app.', + installLinks: [], }; export default Nheko; diff --git a/src/clients/Weechat.tsx b/src/clients/Weechat.tsx index 105a979..805a4b2 100644 --- a/src/clients/Weechat.tsx +++ b/src/clients/Weechat.tsx @@ -86,6 +86,7 @@ const Weechat: TextClient = { }, description: 'Command-line Matrix interface using Weechat', + installLinks: [], }; export default Weechat; diff --git a/src/clients/types.ts b/src/clients/types.ts index 3278f96..8e41546 100644 --- a/src/clients/types.ts +++ b/src/clients/types.ts @@ -53,6 +53,29 @@ export enum ClientId { Fractal = 'fractal', } +/** + * Define a native distribution channel for a client. + * E.g App store for apple, PlayStore or F-Droid for Android + */ +export class StoreDistribution { + public name: string; + public platform: Platform; + public download: string; + public supportReferrer: boolean; + + constructor( + name: string, + platform: Platform, + download: string, + supportReferrer: boolean + ) { + this.name = name; + this.platform = platform; + this.download = download; + this.supportReferrer = supportReferrer; + } +} + /* * The descriptive details of a client */ @@ -67,6 +90,7 @@ export interface ClientDescription { clientId: ClientId; experimental: boolean; linkSupport: (link: SafeLink) => boolean; + installLinks: StoreDistribution[]; } /* diff --git a/src/components/ClientTile.scss b/src/components/ClientTile.scss index 996c56f..6bd4dfb 100644 --- a/src/components/ClientTile.scss +++ b/src/components/ClientTile.scss @@ -51,7 +51,8 @@ limitations under the License. .button { height: 40px; - width: 130px; + min-width: 130px; + max-width: 165px; margin-top: 16px; } } diff --git a/src/components/ClientTile.tsx b/src/components/ClientTile.tsx index e8eddde..7076bc2 100644 --- a/src/components/ClientTile.tsx +++ b/src/components/ClientTile.tsx @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { useContext } from 'react'; import classNames from 'classnames'; +import { UAContext } from '@quentin-sommer/react-useragent'; -import { Client, ClientKind } from '../clients/types'; +import { Client, ClientKind, Platform } from '../clients/types'; import { SafeLink } from '../parser/types'; import Tile from './Tile'; import Button from './Button'; @@ -35,13 +36,54 @@ const ClientTile: React.FC = ({ client, link }: IProps) => {

{client.toInviteString(link)}

) : null; + const { uaResults } = useContext(UAContext); + const className = classNames('clientTile', { clientTileLink: client.kind === ClientKind.LINKED_CLIENT, }); let inviteButton: JSX.Element = <>; + let hasNativeClient = false; + let installButton = undefined; if (client.kind === ClientKind.LINKED_CLIENT) { - inviteButton = ; + const availableClients = client.installLinks.filter((distrib) => { + if ((uaResults as any).ios) { + return distrib.platform == Platform.iOS; + } else if ((uaResults as any).android) { + return distrib.platform == Platform.Android; + } else { + return false; + } + }); + hasNativeClient = availableClients.length > 0; + if (hasNativeClient) { + inviteButton = ( + + ); + } else { + inviteButton = ; + } + + installButton = availableClients.map((distrib) => ( + + )); } else { const copyString = client.copyString(link); if (copyString !== '') { @@ -63,13 +105,18 @@ const ClientTile: React.FC = ({ client, link }: IProps) => {

{client.name}

{client.description}

{inviteLine} + {hasNativeClient && installButton} {inviteButton} ); if (client.kind === ClientKind.LINKED_CLIENT) { - clientTile = {clientTile}; + if (!hasNativeClient) { + clientTile = ( + {clientTile} + ); + } } return clientTile;