From 890669741c07fefdff0037f695151abf74215955 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 09:57:49 +0200 Subject: [PATCH 01/19] Remove Yup dependency and fix link validation --- package.json | 2 +- src/components/CreateLinkTile.tsx | 36 ++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index b4af7ab..792a413 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-scripts": "3.4.1", - "yup": "^0.29.1" + "zod": "^1.10.3" }, "scripts": { "start": "react-scripts start", diff --git a/src/components/CreateLinkTile.tsx b/src/components/CreateLinkTile.tsx index 63a57d3..06b0c97 100644 --- a/src/components/CreateLinkTile.tsx +++ b/src/components/CreateLinkTile.tsx @@ -15,12 +15,15 @@ limitations under the License. */ import React, { useEffect, useRef } from 'react'; +import { Formik, Form } from 'formik'; + import Tile from './Tile'; import Button from './Button'; import TextButton from './TextButton'; import Input from './Input'; -import { Formik, Form } from 'formik'; -import * as Yup from 'yup'; + +import { parseHash } from '../parser/parser'; +import { LinkKind } from '../parser/types'; import './CreateLinkTile.scss'; @@ -28,6 +31,24 @@ interface ILinkNotCreatedTileProps { setLink: React.Dispatch>; } +interface FormValues { + identifier: string; +} + +// Hacky use of types here +function validate(values: FormValues): Partial { + const errors: Partial = {}; + + const parse = parseHash(values.identifier); + + if (parse.kind === LinkKind.ParseFailed) { + errors.identifier = + "That link doesn't look right. Double check the details."; + } + + return errors; +} + const LinkNotCreatedTile: React.FC = ( props: ILinkNotCreatedTileProps ) => { @@ -41,15 +62,7 @@ const LinkNotCreatedTile: React.FC = ( initialValues={{ identifier: '', }} - validationSchema={Yup.object({ - identifier: Yup.string() - .test( - 'is-identifier', - "That link doesn't look right. Double check the details.", - (link) => link - ) - .required('Required'), - })} + validate={validate} onSubmit={(values): void => { props.setLink( document.location.protocol + @@ -109,7 +122,6 @@ const LinkCreatedTile: React.FC = (props) => { const CreateLinkTile: React.FC = () => { const [link, setLink] = React.useState(''); - console.log(link); if (!link) { return ; } else { From 47fe7860c363bfbca876e1732d74caabbd562e42 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 10:14:13 +0200 Subject: [PATCH 02/19] Configure global storage --- package.json | 1 + src/App.tsx | 26 ++++---- src/clients/types.ts | 20 ++++-- src/contexts/ClientContext.ts | 112 ++++++++++++++++++++++---------- src/contexts/GlobalContext.tsx | 36 +++++++++++ src/contexts/HSContext.ts | 113 +++++++++++++++++++++++++++++++++ src/utils/localStorage.ts | 59 +++++++++++++++++ 7 files changed, 313 insertions(+), 54 deletions(-) create mode 100644 src/contexts/GlobalContext.tsx create mode 100644 src/contexts/HSContext.ts create mode 100644 src/utils/localStorage.ts diff --git a/package.json b/package.json index 792a413..ad67d5f 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@quentin-sommer/react-useragent": "^3.1.0", "classnames": "^2.2.6", "formik": "^2.1.4", "matrix-cypher": "^0.1.12", diff --git a/src/App.tsx b/src/App.tsx index 748d405..e2d9347 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,34 +14,34 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React from 'react'; -import SingleColumn from "./layouts/SingleColumn"; -import CreateLinkTile from "./components/CreateLinkTile"; -import MatrixTile from "./components/MatrixTile"; -import Tile from "./components/Tile"; -import LinkRouter from "./pages/LinkRouter"; +import SingleColumn from './layouts/SingleColumn'; +import CreateLinkTile from './components/CreateLinkTile'; +import MatrixTile from './components/MatrixTile'; +import Tile from './components/Tile'; +import LinkRouter from './pages/LinkRouter'; -import "./App.scss"; +import GlobalContext from './contexts/GlobalContext'; /* eslint-disable no-restricted-globals */ const App: React.FC = () => { let page = ( <> -
{" "} +
{' '} ); if (location.hash) { console.log(location.hash); - if (location.hash.startsWith("#/")) { + if (location.hash.startsWith('#/')) { page = ; } else { - console.log("asdfadf"); + console.log('asdfadf'); page = ( - Links should be in the format {location.host}/#/{"<"} - matrix-resource-identifier{">"} + Links should be in the format {location.host}/#/{'<'} + matrix-resource-identifier{'>'} ); } @@ -50,7 +50,7 @@ const App: React.FC = () => { return (
- {page} + {page}
diff --git a/src/clients/types.ts b/src/clients/types.ts index 8daea91..2df503b 100644 --- a/src/clients/types.ts +++ b/src/clients/types.ts @@ -20,10 +20,10 @@ import { SafeLink } from '../parser/types'; * A collection of descriptive tags that can be added to * a clients description. */ -export enum Tag { - IOS = 'IOS', - ANDROID = 'ANDROID', - DESKTOP = 'DESKTOP', +export enum Platform { + iOS = 'iOS', + Android = 'ANDROID', + Desktop = 'DESKTOP', } /* @@ -45,6 +45,12 @@ export enum ClientKind { TEXT_CLIENT = 'TEXT_CLIENT', } +export enum ClientId { + Element = 'element.io', + ElementDevelop = 'develop.element.io', + WeeChat = 'weechat', +} + /* * The descriptive details of a client */ @@ -54,8 +60,10 @@ export interface ClientDescription { homepage: string; logo: string; description: string; - tags: Tag[]; + platform: Platform; maturity: Maturity; + clientId: ClientId; + experimental: boolean; } /* @@ -72,7 +80,7 @@ export interface LinkedClient extends ClientDescription { */ export interface TextClient extends ClientDescription { kind: ClientKind.TEXT_CLIENT; - toInviteString(parsedLink: SafeLink): string; + toInviteString(parsedLink: SafeLink): JSX.Element; } /* diff --git a/src/contexts/ClientContext.ts b/src/contexts/ClientContext.ts index cb530ce..52da9f2 100644 --- a/src/contexts/ClientContext.ts +++ b/src/contexts/ClientContext.ts @@ -15,54 +15,96 @@ limitations under the License. */ import React from 'react'; +import { object, string, boolean, TypeOf } from 'zod'; -import { prefixFetch, Client, discoverServer } from 'matrix-cypher'; +import { ClientId } from '../clients/types'; +import { persistReducer } from '../utils/localStorage'; -type State = { - clientURL: string; - client: Client; -}[]; +const STATE_SCHEMA = object({ + clientId: string().nullable(), + showOnlyDeviceClients: boolean(), + rememberSelection: boolean(), + showExperimentalClients: boolean(), +}); + +type State = TypeOf; // Actions are a discriminated union. -export enum ActionTypes { - AddClient = 'ADD_CLIENT', - RemoveClient = 'REMOVE_CLIENT', +export enum ActionType { + SetClient = 'SET_CLIENT', + ToggleRememberSelection = 'TOGGLE_REMEMBER_SELECTION', + ToggleShowOnlyDeviceClients = 'TOGGLE_SHOW_ONLY_DEVICE_CLIENTS', + ToggleShowExperimentalClients = 'TOGGLE_SHOW_EXPERIMENTAL_CLIENTS', } -export interface AddClient { - action: ActionTypes.AddClient; - clientURL: string; +interface SetClient { + action: ActionType.SetClient; + clientId: ClientId; } -export interface RemoveClient { - action: ActionTypes.RemoveClient; - clientURL: string; +interface ToggleRememberSelection { + action: ActionType.ToggleRememberSelection; } -export type Action = AddClient | RemoveClient; +interface ToggleShowOnlyDeviceClients { + action: ActionType.ToggleShowOnlyDeviceClients; +} -export const INITIAL_STATE: State = []; -export const reducer = async (state: State, action: Action): Promise => { - switch (action.action) { - case ActionTypes.AddClient: - return state.filter((x) => x.clientURL !== action.clientURL); +interface ToggleShowExperimentalClients { + action: ActionType.ToggleShowExperimentalClients; +} - case ActionTypes.RemoveClient: - if (!state.filter((x) => x.clientURL === action.clientURL)) { - const resolvedURL = await discoverServer(action.clientURL); - state.push({ - clientURL: resolvedURL, - client: prefixFetch(resolvedURL), - }); - } - } - return state; +export type Action = + | SetClient + | ToggleRememberSelection + | ToggleShowOnlyDeviceClients + | ToggleShowExperimentalClients; + +const INITIAL_STATE: State = { + clientId: null, + rememberSelection: false, + showOnlyDeviceClients: true, + showExperimentalClients: false, }; -// The null is a hack to make the type checker happy -// create context does not need an argument -const { Provider, Consumer } = React.createContext(null); +export const [initialState, reducer] = persistReducer( + 'default-client', + INITIAL_STATE, + STATE_SCHEMA, + (state: State, action: Action): State => { + switch (action.action) { + case ActionType.SetClient: + return { + ...state, + clientId: action.clientId, + }; + case ActionType.ToggleRememberSelection: + return { + ...state, + rememberSelection: !state.rememberSelection, + }; + case ActionType.ToggleShowOnlyDeviceClients: + return { + ...state, + showOnlyDeviceClients: !state.showOnlyDeviceClients, + }; + case ActionType.ToggleShowExperimentalClients: + return { + ...state, + showExperimentalClients: !state.showExperimentalClients, + }; + default: + return state; + } + } +); + +// The defualt reducer needs to be overwritten with the one above +// after it's been put through react's useReducer +export const ClientContext = React.createContext< + [State, React.Dispatch] +>([initialState, (): void => {}]); // Quick rename to make importing easier -export const ClientProvider = Provider; -export const ClientConsumer = Consumer; +export const ClientProvider = ClientContext.Provider; +export const ClientConsumer = ClientContext.Consumer; diff --git a/src/contexts/GlobalContext.tsx b/src/contexts/GlobalContext.tsx new file mode 100644 index 0000000..20b4401 --- /dev/null +++ b/src/contexts/GlobalContext.tsx @@ -0,0 +1,36 @@ +/* +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, { useReducer } from 'react'; +import { UserAgentProvider } from '@quentin-sommer/react-useragent'; + +import { + ClientProvider, + reducer as clientReducer, + initialState as clientInitialState, +} from './ClientContext'; + +interface IProps { + children: React.ReactNode; +} + +export default ({ children }: IProps): JSX.Element => ( + + + {children} + + +); diff --git a/src/contexts/HSContext.ts b/src/contexts/HSContext.ts new file mode 100644 index 0000000..e6afb95 --- /dev/null +++ b/src/contexts/HSContext.ts @@ -0,0 +1,113 @@ +/* +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 { string, object, union, literal, TypeOf } from 'zod'; + +import { persistReducer } from '../utils/localStorage'; + +//import { prefixFetch, Client, discoverServer } from 'matrix-cypher'; + +enum HSOptions { + // The homeserver contact policy hasn't + // been set yet. + Unset = 'UNSET', + // Matrix.to should only contact a single provided homeserver + TrustedClientOnly = 'TRUSTED_CLIENT_ONLY', + // Matrix.to may contact any homeserver it requires + Any = 'ANY', + // Matrix.to may not contact any homeservers + None = 'NONE', +} + +const STATE_SCHEMA = union([ + object({ + option: literal(HSOptions.Unset), + }), + object({ + option: literal(HSOptions.None), + }), + object({ + option: literal(HSOptions.Any), + }), + object({ + option: literal(HSOptions.TrustedClientOnly), + hs: string(), + }), +]); + +type State = TypeOf; + +// TODO: rename actions to something with more meaning out of context +export enum ActionTypes { + SetHS = 'SET_HS', + SetAny = 'SET_ANY', + SetNone = 'SET_NONE', +} + +export interface SetHS { + action: ActionTypes.SetHS; + HSURL: string; +} + +export interface SetAny { + action: ActionTypes.SetAny; +} + +export interface SetNone { + action: ActionTypes.SetNone; +} + +export type Action = SetHS | SetAny | SetNone; + +export const INITIAL_STATE: State = { + option: HSOptions.Unset, +}; + +export const [initialState, reducer] = persistReducer( + 'home-server-options', + INITIAL_STATE, + STATE_SCHEMA, + (state: State, action: Action): State => { + switch (action.action) { + case ActionTypes.SetNone: + return { + option: HSOptions.None, + }; + case ActionTypes.SetAny: + return { + option: HSOptions.Any, + }; + case ActionTypes.SetHS: + return { + option: HSOptions.TrustedClientOnly, + hs: action.HSURL, + }; + default: + return state; + } + } +); + +// The defualt reducer needs to be overwritten with the one above +// after it's been put through react's useReducer +const { Provider, Consumer } = React.createContext< + [State, React.Dispatch] +>([initialState, (): void => {}]); + +// Quick rename to make importing easier +export const HSProvider = Provider; +export const HSConsumer = Consumer; diff --git a/src/utils/localStorage.ts b/src/utils/localStorage.ts new file mode 100644 index 0000000..b93a2fa --- /dev/null +++ b/src/utils/localStorage.ts @@ -0,0 +1,59 @@ +/* +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 { + Schema, +} from 'zod'; +import React from 'react'; + +/* + * Initialises local storage to initial value if + * a value matching the schema is not in storage. + */ +export function persistReducer( + stateKey: string, + initialState: T, + schema: Schema, + reducer: React.Reducer, +): [T, React.Reducer] { + let currentState = initialState; + // Try to load state from local storage + const stateInStorage = localStorage.getItem(stateKey); + if (stateInStorage) { + try { + // Validate state type + const parsedState = JSON.parse(stateInStorage); + if (parsedState as T) { + currentState = schema.parse(parsedState); + } + } catch (e) { + // if invalid delete state + localStorage.setItem(stateKey, JSON.stringify(initialState)); + } + } else { + localStorage.setItem(stateKey, JSON.stringify(initialState)); + } + + return [ + currentState, + (state: T, action: A) => { + // state passed to this reducer is the source of truth + const newState = reducer(state, action); + localStorage.setItem(stateKey, JSON.stringify(newState)); + return newState; + }, + ]; +} From dd8aa3d0746711bba4311dc0ae860a6e45b74a46 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 10:25:55 +0200 Subject: [PATCH 03/19] Create clients and configure them --- src/clients/Element.io.ts | 47 ++++++++- src/clients/Weechat.tsx | 63 +++++++++++ src/clients/index.ts | 16 ++- src/{clients => imgs}/element.svg | 0 src/imgs/weechat.svg | 170 ++++++++++++++++++++++++++++++ 5 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 src/clients/Weechat.tsx rename src/{clients => imgs}/element.svg (100%) create mode 100644 src/imgs/weechat.svg diff --git a/src/clients/Element.io.ts b/src/clients/Element.io.ts index 402f97f..6d4f6c1 100644 --- a/src/clients/Element.io.ts +++ b/src/clients/Element.io.ts @@ -14,9 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { LinkedClient, Maturity, ClientKind } from './types'; +import { + LinkedClient, + Maturity, + ClientKind, + ClientId, + Platform, +} from './types'; import { LinkKind } from '../parser/types'; -import logo from './element.svg'; +import logo from '../imgs/element.svg'; const Element: LinkedClient = { kind: ClientKind.LINKED_CLIENT, @@ -26,7 +32,9 @@ const Element: LinkedClient = { homepage: 'https://element.io', maturity: Maturity.STABLE, description: 'Fully-featured Matrix client for the Web', - tags: [], + platform: Platform.Desktop, + experimental: false, + clientId: ClientId.Element, toUrl: (link) => { switch (link.kind) { case LinkKind.Alias: @@ -50,4 +58,37 @@ const Element: LinkedClient = { }, }; +export const ElementDevelop: LinkedClient = { + kind: ClientKind.LINKED_CLIENT, + name: 'Element Develop', + author: 'Element', + logo: logo, + homepage: 'https://element.io', + maturity: Maturity.STABLE, + description: 'Fully-featured Matrix client for the Web', + platform: Platform.Desktop, + experimental: true, + clientId: ClientId.ElementDevelop, + toUrl: (link) => { + switch (link.kind) { + case LinkKind.Alias: + case LinkKind.RoomId: + return new URL( + `https://develop.element.io/#/room/${link.identifier}` + ); + case LinkKind.UserId: + return new URL( + `https://develop.element.io/#/user/${link.identifier}` + ); + case LinkKind.Permalink: + return new URL( + `https://develop.element.io/#/room/${link.identifier}` + ); + case LinkKind.GroupId: + return new URL( + `https://develop.element.io/#/group/${link.identifier}` + ); + } + }, +}; export default Element; diff --git a/src/clients/Weechat.tsx b/src/clients/Weechat.tsx new file mode 100644 index 0000000..e88dc24 --- /dev/null +++ b/src/clients/Weechat.tsx @@ -0,0 +1,63 @@ +/* +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 { TextClient, Maturity, ClientKind, ClientId, Platform } from './types'; + +import { LinkKind } from '../parser/types'; + +import logo from '../imgs/weechat.svg'; + +const Weechat: TextClient = { + kind: ClientKind.TEXT_CLIENT, + name: 'Weechat', + logo: logo, + author: 'Poljar', + homepage: 'https://github.com/poljar/weechat-matrix', + maturity: Maturity.LATE_BETA, + experimental: false, + platform: Platform.Desktop, + clientId: ClientId.WeeChat, + toInviteString: (link) => { + switch (link.kind) { + case LinkKind.Alias: + case LinkKind.RoomId: + return ( + + Type{' '} + + /join {link.identifier} + + + ); + case LinkKind.UserId: + return ( + + Type{' '} + + /invite {link.identifier} + + + ); + default: + return Weechat doesn't support this kind of link; + } + }, + description: 'Commandline Matrix interface using Weechat', +}; + +export default Weechat; diff --git a/src/clients/index.ts b/src/clients/index.ts index 0fc102f..ba39676 100644 --- a/src/clients/index.ts +++ b/src/clients/index.ts @@ -16,12 +16,24 @@ limitations under the License. import { Client } from './types'; -import Element from './Element.io'; +import Element, { ElementDevelop } from './Element.io'; +import Weechat from './Weechat'; /* * All the supported clients of matrix.to */ -const clients: Client[] = [Element]; +const clients: Client[] = [Element, Weechat, ElementDevelop]; + +/* + * A map from sharer string to client. + * Configured by hand so we can change the mappings + * easily later. + */ +export const clientMap: { [key: string]: Client } = { + [Element.clientId]: Element, + [Weechat.clientId]: Weechat, + [ElementDevelop.clientId]: ElementDevelop, +}; /* * All the supported clients of matrix.to diff --git a/src/clients/element.svg b/src/imgs/element.svg similarity index 100% rename from src/clients/element.svg rename to src/imgs/element.svg diff --git a/src/imgs/weechat.svg b/src/imgs/weechat.svg new file mode 100644 index 0000000..96b92b1 --- /dev/null +++ b/src/imgs/weechat.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + From 6e7a1198315b8cf0f61ca9839137da2f37e92e29 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 10:37:15 +0200 Subject: [PATCH 04/19] Add inviting client tile --- src/components/InvitingClientTile.stories.tsx | 23 ++++++++ src/components/InvitingClientTile.tsx | 58 +++++++++++++++++++ src/pages/LinkRouter.tsx | 19 +++++- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/components/InvitingClientTile.stories.tsx create mode 100644 src/components/InvitingClientTile.tsx diff --git a/src/components/InvitingClientTile.stories.tsx b/src/components/InvitingClientTile.stories.tsx new file mode 100644 index 0000000..6cac6ee --- /dev/null +++ b/src/components/InvitingClientTile.stories.tsx @@ -0,0 +1,23 @@ +/* +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 ClientTile from './InvitingClientTile'; + +export default { title: 'ClientTile' }; + +export const Element = ; diff --git a/src/components/InvitingClientTile.tsx b/src/components/InvitingClientTile.tsx new file mode 100644 index 0000000..2d22a6c --- /dev/null +++ b/src/components/InvitingClientTile.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 Tile from './Tile'; + +import { clientMap } from '../clients'; +import './MatrixTile.scss'; + +interface IProps { + clientName: string; +} + +const InvitingClientTile: React.FC = ({ clientName }: IProps) => { + const client = clientMap[clientName]; + + if (!client) { + return ( + + {/* TODO: add gh link */} +

+ The client that created this link "{clientName}" is not a + recognised client. If this is a mistake and you'd like a + nice advertisement for it here please{' '} + + open a pr + + . +

+
+ ); + } + + return ( + + {client.name} +

+ Invite created with {client.name} +

+
{client.description}
+
+ ); +}; + +export default InvitingClientTile; diff --git a/src/pages/LinkRouter.tsx b/src/pages/LinkRouter.tsx index fe4a2a1..a455c7d 100644 --- a/src/pages/LinkRouter.tsx +++ b/src/pages/LinkRouter.tsx @@ -18,6 +18,7 @@ import React from 'react'; import Tile from '../components/Tile'; import LinkPreview from '../components/LinkPreview'; +import InvitingClientTile from '../components/InvitingClientTile'; import { parseHash } from '../parser/parser'; import { LinkKind } from '../parser/types'; @@ -28,9 +29,9 @@ interface IProps { const LinkRouter: React.FC = ({ link }: IProps) => { // our room id's will be stored in the hash const parsedLink = parseHash(link); - console.log({ link }); let feedback: JSX.Element; + let client: JSX.Element = <>; switch (parsedLink.kind) { case LinkKind.ParseFailed: feedback = ( @@ -41,7 +42,21 @@ const LinkRouter: React.FC = ({ link }: IProps) => { ); break; default: - feedback = ; + if (parsedLink.arguments.client) { + client = ( + + ); + } + + feedback = ( + <> + +
+ {client} + + ); } return feedback; From 74d223e47589365532e67ba112e2965438261468 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 10:41:17 +0200 Subject: [PATCH 05/19] Add client options and styling for client tiles --- src/components/ClientList.scss | 22 +++++++ src/components/ClientList.tsx | 91 +++++++++++++++++++++++++++++ src/components/ClientSelection.scss | 30 ++++++++++ src/components/ClientSelection.tsx | 79 +++++++++++++++++++++++++ src/components/ClientTile.scss | 66 +++++++++++++++++++++ src/components/ClientTile.tsx | 58 ++++++++++++++++++ 6 files changed, 346 insertions(+) create mode 100644 src/components/ClientList.scss create mode 100644 src/components/ClientList.tsx create mode 100644 src/components/ClientSelection.scss create mode 100644 src/components/ClientSelection.tsx create mode 100644 src/components/ClientTile.scss create mode 100644 src/components/ClientTile.tsx diff --git a/src/components/ClientList.scss b/src/components/ClientList.scss new file mode 100644 index 0000000..24c8823 --- /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 0000000..3fdb745 --- /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 = ({ 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 => ( +
  • + rememberSelection + ? clientDispatcher({ + action: ActionType.SetClient, + clientId: client.clientId, + }) + : undefined + } + > + +
  • + ); + + return ( +
      + {Clients.filter(showClient).map(clientLi)} +
    + ); +}; + +export default ClientList; diff --git a/src/components/ClientSelection.scss b/src/components/ClientSelection.scss new file mode 100644 index 0000000..3900f77 --- /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 0000000..6a15ad4 --- /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 = ({ link }: IProps) => { + const [clientState, clientStateDispatch] = useContext(ClientContext); + const options = ( +
    + + + +
    + ); + + return ( +
    + {options} + +
    + ); +}; + +export default ClientSelection; diff --git a/src/components/ClientTile.scss b/src/components/ClientTile.scss new file mode 100644 index 0000000..18a8be0 --- /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 0000000..6367b72 --- /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 = ({ client, link }: IProps) => { + const inviteLine = + client.kind === ClientKind.TEXT_CLIENT ? ( +

    {client.toInviteString(link)}

    + ) : null; + + const className = classNames('clientTile', { + clientTileLink: client.kind === ClientKind.LINKED_CLIENT, + }); + let clientTile = ( + + {client.name +
    +

    {client.name}

    +

    {client.description}

    + {inviteLine} +
    +
    + ); + + if (client.kind === ClientKind.LINKED_CLIENT) { + clientTile = {clientTile}; + } + + return clientTile; +}; + +export default ClientTile; From e6b9325d0535ad7a0cec481f49506661939ab4f5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 10:43:01 +0200 Subject: [PATCH 06/19] Display correct client link by default --- src/components/InviteTile.tsx | 84 ++++++++++++++++++++++++++-------- src/components/LinkPreview.tsx | 19 ++++++-- src/layouts/SingleColumn.scss | 5 ++ 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/components/InviteTile.tsx b/src/components/InviteTile.tsx index bf200fc..b719f7d 100644 --- a/src/components/InviteTile.tsx +++ b/src/components/InviteTile.tsx @@ -14,43 +14,89 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import './InviteTile.scss'; import Tile from './Tile'; import LinkButton from './LinkButton'; -import TextButton from './TextButton'; +import ClientSelection from './ClientSelection'; import { Client, ClientKind } from '../clients/types'; import { SafeLink } from '../parser/types'; +import TextButton from './TextButton'; interface IProps { children?: React.ReactNode; - client: Client; + client: Client | null; link: SafeLink; } const InviteTile: React.FC = ({ children, client, link }: IProps) => { + const [showAdvanced, setShowAdvanced] = useState(false); let invite: React.ReactNode; - switch (client.kind) { - case ClientKind.LINKED_CLIENT: - invite = ( - - Accept invite - - ); - break; - case ClientKind.TEXT_CLIENT: - invite =

    {client.toInviteString(link)}

    ; - break; + let advanced: React.ReactNode; + // This i s a hacky way to get a the overlapping list of client + // options working. + let advancedPlaceholder: React.ReactNode; + + if (client === null) { + invite = null; + } else { + let inviteUseString: string; + + switch (client.kind) { + case ClientKind.LINKED_CLIENT: + invite = ( + + Accept invite + + ); + inviteUseString = `Accepting will open ${link.identifier} in ${client.name}.`; + break; + case ClientKind.TEXT_CLIENT: + // TODO: copy to clipboard + invite =

    {client.toInviteString(link)}

    ; + inviteUseString = `These are instructions for ${client.name}.`; + break; + } + + const advancedToggle = showAdvanced ? ( + setShowAdvanced(!showAdvanced)}> + Hide advanced options + + ) : ( +

    + {inviteUseString} + setShowAdvanced(!showAdvanced)} + > + Change Client. + +

    + ); + + invite = ( + <> + {invite} + {advancedToggle} + + ); + } + + if (client === null || showAdvanced) { + advanced = ; + advancedPlaceholder =
    ; } return ( - - {children} - {invite} - Advanced options - + <> + + {children} + {invite} + {advancedPlaceholder} + + {advanced} + ); }; diff --git a/src/components/LinkPreview.tsx b/src/components/LinkPreview.tsx index 9df012b..43abb10 100644 --- a/src/components/LinkPreview.tsx +++ b/src/components/LinkPreview.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useContext } from 'react'; import { getEvent, client } from 'matrix-cypher'; import { RoomPreviewWithTopic } from './RoomPreview'; @@ -22,13 +22,14 @@ import InviteTile from './InviteTile'; import { SafeLink, LinkKind } from '../parser/types'; import UserPreview from './UserPreview'; import EventPreview from './EventPreview'; -import Clients from '../clients'; +import { clientMap } from '../clients'; import { getRoomFromId, getRoomFromAlias, getRoomFromPermalink, getUser, } from '../utils/cypher-wrapper'; +import { ClientContext } from '../contexts/ClientContext'; interface IProps { link: SafeLink; @@ -91,8 +92,20 @@ const LinkPreview: React.FC = ({ link }: IProps) => { (async (): Promise => setContent(await invite({ link })))(); }, [link]); + const [{ rememberSelection, clientId }] = useContext(ClientContext); + + // Select which client to link to + const displayClientId = + rememberSelection && clientId + ? clientId + : link.arguments.client + ? link.arguments.client + : null; + + const client = displayClientId ? clientMap[displayClientId] : null; + return ( - + {content} ); diff --git a/src/layouts/SingleColumn.scss b/src/layouts/SingleColumn.scss index 00edf39..045504f 100644 --- a/src/layouts/SingleColumn.scss +++ b/src/layouts/SingleColumn.scss @@ -10,4 +10,9 @@ row-gap: 60px; align-items: center; + + .advanced { + position: relative; + top: -335px; + } } From 21e5435720aeda32f6ad860c988554c0eb8b2946 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 10:43:29 +0200 Subject: [PATCH 07/19] Configure background --- src/App.scss | 3 +- src/imgs/background.svg | 221 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 src/imgs/background.svg diff --git a/src/App.scss b/src/App.scss index 1d4ff36..9743f1a 100644 --- a/src/App.scss +++ b/src/App.scss @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -@import "./color-scheme"; +@import './color-scheme'; #root { background-color: $app-background; + background-image: url('./imgs/background.svg'); } @mixin spacer { diff --git a/src/imgs/background.svg b/src/imgs/background.svg new file mode 100644 index 0000000..c5ad331 --- /dev/null +++ b/src/imgs/background.svg @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3372fccc0a7fd8e813144660921feca9cfd0778e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 10:44:26 +0200 Subject: [PATCH 08/19] lint, css polish, yarn lock, fix some warning --- src/App.tsx | 8 +++-- src/components/InviteTile.scss | 9 +++-- src/components/LinkButton.tsx | 10 ++++-- src/components/RoomPreview.tsx | 8 +++-- yarn.lock | 62 +++++++++++++--------------------- 5 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e2d9347..5f60456 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,6 +22,8 @@ import MatrixTile from './components/MatrixTile'; import Tile from './components/Tile'; import LinkRouter from './pages/LinkRouter'; +import './App.scss'; + import GlobalContext from './contexts/GlobalContext'; /* eslint-disable no-restricted-globals */ @@ -29,15 +31,15 @@ import GlobalContext from './contexts/GlobalContext'; const App: React.FC = () => { let page = ( <> -
    {' '} + +
    ); + if (location.hash) { - console.log(location.hash); if (location.hash.startsWith('#/')) { page = ; } else { - console.log('asdfadf'); page = ( Links should be in the format {location.host}/#/{'<'} diff --git a/src/components/InviteTile.scss b/src/components/InviteTile.scss index d1efbd2..35d45c5 100644 --- a/src/components/InviteTile.scss +++ b/src/components/InviteTile.scss @@ -15,11 +15,10 @@ limitations under the License. */ .inviteTile { - .button { - margin-top: 24px; - } + display: grid; + row-gap: 24px; - > .textButton { - margin-top: 28px; + .advancedPlaceholder { + height: 245px; } } diff --git a/src/components/LinkButton.tsx b/src/components/LinkButton.tsx index ebada6d..375f926 100644 --- a/src/components/LinkButton.tsx +++ b/src/components/LinkButton.tsx @@ -21,8 +21,14 @@ import './Button.scss'; interface IProps extends React.LinkHTMLAttributes {} -const LinkButton: React.FC = ({ className, ...props }: IProps) => ( - +const LinkButton: React.FC = ({ + className, + children, + ...props +}: IProps) => ( + + {children} + ); export default LinkButton; diff --git a/src/components/RoomPreview.tsx b/src/components/RoomPreview.tsx index a5a6225..e7e5404 100644 --- a/src/components/RoomPreview.tsx +++ b/src/components/RoomPreview.tsx @@ -26,11 +26,15 @@ interface IProps { } const RoomPreview: React.FC = ({ room }: IProps) => { - const roomAlias = room.aliases ? room.aliases[0] : room.room_id; + const roomAlias = room.canonical_alias + ? room.canonical_alias + : room.aliases + ? room.aliases[0] + : room.room_id; return (
    -

    {room.name ? room.name : room.room_id}

    +

    {room.name ? room.name : roomAlias}

    {room.num_joined_members.toLocaleString()} members

    {roomAlias}

    diff --git a/yarn.lock b/yarn.lock index 280197a..a868985 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1133,7 +1133,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.11.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== @@ -1512,6 +1512,13 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@quentin-sommer/react-useragent@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@quentin-sommer/react-useragent/-/react-useragent-3.1.0.tgz#8aa823fed7da5034edbf5bb1dc3cefd3d19473ea" + integrity sha512-twLVX4A7GpHCEcr5M3c97QR88t+uSZUjvNSTZMqoY5vjPXhpAcw0ZlWpMF6Nlg7Ofkwo3pZa3paQ9Nt7VJYHOA== + dependencies: + ua-parser-js "^0.7.20" + "@reach/router@^1.2.1": version "1.3.4" resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.4.tgz#d2574b19370a70c80480ed91f3da840136d10f8c" @@ -6614,11 +6621,6 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -fn-name@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" - integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA== - focus-lock@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.7.0.tgz#b2bfb0ca7beacc8710a1ff74275fe0dc60a1d88a" @@ -9010,7 +9012,7 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.17.11, lodash-es@^4.17.14: +lodash-es@^4.17.14: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -9231,10 +9233,10 @@ material-colors@^1.2.1: resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg== -matrix-cypher@^0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/matrix-cypher/-/matrix-cypher-0.1.11.tgz#3152f47c097943592a12729f9c9340f56b130393" - integrity sha512-fwEntUxC79ycItPl8PRhiJD1oBgOSrMaOjkmt3eYHvmj1F4ylUyxRl3pJICAqZdUeBpbRrMFDhg7oWtGp9v2Ng== +matrix-cypher@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/matrix-cypher/-/matrix-cypher-0.1.13.tgz#a8aadaaf7c15d0b85cca3c8d31f7ee007cefa65a" + integrity sha512-n0PiKVD4On3Vwvm8iXlYZhpTh5edwWVnd1U4nEjNEbjX9MyclxeSsBja4R1w+8NV2WIxV35iypC2y6u+ElX2zg== dependencies: cross-fetch "^3.0.5" promise.any "^2.0.1" @@ -11386,11 +11388,6 @@ prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, object-assign "^4.1.1" react-is "^16.8.1" -property-expr@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.2.tgz#fff2a43919135553a3bc2fdd94bdb841965b2330" - integrity sha512-bc/5ggaYZxNkFKj374aLbEDqVADdYaLcFo8XBkishUWbaAdjlphaBFns9TvRA2pUseVL/wMFmui9X3IdNDU37g== - property-information@^5.0.0: version "5.5.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.5.0.tgz#4dc075d493061a82e2b7d096f406e076ed859943" @@ -13516,11 +13513,6 @@ symbol.prototype.description@^1.0.0: es-abstract "^1.17.0-next.1" has-symbols "^1.0.1" -synchronous-promise@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.13.tgz#9d8c165ddee69c5a6542862b405bc50095926702" - integrity sha512-R9N6uDkVsghHePKh1TEqbnLddO2IY25OcsksyFp/qBe7XYd0PVbKEWxhcdMhpLzE1I6skj5l4aEZ3CRxcbArlA== - table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -13757,11 +13749,6 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -toposort@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" - integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= - tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -13919,6 +13906,11 @@ typescript@~3.7.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== +ua-parser-js@^0.7.20: + version "0.7.21" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" + integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + unfetch@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" @@ -14799,20 +14791,12 @@ yargs@^13.3.0, yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yup@^0.29.1: - version "0.29.3" - resolved "https://registry.yarnpkg.com/yup/-/yup-0.29.3.tgz#69a30fd3f1c19f5d9e31b1cf1c2b851ce8045fea" - integrity sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ== - dependencies: - "@babel/runtime" "^7.10.5" - fn-name "~3.0.0" - lodash "^4.17.15" - lodash-es "^4.17.11" - property-expr "^2.0.2" - synchronous-promise "^2.0.13" - toposort "^2.0.2" - zod@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/zod/-/zod-1.10.2.tgz#fd2585bfcabc6a1dee267815ce0a036f184a5473" integrity sha512-/T7CBnpJNf2hOlFburyOSP56nedBqrhwTgrwTKp3xPcZzfdRgRXORVHgDLMOIUQUVJyZbDrQqbS2RHuU/2XmHg== + +zod@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-1.10.3.tgz#a69e60a96115d8968020e42aa0d7d1903e3e50c7" + integrity sha512-TxvLXUx7red0MKzx85sOwcoAWSsO+ChnYIsPzV/rWouS57yagZZlPp2bBTaXOu685lZE8H9vQK2nJs1vxleQTA== From d5fa741a05e014c3c48469ad697b15fa4d417516 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 11:09:48 +0200 Subject: [PATCH 09/19] fix wrap on h1 --- src/index.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.scss b/src/index.scss index 83fd0f2..a2482e8 100644 --- a/src/index.scss +++ b/src/index.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -@import "color-scheme"; +@import 'color-scheme'; // CSS reset @@ -47,6 +47,7 @@ h1 { font-size: 24px; line-height: 32px; text-align: center; + word-break: break-all; } a { From 2ecc505e78f1ca35b4fc1191846b06b57be71171 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 1 Sep 2020 11:38:34 +0200 Subject: [PATCH 10/19] Fix generated links --- package.json | 2 - src/components/CreateLinkTile.scss | 6 ++ src/components/CreateLinkTile.tsx | 7 +- yarn.lock | 101 ++--------------------------- 4 files changed, 14 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index ad67d5f..586b537 100644 --- a/package.json +++ b/package.json @@ -69,14 +69,12 @@ "@types/node": "^12.0.0", "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", - "@types/react-router-dom": "^5.1.5", "@types/yup": "^0.29.3", "eslint-config-matrix-org": "^0.1.0", "husky": "^4.2.5", "lint-staged": "^10.2.7", "node-sass": "^4.14.1", "prettier": "^2.0.5", - "react-router-dom": "^5.2.0", "storybook-addon-designs": "^5.4.0", "ts-jest": "^26.1.4", "typescript": "~3.7.2" diff --git a/src/components/CreateLinkTile.scss b/src/components/CreateLinkTile.scss index d5aadc5..8222c3c 100644 --- a/src/components/CreateLinkTile.scss +++ b/src/components/CreateLinkTile.scss @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +@import '../color-scheme'; + .createLinkTile { background: none; @@ -29,4 +31,8 @@ limitations under the License. align-self: center; padding: 0 30px; } + + > a { + color: $foreground; + } } diff --git a/src/components/CreateLinkTile.tsx b/src/components/CreateLinkTile.tsx index 06b0c97..51a79eb 100644 --- a/src/components/CreateLinkTile.tsx +++ b/src/components/CreateLinkTile.tsx @@ -21,7 +21,6 @@ import Tile from './Tile'; import Button from './Button'; import TextButton from './TextButton'; import Input from './Input'; - import { parseHash } from '../parser/parser'; import { LinkKind } from '../parser/types'; @@ -68,7 +67,7 @@ const LinkNotCreatedTile: React.FC = ( document.location.protocol + '//' + document.location.host + - '/' + + '/#/' + values.identifier ); }} @@ -106,7 +105,9 @@ const LinkCreatedTile: React.FC = (props) => { props.setLink('')}> Create another lnk -

    {props.link}

    + +

    {props.link}

    +
    + ) : null; + return (
    {options} - + + {clearSelection}
    ); }; diff --git a/src/components/LinkPreview.tsx b/src/components/LinkPreview.tsx index 43abb10..8796751 100644 --- a/src/components/LinkPreview.tsx +++ b/src/components/LinkPreview.tsx @@ -92,15 +92,14 @@ const LinkPreview: React.FC = ({ link }: IProps) => { (async (): Promise => setContent(await invite({ link })))(); }, [link]); - const [{ rememberSelection, clientId }] = useContext(ClientContext); + const [{ clientId }] = useContext(ClientContext); // Select which client to link to - const displayClientId = - rememberSelection && clientId - ? clientId - : link.arguments.client - ? link.arguments.client - : null; + const displayClientId = clientId + ? clientId + : link.arguments.client + ? link.arguments.client + : null; const client = displayClientId ? clientMap[displayClientId] : null; diff --git a/src/contexts/ClientContext.ts b/src/contexts/ClientContext.ts index 52da9f2..0c78807 100644 --- a/src/contexts/ClientContext.ts +++ b/src/contexts/ClientContext.ts @@ -23,7 +23,6 @@ import { persistReducer } from '../utils/localStorage'; const STATE_SCHEMA = object({ clientId: string().nullable(), showOnlyDeviceClients: boolean(), - rememberSelection: boolean(), showExperimentalClients: boolean(), }); @@ -32,7 +31,7 @@ type State = TypeOf; // Actions are a discriminated union. export enum ActionType { SetClient = 'SET_CLIENT', - ToggleRememberSelection = 'TOGGLE_REMEMBER_SELECTION', + ClearClient = 'CLEAR_CLIENT', ToggleShowOnlyDeviceClients = 'TOGGLE_SHOW_ONLY_DEVICE_CLIENTS', ToggleShowExperimentalClients = 'TOGGLE_SHOW_EXPERIMENTAL_CLIENTS', } @@ -42,8 +41,8 @@ interface SetClient { clientId: ClientId; } -interface ToggleRememberSelection { - action: ActionType.ToggleRememberSelection; +interface ClearClient { + action: ActionType.ClearClient; } interface ToggleShowOnlyDeviceClients { @@ -56,13 +55,12 @@ interface ToggleShowExperimentalClients { export type Action = | SetClient - | ToggleRememberSelection + | ClearClient | ToggleShowOnlyDeviceClients | ToggleShowExperimentalClients; const INITIAL_STATE: State = { clientId: null, - rememberSelection: false, showOnlyDeviceClients: true, showExperimentalClients: false, }; @@ -78,11 +76,6 @@ export const [initialState, reducer] = persistReducer( ...state, clientId: action.clientId, }; - case ActionType.ToggleRememberSelection: - return { - ...state, - rememberSelection: !state.rememberSelection, - }; case ActionType.ToggleShowOnlyDeviceClients: return { ...state, @@ -93,8 +86,11 @@ export const [initialState, reducer] = persistReducer( ...state, showExperimentalClients: !state.showExperimentalClients, }; - default: - return state; + case ActionType.ClearClient: + return { + ...state, + clientId: null, + }; } } ); From a82c318ecf7ff2cd070e6e1d46933e4be4db42c7 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 10 Sep 2020 11:27:47 +0100 Subject: [PATCH 15/19] Update client selection to match figma --- src/App.scss | 2 +- src/components/ClientSelection.scss | 2 -- src/components/ClientSelection.tsx | 1 + src/components/ClientTile.scss | 37 ++++++++++++++++--------- src/components/ClientTile.tsx | 8 ++++++ src/components/FakeProgress.scss | 31 +++++++++++++++++++++ src/components/FakeProgress.tsx | 27 +++++++++++++++++++ src/components/InviteTile.scss | 8 ++++-- src/components/InviteTile.tsx | 42 +++++++++++++++++++---------- src/index.scss | 5 ++++ src/layouts/SingleColumn.scss | 5 ---- 11 files changed, 132 insertions(+), 36 deletions(-) create mode 100644 src/components/FakeProgress.scss create mode 100644 src/components/FakeProgress.tsx diff --git a/src/App.scss b/src/App.scss index 6b61de3..7179a17 100644 --- a/src/App.scss +++ b/src/App.scss @@ -20,7 +20,7 @@ limitations under the License. background-color: $app-background; background-image: url('./imgs/background.svg'); background-repeat: none; - background-position: 50% 10%; + background-position: 50% -20%; } @mixin spacer { diff --git a/src/components/ClientSelection.scss b/src/components/ClientSelection.scss index 4754359..f4977b7 100644 --- a/src/components/ClientSelection.scss +++ b/src/components/ClientSelection.scss @@ -15,8 +15,6 @@ limitations under the License. */ .advanced { - margin: 0 5%; - display: grid; row-gap: 20px; diff --git a/src/components/ClientSelection.tsx b/src/components/ClientSelection.tsx index 794ba0a..8a41d4a 100644 --- a/src/components/ClientSelection.tsx +++ b/src/components/ClientSelection.tsx @@ -84,6 +84,7 @@ const ClientSelection: React.FC = ({ link }: IProps) => { return (
    {options} +

    Clients you can accept this invite with

    {clearSelection}
    diff --git a/src/components/ClientTile.scss b/src/components/ClientTile.scss index 18a8be0..0b8f091 100644 --- a/src/components/ClientTile.scss +++ b/src/components/ClientTile.scss @@ -19,36 +19,49 @@ limitations under the License. .clientTile { display: flex; flex-direction: row; + align-items: center; - height: 155px; + min-height: 150px; width: 100%; color: $foreground; > img { flex-shrink: 0; - height: 100%; + height: 130px; } - h1 { - text-align: left; - font-size: 14px; - line-height: 24px; - } + > div { + display: flex; + flex-direction: column; + justify-content: space-between; + h1 { + text-align: left; + font-size: 14px; + line-height: 24px; + } - p { - margin-right: 20px; - margin-top: 20px; - text-align: left; + p { + margin-right: 20px; + text-align: left; + } + + .button { + margin: 5px; + } } border: 1px solid $borders; border-radius: 8px; - padding: 8px; + padding: 15px; // For the chevron position: relative; + + &::hover { + background-color: $grey; + } } .clientTileLink { diff --git a/src/components/ClientTile.tsx b/src/components/ClientTile.tsx index 6367b72..7644782 100644 --- a/src/components/ClientTile.tsx +++ b/src/components/ClientTile.tsx @@ -20,6 +20,7 @@ import classNames from 'classnames'; import { Client, ClientKind } from '../clients/types'; import { SafeLink } from '../parser/types'; import Tile from './Tile'; +import Button from './Button'; import './ClientTile.scss'; @@ -37,6 +38,12 @@ const ClientTile: React.FC = ({ client, link }: IProps) => { const className = classNames('clientTile', { clientTileLink: client.kind === ClientKind.LINKED_CLIENT, }); + + const inviteButton = + client.kind === ClientKind.LINKED_CLIENT ? ( + + ) : null; + let clientTile = ( {client.name @@ -44,6 +51,7 @@ const ClientTile: React.FC = ({ client, link }: IProps) => {

    {client.name}

    {client.description}

    {inviteLine} + {inviteButton}
    ); diff --git a/src/components/FakeProgress.scss b/src/components/FakeProgress.scss new file mode 100644 index 0000000..32cd224 --- /dev/null +++ b/src/components/FakeProgress.scss @@ -0,0 +1,31 @@ +/* +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'; + +.fakeProgress { + width: 100%; + height: 4px; + background-color: lighten($grey, 50%); + border-radius: 4px; + + > div { + width: 60%; + height: 100%; + background-color: $foreground; + border-radius: 4px; + } +} diff --git a/src/components/FakeProgress.tsx b/src/components/FakeProgress.tsx new file mode 100644 index 0000000..587a7e3 --- /dev/null +++ b/src/components/FakeProgress.tsx @@ -0,0 +1,27 @@ +/* +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 './FakeProgress.scss'; + +const FakeProgress = () => ( +
    +
    +
    +); + +export default FakeProgress; diff --git a/src/components/InviteTile.scss b/src/components/InviteTile.scss index 35d45c5..0421927 100644 --- a/src/components/InviteTile.scss +++ b/src/components/InviteTile.scss @@ -18,7 +18,11 @@ limitations under the License. display: grid; row-gap: 24px; - .advancedPlaceholder { - height: 245px; + .inviteTileClientSelection { + margin: 0 5%; + display: grid; + + justify-content: space-between; + row-gap: 20px; } } diff --git a/src/components/InviteTile.tsx b/src/components/InviteTile.tsx index b719f7d..1311f9f 100644 --- a/src/components/InviteTile.tsx +++ b/src/components/InviteTile.tsx @@ -20,10 +20,12 @@ import './InviteTile.scss'; import Tile from './Tile'; import LinkButton from './LinkButton'; +import Button from './Button'; import ClientSelection from './ClientSelection'; import { Client, ClientKind } from '../clients/types'; import { SafeLink } from '../parser/types'; import TextButton from './TextButton'; +import FakeProgress from './FakeProgress'; interface IProps { children?: React.ReactNode; @@ -35,12 +37,15 @@ const InviteTile: React.FC = ({ children, client, link }: IProps) => { const [showAdvanced, setShowAdvanced] = useState(false); let invite: React.ReactNode; let advanced: React.ReactNode; - // This i s a hacky way to get a the overlapping list of client - // options working. - let advancedPlaceholder: React.ReactNode; if (client === null) { - invite = null; + invite = showAdvanced ? ( + + ) : ( + + ); } else { let inviteUseString: string; @@ -60,11 +65,7 @@ const InviteTile: React.FC = ({ children, client, link }: IProps) => { break; } - const advancedToggle = showAdvanced ? ( - setShowAdvanced(!showAdvanced)}> - Hide advanced options - - ) : ( + const advancedToggle = (

    {inviteUseString} = ({ children, client, link }: IProps) => { ); } - if (client === null || showAdvanced) { - advanced = ; - advancedPlaceholder =

    ; + if (showAdvanced) { + if (client === null) { + advanced = ( + <> +

    Pick an app to accept the invite with

    + + + ); + } else { + advanced = ( + <> +
    +

    Change app

    + + + ); + } } return ( @@ -93,9 +108,8 @@ const InviteTile: React.FC = ({ children, client, link }: IProps) => { {children} {invite} - {advancedPlaceholder} +
    {advanced}
    - {advanced} ); }; diff --git a/src/index.scss b/src/index.scss index 2c9c506..a9b22d8 100644 --- a/src/index.scss +++ b/src/index.scss @@ -49,6 +49,11 @@ h1 { text-align: center; } +h4 { + text-align: left; + width: 100%; +} + a { color: $link; text-decoration: none; diff --git a/src/layouts/SingleColumn.scss b/src/layouts/SingleColumn.scss index 045504f..00edf39 100644 --- a/src/layouts/SingleColumn.scss +++ b/src/layouts/SingleColumn.scss @@ -10,9 +10,4 @@ row-gap: 60px; align-items: center; - - .advanced { - position: relative; - top: -335px; - } } From 154f82f7d47fecd7c04b3dee6215e9f941be892b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 10 Sep 2020 11:31:01 +0100 Subject: [PATCH 16/19] Stop background from wrapping --- src/App.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.scss b/src/App.scss index 7179a17..0f0855e 100644 --- a/src/App.scss +++ b/src/App.scss @@ -19,7 +19,7 @@ limitations under the License. #root { background-color: $app-background; background-image: url('./imgs/background.svg'); - background-repeat: none; + background-repeat: no-repeat; background-position: 50% -20%; } From 4921513e13ef26d63ab4f973a60c2e3b9c83cf66 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 10 Sep 2020 11:52:13 +0100 Subject: [PATCH 17/19] Automaitcally copy invite information --- src/clients/Weechat.tsx | 11 +++++++++++ src/clients/types.ts | 1 + src/components/ClientTile.tsx | 11 ++++++++++- src/components/InviteTile.tsx | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/clients/Weechat.tsx b/src/clients/Weechat.tsx index e88dc24..a29c150 100644 --- a/src/clients/Weechat.tsx +++ b/src/clients/Weechat.tsx @@ -57,6 +57,17 @@ const Weechat: TextClient = { return Weechat doesn't support this kind of link; } }, + copyString: (link) => { + switch (link.kind) { + case LinkKind.Alias: + case LinkKind.RoomId: + return `/join ${link.identifier}`; + case LinkKind.UserId: + return `/invite ${link.identifier}`; + default: + return ''; + } + }, description: 'Commandline Matrix interface using Weechat', }; diff --git a/src/clients/types.ts b/src/clients/types.ts index 2df503b..cdaed27 100644 --- a/src/clients/types.ts +++ b/src/clients/types.ts @@ -81,6 +81,7 @@ export interface LinkedClient extends ClientDescription { export interface TextClient extends ClientDescription { kind: ClientKind.TEXT_CLIENT; toInviteString(parsedLink: SafeLink): JSX.Element; + copyString(parsedLink: SafeLink): string; } /* diff --git a/src/components/ClientTile.tsx b/src/components/ClientTile.tsx index 7644782..ba3e57d 100644 --- a/src/components/ClientTile.tsx +++ b/src/components/ClientTile.tsx @@ -42,7 +42,16 @@ const ClientTile: React.FC = ({ client, link }: IProps) => { const inviteButton = client.kind === ClientKind.LINKED_CLIENT ? ( - ) : null; + ) : ( + + ); let clientTile = ( diff --git a/src/components/InviteTile.tsx b/src/components/InviteTile.tsx index 1311f9f..a35b2c7 100644 --- a/src/components/InviteTile.tsx +++ b/src/components/InviteTile.tsx @@ -61,6 +61,7 @@ const InviteTile: React.FC = ({ children, client, link }: IProps) => { case ClientKind.TEXT_CLIENT: // TODO: copy to clipboard invite =

    {client.toInviteString(link)}

    ; + navigator.clipboard.writeText(client.copyString(link)); inviteUseString = `These are instructions for ${client.name}.`; break; } From 41b803fbe121d1b22384625c6ce8559a5baec955 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 10 Sep 2020 12:54:02 +0200 Subject: [PATCH 18/19] Update weechat description Co-authored-by: J. Ryan Stinnett --- src/clients/Weechat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clients/Weechat.tsx b/src/clients/Weechat.tsx index a29c150..ff6b528 100644 --- a/src/clients/Weechat.tsx +++ b/src/clients/Weechat.tsx @@ -68,7 +68,7 @@ const Weechat: TextClient = { return ''; } }, - description: 'Commandline Matrix interface using Weechat', + description: 'Command-line Matrix interface using Weechat', }; export default Weechat; From 40aa7769f8350645ffc6f02120473726a9e711f9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Sun, 13 Sep 2020 15:00:04 +0100 Subject: [PATCH 19/19] Typo Co-authored-by: J. Ryan Stinnett --- src/components/CreateLinkTile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CreateLinkTile.tsx b/src/components/CreateLinkTile.tsx index 51a79eb..3553289 100644 --- a/src/components/CreateLinkTile.tsx +++ b/src/components/CreateLinkTile.tsx @@ -103,7 +103,7 @@ const LinkCreatedTile: React.FC = (props) => { return ( props.setLink('')}> - Create another lnk + Create another link

    {props.link}