Merge pull request #166 from matrix-org/valr/mobile_support

Add mobile store links
This commit is contained in:
Bruno Windels 2020-11-24 11:32:05 +00:00 committed by GitHub
commit 92ba3546a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 196 additions and 5 deletions

View File

@ -20,6 +20,9 @@ import {
ClientKind, ClientKind,
ClientId, ClientId,
Platform, Platform,
AppleStoreLink,
PlayStoreLink,
FDroidLink,
} from './types'; } from './types';
import { LinkKind } from '../parser/types'; import { LinkKind } from '../parser/types';
import logo from '../imgs/element.svg'; import logo from '../imgs/element.svg';
@ -31,7 +34,7 @@ export const Element: LinkedClient = {
logo: logo, logo: logo,
homepage: 'https://element.io', homepage: 'https://element.io',
maturity: Maturity.STABLE, maturity: Maturity.STABLE,
description: 'Fully-featured Matrix client for the Web', description: 'Fully-featured Matrix client',
platforms: [Platform.Desktop, Platform.Android, Platform.iOS], platforms: [Platform.Desktop, Platform.Android, Platform.iOS],
experimental: false, experimental: false,
clientId: ClientId.Element, clientId: ClientId.Element,
@ -59,6 +62,11 @@ export const Element: LinkedClient = {
} }
}, },
linkSupport: () => true, linkSupport: () => true,
installLinks: [
new AppleStoreLink('vector', 'id1083446067'),
new PlayStoreLink('im.vector.app'),
new FDroidLink('im.vector.app'),
],
}; };
export const ElementDevelop: LinkedClient = { export const ElementDevelop: LinkedClient = {
@ -94,4 +102,5 @@ export const ElementDevelop: LinkedClient = {
} }
}, },
linkSupport: () => true, linkSupport: () => true,
installLinks: [],
}; };

View File

@ -69,6 +69,7 @@ const Fractal: TextClient = {
}, },
description: 'Fractal is a Matrix Client written in Rust', description: 'Fractal is a Matrix Client written in Rust',
installLinks: [],
}; };
export default Fractal; export default Fractal;

View File

@ -86,6 +86,7 @@ const Nheko: TextClient = {
}, },
description: description:
'A native desktop app for Matrix that feels more like a mainstream chat app.', 'A native desktop app for Matrix that feels more like a mainstream chat app.',
installLinks: [],
}; };
export default Nheko; export default Nheko;

View File

@ -86,6 +86,7 @@ const Weechat: TextClient = {
}, },
description: 'Command-line Matrix interface using Weechat', description: 'Command-line Matrix interface using Weechat',
installLinks: [],
}; };
export default Weechat; export default Weechat;

View File

@ -53,6 +53,79 @@ export enum ClientId {
Fractal = 'fractal', Fractal = 'fractal',
} }
/**
* Define a native distribution channel for a client.
* E.g App store for apple, PlayStore or F-Droid for Android
*/
export interface InstallLink {
createInstallURL(deepLink: SafeLink) : string;
// in AppleStoreLink, we can set the cookie here for deeplinking
// onInstallChosen(deepLink: SafeLink);
platform: Platform;
channelId: string;
description: string;
}
export class AppleStoreLink implements InstallLink {
constructor(private org: string, private appId: string) {}
createInstallURL(deepLink: SafeLink) : string {
return `https://apps.apple.com/app/${encodeURIComponent(this.org)}/${encodeURIComponent(this.appId)}`;
}
get platform() : Platform {
return Platform.iOS;
}
get channelId(): string {
return "apple-app-store";
}
get description() {
return "Download on the App Store";
}
}
export class PlayStoreLink implements InstallLink {
constructor(private appId: string) {}
createInstallURL(deepLink: SafeLink) : string {
return `https://play.google.com/store/apps/details?id=${encodeURIComponent(this.appId)}&referrer=${encodeURIComponent(deepLink.originalLink)}`;
}
get platform() : Platform {
return Platform.Android;
}
get channelId(): string {
return "play-store";
}
get description() {
return "Get it on Google Play";
}
}
export class FDroidLink implements InstallLink {
constructor(private appId: string) {}
createInstallURL(deepLink: SafeLink) : string {
return `https://f-droid.org/packages/${encodeURIComponent(this.appId)}`;
}
get platform() : Platform {
return Platform.Android;
}
get channelId(): string {
return "fdroid";
}
get description() {
return "Get it on F-Droid";
}
}
/* /*
* The descriptive details of a client * The descriptive details of a client
*/ */
@ -67,6 +140,7 @@ export interface ClientDescription {
clientId: ClientId; clientId: ClientId;
experimental: boolean; experimental: boolean;
linkSupport: (link: SafeLink) => boolean; linkSupport: (link: SafeLink) => boolean;
installLinks: InstallLink[];
} }
/* /*

View File

@ -51,7 +51,8 @@ limitations under the License.
.button { .button {
height: 40px; height: 40px;
width: 130px; min-width: 130px;
max-width: 165px;
margin-top: 16px; margin-top: 16px;
} }
} }
@ -66,6 +67,15 @@ limitations under the License.
&:hover { &:hover {
background-color: $app-background; background-color: $app-background;
} }
.installLink {
display: inline-block;
height: 40px;
margin: 8px 16px 8px 0;
img {
height: 100%;
}
}
} }
.clientTileLink { .clientTileLink {

View File

@ -14,14 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, { useContext } from 'react';
import classNames from 'classnames'; 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 { SafeLink } from '../parser/types';
import Tile from './Tile'; import Tile from './Tile';
import Button from './Button'; import Button from './Button';
import appStoreBadge from '../imgs/app-store-us-alt.svg';
import playStoreBadge from '../imgs/google-play-us.svg';
import fdroidBadge from '../imgs/fdroid-badge.png';
import './ClientTile.scss'; import './ClientTile.scss';
interface IProps { interface IProps {
@ -29,17 +34,54 @@ interface IProps {
link: SafeLink; link: SafeLink;
} }
interface IInstallBadgeImages {
[index: string]: string;
}
const installBadgeImages : IInstallBadgeImages = {
"fdroid": fdroidBadge,
"apple-app-store": appStoreBadge,
"play-store": playStoreBadge
};
const ClientTile: React.FC<IProps> = ({ client, link }: IProps) => { const ClientTile: React.FC<IProps> = ({ client, link }: IProps) => {
const inviteLine = const inviteLine =
client.kind === ClientKind.TEXT_CLIENT ? ( client.kind === ClientKind.TEXT_CLIENT ? (
<p>{client.toInviteString(link)}</p> <p>{client.toInviteString(link)}</p>
) : null; ) : null;
const { uaResults } = useContext(UAContext);
const className = classNames('clientTile', { const className = classNames('clientTile', {
clientTileLink: client.kind === ClientKind.LINKED_CLIENT, clientTileLink: client.kind === ClientKind.LINKED_CLIENT,
}); });
let inviteButton: JSX.Element = <></>; let inviteButton: JSX.Element = <></>;
const matchingInstallLinks = client.installLinks.filter((installLink) => {
if ((uaResults as any).ios) {
return installLink.platform === Platform.iOS;
} else if ((uaResults as any).android) {
return installLink.platform === Platform.Android;
} else {
return false;
}
});
const hasNativeClient = matchingInstallLinks.length > 0;
let installButtons = undefined;
if (matchingInstallLinks.length) {
installButtons = <p>{matchingInstallLinks.map((installLink) => {
return <a
rel="noopener noreferrer"
aria-label={installLink.description}
key={installLink.channelId}
href={installLink.createInstallURL(link)}
className="installLink"
target="_blank">
<img src={installBadgeImages[installLink.channelId]} alt={installLink.description} />
</a>;
})}</p>;
}
if (client.kind === ClientKind.LINKED_CLIENT) { if (client.kind === ClientKind.LINKED_CLIENT) {
inviteButton = <Button>Accept invite</Button>; inviteButton = <Button>Accept invite</Button>;
} else { } else {
@ -62,6 +104,7 @@ const ClientTile: React.FC<IProps> = ({ client, link }: IProps) => {
<div> <div>
<h1>{client.name}</h1> <h1>{client.name}</h1>
<p>{client.description}</p> <p>{client.description}</p>
{installButtons}
{inviteLine} {inviteLine}
{inviteButton} {inviteButton}
</div> </div>
@ -69,7 +112,11 @@ const ClientTile: React.FC<IProps> = ({ client, link }: IProps) => {
); );
if (client.kind === ClientKind.LINKED_CLIENT) { if (client.kind === ClientKind.LINKED_CLIENT) {
clientTile = <a href={client.toUrl(link).toString()}>{clientTile}</a>; if (!hasNativeClient) {
clientTile = (
<a href={client.toUrl(link).toString()}>{clientTile}</a>
);
}
} }
return clientTile; return clientTile;

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/imgs/fdroid-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB