diff --git a/src/components/ClientList.scss b/src/components/ClientList.scss new file mode 100644 index 0000000000000000000000000000000000000000..24c882309fc63386edfa9eaf6aa7ddf8778ec3af --- /dev/null +++ b/src/components/ClientList.scss @@ -0,0 +1,22 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.clientList { + display: grid; + row-gap: 20px; + list-style-type: none; + padding-inline-start: 0; +} diff --git a/src/components/ClientList.tsx b/src/components/ClientList.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3fdb7456bf14e4aaf023ae9bf9f61ed7f7c936a7 --- /dev/null +++ b/src/components/ClientList.tsx @@ -0,0 +1,91 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { useContext } from 'react'; +import { UAContext } from '@quentin-sommer/react-useragent'; + +import { SafeLink } from '../parser/types'; +import { ActionType, ClientContext } from '../contexts/ClientContext'; +import Clients from '../clients'; +import { Client, Platform } from '../clients/types'; +import ClientTile from './ClientTile'; + +import './ClientList.scss'; + +interface IProps { + link: SafeLink; +} + +const ClientList: React.FC<IProps> = ({ link }: IProps) => { + const [ + { rememberSelection, showOnlyDeviceClients, showExperimentalClients }, + clientDispatcher, + ] = useContext(ClientContext); + const { uaResults } = useContext(UAContext); + + /* + * Function to decide whether a client is shown + */ + const showClient = (client: Client): boolean => { + let showClient = false; + + if (!showOnlyDeviceClients || uaResults === {}) { + showClient = true; + } + + switch (client.platform) { + case Platform.Desktop: + showClient = showClient || !(uaResults as any).mobile; + break; + case Platform.iOS: + showClient = showClient || (uaResults as any).ios; + break; + case Platform.Android: + showClient = showClient || (uaResults as any).android; + break; + } + + if (!showExperimentalClients && client.experimental) { + showClient = false; + } + + return showClient; + }; + + const clientLi = (client: Client): JSX.Element => ( + <li + key={client.clientId} + onClick={(): void => + rememberSelection + ? clientDispatcher({ + action: ActionType.SetClient, + clientId: client.clientId, + }) + : undefined + } + > + <ClientTile client={client} link={link} /> + </li> + ); + + return ( + <ul className="clientList"> + {Clients.filter(showClient).map(clientLi)} + </ul> + ); +}; + +export default ClientList; diff --git a/src/components/ClientSelection.scss b/src/components/ClientSelection.scss new file mode 100644 index 0000000000000000000000000000000000000000..3900f7726c835c999f9c2e7a5a51e2c2e3af998f --- /dev/null +++ b/src/components/ClientSelection.scss @@ -0,0 +1,30 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.advanced { + margin: 0 5%; + + .advancedOptions { + display: flex; + flex-direction: column; + + align-items: flex-start; + } + + .clientList { + margin-top: 20px; + } +} diff --git a/src/components/ClientSelection.tsx b/src/components/ClientSelection.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6a15ad42969c81fc1b5cd4ed01529a4596767c8d --- /dev/null +++ b/src/components/ClientSelection.tsx @@ -0,0 +1,79 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { useContext } from 'react'; + +import './ClientSelection.scss'; +import { ActionType, ClientContext } from '../contexts/ClientContext'; +import ClientList from './ClientList'; +import { SafeLink } from '../parser/types'; + +interface IProps { + link: SafeLink; +} + +const ClientSelection: React.FC<IProps> = ({ link }: IProps) => { + const [clientState, clientStateDispatch] = useContext(ClientContext); + const options = ( + <div className="advancedOptions"> + <label> + <input + type="checkbox" + onChange={(): void => { + clientStateDispatch({ + action: ActionType.ToggleRememberSelection, + }); + }} + checked={clientState.rememberSelection} + /> + Remember my selection for future invites in this browser + </label> + <label> + <input + type="checkbox" + onChange={(): void => { + clientStateDispatch({ + action: ActionType.ToggleShowOnlyDeviceClients, + }); + }} + checked={clientState.showOnlyDeviceClients} + /> + Show only clients suggested for this device + </label> + <label> + <input + type="checkbox" + onChange={(): void => { + clientStateDispatch({ + action: ActionType.ToggleShowExperimentalClients, + }); + }} + checked={clientState.showExperimentalClients} + /> + Show experimental clients + </label> + </div> + ); + + return ( + <div className="advanced"> + {options} + <ClientList link={link} /> + </div> + ); +}; + +export default ClientSelection; diff --git a/src/components/ClientTile.scss b/src/components/ClientTile.scss new file mode 100644 index 0000000000000000000000000000000000000000..18a8be053f669ed6a61027d858a3980895165345 --- /dev/null +++ b/src/components/ClientTile.scss @@ -0,0 +1,66 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +@import '../color-scheme'; + +.clientTile { + display: flex; + flex-direction: row; + + height: 155px; + width: 100%; + + color: $foreground; + + > img { + flex-shrink: 0; + height: 100%; + } + + h1 { + text-align: left; + font-size: 14px; + line-height: 24px; + } + + p { + margin-right: 20px; + margin-top: 20px; + text-align: left; + } + + border: 1px solid $borders; + border-radius: 8px; + + padding: 8px; + + // For the chevron + position: relative; +} + +.clientTileLink { + position: relative; + + width: 100%; + + &::after { + // TODO: add chevron top right + position: absolute; + right: 10px; + top: 5px; + content: '>'; + } +} diff --git a/src/components/ClientTile.tsx b/src/components/ClientTile.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6367b72bd748507a62cc21fd22fb07cd3c7f3c4a --- /dev/null +++ b/src/components/ClientTile.tsx @@ -0,0 +1,58 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import classNames from 'classnames'; + +import { Client, ClientKind } from '../clients/types'; +import { SafeLink } from '../parser/types'; +import Tile from './Tile'; + +import './ClientTile.scss'; + +interface IProps { + client: Client; + link: SafeLink; +} + +const ClientTile: React.FC<IProps> = ({ client, link }: IProps) => { + const inviteLine = + client.kind === ClientKind.TEXT_CLIENT ? ( + <p>{client.toInviteString(link)}</p> + ) : null; + + const className = classNames('clientTile', { + clientTileLink: client.kind === ClientKind.LINKED_CLIENT, + }); + let clientTile = ( + <Tile className={className}> + <img src={client.logo} alt={client.name + ' logo'} /> + <div> + <h1>{client.name}</h1> + <p>{client.description}</p> + {inviteLine} + </div> + </Tile> + ); + + if (client.kind === ClientKind.LINKED_CLIENT) { + clientTile = <a href={client.toUrl(link).toString()}>{clientTile}</a>; + } + + return clientTile; +}; + +export default ClientTile;