From ba90c5558d38229cb7dd998beae42779628146aa Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 6 Aug 2020 16:38:26 +0100 Subject: [PATCH] Add preview components --- .gitignore | 4 +- .storybook/preview.js | 8 +- README.md | 3 + src/App.scss | 2 + src/_color-scheme.scss | 1 + src/components/Avatar.scss | 24 +++++ src/components/Avatar.stories.tsx | 39 ++++++++ src/components/Avatar.tsx | 86 ++++++++++++++++++ src/components/EventPreview.tsx | 36 ++++++++ src/components/InviteTile.scss | 25 ++++++ src/components/InviteTile.stories.tsx | 122 ++++++++++++++++++++++++++ src/components/InviteTile.tsx | 64 ++++++++++++++ src/components/LinkButton.tsx | 28 ++++++ src/components/MatrixTile.tsx | 4 +- src/components/RoomPreview.scss | 31 +++++++ src/components/RoomPreview.tsx | 50 +++++++++++ src/components/Tile.scss | 8 +- src/components/UserPreview.scss | 74 ++++++++++++++++ src/components/UserPreview.tsx | 50 +++++++++++ src/contexts/ClientContext.ts | 65 ++++++++++++++ 20 files changed, 716 insertions(+), 8 deletions(-) create mode 100644 src/components/Avatar.scss create mode 100644 src/components/Avatar.stories.tsx create mode 100644 src/components/Avatar.tsx create mode 100644 src/components/EventPreview.tsx create mode 100644 src/components/InviteTile.scss create mode 100644 src/components/InviteTile.stories.tsx create mode 100644 src/components/InviteTile.tsx create mode 100644 src/components/LinkButton.tsx create mode 100644 src/components/RoomPreview.scss create mode 100644 src/components/RoomPreview.tsx create mode 100644 src/components/UserPreview.scss create mode 100644 src/components/UserPreview.tsx create mode 100644 src/contexts/ClientContext.ts diff --git a/.gitignore b/.gitignore index 3a9c6a5..0bd7603 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -.vercel \ No newline at end of file +.vercel + +storybook-static diff --git a/.storybook/preview.js b/.storybook/preview.js index cff5d31..bebbd5b 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -5,13 +5,15 @@ import { withKnobs } from '@storybook/addon-knobs'; import { withDesign } from 'storybook-addon-designs' import { addParameters } from '@storybook/react'; +import SingleColumn from '../src/layouts/SingleColumn'; + // Default styles import "../src/index.scss"; addDecorator( - storyFn =>
{storyFn()}
+ storyFn => {storyFn()} ); addDecorator(withA11y); @@ -22,7 +24,7 @@ addDecorator(withDesign); addParameters({ backgrounds: [ - {name: 'light', value: '#F4F4F4'}, - {name: 'white', value: '#FFFFFF', default: true}, + {name: 'light', value: '#F4F4F4', default: true}, + {name: 'white', value: '#FFFFFF'}, ], }); diff --git a/README.md b/README.md index 42c4536..0694cfb 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,6 @@ You can discuss matrix.to in [`#matrix.to:matrix.org`](https://matrix.to/#/#matrix.to:matrix.org) A development build of matrix-two can be found at https://matrix-to.vercel.app + +A preview of all components can be found at +https://matrix-to-storybook.vercel.app diff --git a/src/App.scss b/src/App.scss index 648cdf6..1d4ff36 100644 --- a/src/App.scss +++ b/src/App.scss @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +@import "./color-scheme"; + #root { background-color: $app-background; } diff --git a/src/_color-scheme.scss b/src/_color-scheme.scss index d7fc24c..1c32596 100644 --- a/src/_color-scheme.scss +++ b/src/_color-scheme.scss @@ -22,3 +22,4 @@ $grey: #666666; $accent: #0098d4; $error: #d6001c; $link: #0098d4; +$borders: #f4f4f4; diff --git a/src/components/Avatar.scss b/src/components/Avatar.scss new file mode 100644 index 0000000..7895a82 --- /dev/null +++ b/src/components/Avatar.scss @@ -0,0 +1,24 @@ +/* +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"; + +.avatar { + border-radius: 100%; + border: 1px solid $borders; + height: 50px; + width: 50px; +} diff --git a/src/components/Avatar.stories.tsx b/src/components/Avatar.stories.tsx new file mode 100644 index 0000000..d586c6f --- /dev/null +++ b/src/components/Avatar.stories.tsx @@ -0,0 +1,39 @@ +/* +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 { UserAvatar } from "./Avatar"; + +export default { + title: "Avatar", + parameters: { + design: { + type: "figma", + url: + "https://www.figma.com/file/WSXjCGc1k6FVI093qhlzOP/04-Recieving-share-link?node-id=143%3A5853", + }, + }, +}; + +export const Default: React.FC<{}> = () => ( + +); diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx new file mode 100644 index 0000000..1a73b9f --- /dev/null +++ b/src/components/Avatar.tsx @@ -0,0 +1,86 @@ +/* +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, { useEffect, useState } from "react"; +import classNames from "classnames"; +import { convertMXCtoMediaQuery } from "cypher"; +import { Room } from "cypher/src/schemas/PublicRoomsSchema"; +import { User } from "cypher/src/schemas/UserSchema"; +import logo from "../imgs/matrix-logo.svg"; + +import "./Avatar.scss"; + +interface IProps { + className?: string; + avatarUrl: string; + label: string; +} + +const Avatar: React.FC = ({ className, avatarUrl, label }: IProps) => { + const [src, setSrc] = useState(avatarUrl); + useEffect(() => { + setSrc(avatarUrl); + }, [avatarUrl]); + + return ( + setSrc(logo)} + alt={label} + className={classNames("avatar", className)} + /> + ); +}; + +interface IPropsUserAvatar { + user: User; +} + +export const UserAvatar: React.FC = ({ + user, +}: IPropsUserAvatar) => ( + +); + +interface IPropsRoomAvatar { + room: Room; +} + +export const RoomAvatar: React.FC = ({ + room, +}: IPropsRoomAvatar) => ( + +); + +export default Avatar; diff --git a/src/components/EventPreview.tsx b/src/components/EventPreview.tsx new file mode 100644 index 0000000..f0eef35 --- /dev/null +++ b/src/components/EventPreview.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 from "react"; +import { Room } from "cypher/src/schemas/PublicRoomsSchema"; +import { Event } from "cypher/src/schemas/EventSchema"; + +import RoomPreview from "./RoomPreview"; + +interface IProps { + room: Room; + event: Event; +} + +const EventPreview: React.FC = ({ room, event }: IProps) => ( + <> + +

"{event.content}"

+

{event.sender}

+ +); + +export default EventPreview; diff --git a/src/components/InviteTile.scss b/src/components/InviteTile.scss new file mode 100644 index 0000000..d1efbd2 --- /dev/null +++ b/src/components/InviteTile.scss @@ -0,0 +1,25 @@ +/* +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. +*/ + +.inviteTile { + .button { + margin-top: 24px; + } + + > .textButton { + margin-top: 28px; + } +} diff --git a/src/components/InviteTile.stories.tsx b/src/components/InviteTile.stories.tsx new file mode 100644 index 0000000..e42b544 --- /dev/null +++ b/src/components/InviteTile.stories.tsx @@ -0,0 +1,122 @@ +/* +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 InviteTile from "./InviteTile"; +import UserPreview, { InviterPreview } from "./UserPreview"; +import RoomPreview, { RoomPreviewWithTopic } from "./RoomPreview"; + +export default { + title: "InviteTile", + parameters: { + design: { + type: "figma", + url: + "https://figma.com/file/WSXjCGc1k6FVI093qhlzOP/04-Recieving-share-link?node-id=59%3A334", + }, + }, +}; + +export const withLink: React.FC<{}> = () => ( + + This is an invite with a link + +); + +export const withInstruction: React.FC<{}> = () => ( + + This is an invite with an instruction + +); + +export const withUserPreview: React.FC<{}> = () => ( + + + +); + +export const withRoomPreviewAndRoomTopic: React.FC<{}> = () => ( + + + +); + +export const withRoomPreviewAndInviter: React.FC<{}> = () => ( + + + + +); diff --git a/src/components/InviteTile.tsx b/src/components/InviteTile.tsx new file mode 100644 index 0000000..154db4c --- /dev/null +++ b/src/components/InviteTile.tsx @@ -0,0 +1,64 @@ +/* +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 LinkButton from "./LinkButton"; +import TextButton from "./TextButton"; + +import "./InviteTile.scss"; + +export interface InviteLink { + type: "link"; + link: string; +} + +export interface InviteInstruction { + type: "instruction"; + text: string; +} + +type InviteAction = InviteLink | InviteInstruction; + +interface IProps { + children?: React.ReactNode; + inviteAction: InviteAction; +} + +const InviteTile: React.FC = ({ children, inviteAction }: IProps) => { + let invite: React.ReactNode; + switch (inviteAction.type) { + case "link": + invite = ( + Accept invite + ); + break; + case "instruction": + invite =

{inviteAction.text}

; + break; + } + + return ( + + {children} + {invite} + Advanced options + + ); +}; + +export default InviteTile; diff --git a/src/components/LinkButton.tsx b/src/components/LinkButton.tsx new file mode 100644 index 0000000..bd98d73 --- /dev/null +++ b/src/components/LinkButton.tsx @@ -0,0 +1,28 @@ +/* +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 "./Button.scss"; + +interface IProps extends React.LinkHTMLAttributes {} + +const LinkButton: React.FC = ({ className, ...props }: IProps) => ( + +); + +export default LinkButton; diff --git a/src/components/MatrixTile.tsx b/src/components/MatrixTile.tsx index 8855b95..d76d0eb 100644 --- a/src/components/MatrixTile.tsx +++ b/src/components/MatrixTile.tsx @@ -26,8 +26,8 @@ const MatrixTile: React.FC = () => { matrix-logo
- Matrix.to is a stateless URL redirecting service for the{" "} - Matrix ecosystem. + This invite uses Matrix, an + open network for secure, decentralized communication.
); diff --git a/src/components/RoomPreview.scss b/src/components/RoomPreview.scss new file mode 100644 index 0000000..ef82fb9 --- /dev/null +++ b/src/components/RoomPreview.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. +*/ + +.roomPreview { + > .avatar { + margin-top: 20px; + margin-bottom: 16px; + } + + > h1 { + font-size: 20px; + margin-bottom: 4px; + } +} + +.roomTopic { + padding-top: 32px; +} diff --git a/src/components/RoomPreview.tsx b/src/components/RoomPreview.tsx new file mode 100644 index 0000000..9d18ee5 --- /dev/null +++ b/src/components/RoomPreview.tsx @@ -0,0 +1,50 @@ +/* +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 { Room } from "cypher/src/schemas/PublicRoomsSchema"; + +import { RoomAvatar } from "./Avatar"; + +import "./RoomPreview.scss"; + +interface IProps { + room: Room; +} + +const RoomPreview: React.FC = ({ room }: IProps) => { + const roomAlias = room.aliases ? room.aliases[0] : room.room_id; + return ( +
+ +

{room.name}

+

{room.num_joined_members.toLocaleString()} members

+

{roomAlias}

+
+ ); +}; + +export const RoomPreviewWithTopic: React.FC = ({ room }: IProps) => { + const topic = room.topic ?

{room.topic}

: null; + return ( + <> + + {topic} + + ); +}; + +export default RoomPreview; diff --git a/src/components/Tile.scss b/src/components/Tile.scss index 57c22ee..a18f8ad 100644 --- a/src/components/Tile.scss +++ b/src/components/Tile.scss @@ -19,11 +19,15 @@ limitations under the License. .tile { background-color: $background; - border-radius: 8px; - padding: 1rem; + border-radius: 16px; + padding: 2rem; display: grid; justify-items: center; text-align: center; + + p { + color: $grey; + } } diff --git a/src/components/UserPreview.scss b/src/components/UserPreview.scss new file mode 100644 index 0000000..d35dfda --- /dev/null +++ b/src/components/UserPreview.scss @@ -0,0 +1,74 @@ +/* +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"; + +.userPreview { + width: 100%; + + > .avatar { + margin-top: 20px; + margin-bottom: 16px; + } + + h1 { + font-size: 20px; + margin-bottom: 4px; + } + + p { + margin-bottom: 16px; + } + + hr { + width: 100%; + margin: 0; + opacity: 0.2; + } +} + +.miniUserPreview { + width: 100%; + + display: flex; + flex-direction: row; + align-items: center; + + border: 1px solid $app-background; + border-radius: 16px; + padding: 6px 16px; + + > div { + flex-grow: 1; + text-align: left; + } + + h1 { + font-weight: normal; + font-size: 14px; + line-height: 20px; + text-align: left; + } + + p { + line-height: 20px; + } + + .avatar { + flex-grow: 0; + flex-shrink: 0; + } +} diff --git a/src/components/UserPreview.tsx b/src/components/UserPreview.tsx new file mode 100644 index 0000000..e0992fd --- /dev/null +++ b/src/components/UserPreview.tsx @@ -0,0 +1,50 @@ +/* +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 { User } from "cypher/src/schemas/UserSchema"; + +import { UserAvatar } from "./Avatar"; + +import "./UserPreview.scss"; + +interface IProps { + user: User; + userId: string; +} + +const UserPreview: React.FC = ({ user, userId }: IProps) => ( +
+ +

{user.displayname} invites you to connect

+

{userId}

+
+
+); + +export default UserPreview; + +export const InviterPreview: React.FC = ({ user, userId }: IProps) => ( +
+
+

+ Invited by {user.displayname} +

+

{userId}

+
+ +
+); diff --git a/src/contexts/ClientContext.ts b/src/contexts/ClientContext.ts new file mode 100644 index 0000000..0b14e47 --- /dev/null +++ b/src/contexts/ClientContext.ts @@ -0,0 +1,65 @@ +/* +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 { Client, discoverServer } from "cypher"; +import { prefixFetch } from "cypher/src/utils/fetch"; + +type State = { + clientURL: string; + client: Client; +}[]; + +// Actions are a discriminated union. +export enum ActionTypes { + AddClient = "ADD_CLIENT", + RemoveClient = "REMOVE_CLIENT", +} + +export interface AddClient { + action: ActionTypes.AddClient; + clientURL: string; +} + +export interface RemoveClient { + action: ActionTypes.RemoveClient; + clientURL: string; +} + +export type Action = AddClient | RemoveClient; + +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); + + 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; +}; + +// The null is a hack to make the type checker happy +// create context does not need an argument +export default React.createContext(null);