Implement minimum amount for a working matrix.to

This commit is contained in:
Jorik Schellekens
2020-08-17 17:48:13 +01:00
parent f8fe32ffbc
commit 1ad11ed25f
28 changed files with 703 additions and 99 deletions
+1
View File
@@ -35,5 +35,6 @@ export const Default: React.FC<{}> = () => (
avatar_url: "mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf",
displayname: "Jorik Schellekens",
}}
userId="@jorik:matrix.org"
/>
);
+16 -10
View File
@@ -16,9 +16,9 @@ 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 { Room, User } from "matrix-cypher";
import { convertMXCtoMediaQuery } from "../utils/cypher-wrapper";
import logo from "../imgs/matrix-logo.svg";
import "./Avatar.scss";
@@ -38,7 +38,7 @@ const Avatar: React.FC<IProps> = ({ className, avatarUrl, label }: IProps) => {
return (
<img
src={src}
onError={(_) => setSrc(logo)}
onError={(): void => setSrc(logo)}
alt={label}
className={classNames("avatar", className)}
/>
@@ -47,18 +47,24 @@ const Avatar: React.FC<IProps> = ({ className, avatarUrl, label }: IProps) => {
interface IPropsUserAvatar {
user: User;
userId: string;
}
export const UserAvatar: React.FC<IPropsUserAvatar> = ({
user,
userId,
}: IPropsUserAvatar) => (
<Avatar
avatarUrl={convertMXCtoMediaQuery(
// TODO: replace with correct client
"matrix.org",
avatarUrl={
user.avatar_url
)}
label={user.displayname}
? convertMXCtoMediaQuery(
// TODO: replace with correct client
"https://matrix.org",
user.avatar_url
)
: ""
}
label={user.displayname ? user.displayname : userId}
/>
);
@@ -74,7 +80,7 @@ export const RoomAvatar: React.FC<IPropsRoomAvatar> = ({
room.avatar_url
? convertMXCtoMediaQuery(
// TODO: replace with correct client
"matrix.org",
"https://matrix.org",
room.avatar_url
)
: ""
+1 -1
View File
@@ -22,7 +22,7 @@ import Button from "./Button";
export default { title: "Button" };
export const WithText = () => (
export const WithText: React.FC = () => (
<Button onClick={action("clicked")}>
{text("label", "Hello Story Book")}
</Button>
+1 -1
View File
@@ -29,4 +29,4 @@ export default {
},
};
export const Default = () => <CreateLinkTile />;
export const Default: React.FC = () => <CreateLinkTile />;
+9 -5
View File
@@ -28,7 +28,9 @@ interface ILinkNotCreatedTileProps {
setLink: React.Dispatch<React.SetStateAction<string>>;
}
const LinkNotCreatedTile = (props: ILinkNotCreatedTileProps) => {
const LinkNotCreatedTile: React.FC<ILinkNotCreatedTileProps> = (
props: ILinkNotCreatedTileProps
) => {
return (
<Tile className="createLinkTile">
<h1>
@@ -48,7 +50,7 @@ const LinkNotCreatedTile = (props: ILinkNotCreatedTileProps) => {
)
.required("Required"),
})}
onSubmit={(values) => {
onSubmit={(values): void => {
props.setLink(
document.location.protocol +
"//" +
@@ -80,7 +82,7 @@ const LinkCreatedTile: React.FC<ILinkCreatedTileProps> = (props) => {
const buttonRef = useRef<HTMLButtonElement>(null);
// Focus button on render
useEffect(() => {
useEffect((): void => {
if (buttonRef && buttonRef.current) {
buttonRef.current.focus();
}
@@ -88,13 +90,15 @@ const LinkCreatedTile: React.FC<ILinkCreatedTileProps> = (props) => {
return (
<Tile className="createLinkTile">
<TextButton onClick={() => props.setLink("")}>
<TextButton onClick={(): void => props.setLink("")}>
Create another lnk
</TextButton>
<h1>{props.link}</h1>
<Button
flashChildren={"Copied"}
onClick={() => navigator.clipboard.writeText(props.link)}
onClick={(): void => {
navigator.clipboard.writeText(props.link);
}}
ref={buttonRef}
>
Copy Link
+1 -2
View File
@@ -15,8 +15,7 @@ limitations under the License.
*/
import React from "react";
import { Room } from "cypher/src/schemas/PublicRoomsSchema";
import { Event } from "cypher/src/schemas/EventSchema";
import { Room, Event } from "matrix-cypher";
import RoomPreview from "./RoomPreview";
+2 -2
View File
@@ -32,8 +32,8 @@ export default {
decorators: [withDesign],
};
export const Default = () => (
<Formik initialValues={{}} onSubmit={() => {}}>
export const Default: React.FC = () => (
<Formik initialValues={{}} onSubmit={(): void => {}}>
<Form>
<Input
name="Example input"
+25 -30
View File
@@ -19,6 +19,8 @@ import React from "react";
import InviteTile from "./InviteTile";
import UserPreview, { InviterPreview } from "./UserPreview";
import RoomPreview, { RoomPreviewWithTopic } from "./RoomPreview";
import Clients from "../clients";
import { LinkKind, SafeLink } from "../parser/types";
export default {
title: "InviteTile",
@@ -31,35 +33,38 @@ export default {
},
};
const userLink: SafeLink = {
kind: LinkKind.UserId,
identifier: "@jorik:matrix.org",
arguments: {
vias: [],
},
originalLink: "asdfsadf",
};
const roomLink: SafeLink = {
kind: LinkKind.Alias,
identifier: "#element-dev:matrix.org",
arguments: {
vias: [],
},
originalLink: "asdfsadf",
};
export const withLink: React.FC<{}> = () => (
<InviteTile
inviteAction={{
type: "link",
link: "https://app.element.io/#/room/#asdfasf:matrix.org",
}}
>
<InviteTile client={Clients[0]} link={userLink}>
This is an invite with a link
</InviteTile>
);
export const withInstruction: React.FC<{}> = () => (
<InviteTile
inviteAction={{
type: "instruction",
text: "Type /join #asdfasf:matrix.org",
}}
>
<InviteTile client={Clients[0]} link={userLink}>
This is an invite with an instruction
</InviteTile>
);
export const withUserPreview: React.FC<{}> = () => (
<InviteTile
inviteAction={{
type: "link",
link: "https://app.element.io/#/room/#asdfasf:matrix.org",
}}
>
<InviteTile client={Clients[0]} link={userLink}>
<UserPreview
user={{
avatar_url: "mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf",
@@ -71,12 +76,7 @@ export const withUserPreview: React.FC<{}> = () => (
);
export const withRoomPreviewAndRoomTopic: React.FC<{}> = () => (
<InviteTile
inviteAction={{
type: "link",
link: "https://app.element.io/#/room/#asdfasf:matrix.org",
}}
>
<InviteTile client={Clients[0]} link={roomLink}>
<RoomPreviewWithTopic
room={{
aliases: ["#murrays:cheese.bar"],
@@ -93,12 +93,7 @@ export const withRoomPreviewAndRoomTopic: React.FC<{}> = () => (
);
export const withRoomPreviewAndInviter: React.FC<{}> = () => (
<InviteTile
inviteAction={{
type: "link",
link: "https://app.element.io/#/room/#asdfasf:matrix.org",
}}
>
<InviteTile client={Clients[0]} link={roomLink}>
<InviterPreview
user={{
avatar_url: "mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf",
+14 -21
View File
@@ -16,39 +16,32 @@ limitations under the License.
import React from "react";
import "./InviteTile.scss";
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;
import { Client, ClientKind } from "../clients/types";
import { SafeLink } from "../parser/types";
interface IProps {
children?: React.ReactNode;
inviteAction: InviteAction;
client: Client;
link: SafeLink;
}
const InviteTile: React.FC<IProps> = ({ children, inviteAction }: IProps) => {
const InviteTile: React.FC<IProps> = ({ children, client, link }: IProps) => {
let invite: React.ReactNode;
switch (inviteAction.type) {
case "link":
switch (client.kind) {
case ClientKind.LINKED_CLIENT:
invite = (
<LinkButton href={inviteAction.link}>Accept invite</LinkButton>
<LinkButton href={client.toUrl(link).toString()}>
Accept invite
</LinkButton>
);
break;
case "instruction":
invite = <p>{inviteAction.text}</p>;
case ClientKind.TEXT_CLIENT:
invite = <p>{client.toInviteString(link)}</p>;
break;
}
+126
View File
@@ -0,0 +1,126 @@
/*
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, { useState, useEffect } from "react";
import { Client, getEvent, client } from "matrix-cypher";
import {
getRoomIdFromAlias,
searchPublicRooms,
getUserDetails,
} from "../utils/cypher-wrapper";
import { RoomPreviewWithTopic } from "./RoomPreview";
import InviteTile from "./InviteTile";
import { SafeLink } from "../parser/types";
import { LinkKind } from "../parser/types";
import UserPreview from "./UserPreview";
import EventPreview from "./EventPreview";
import Clients from "../clients";
interface IProps {
link: SafeLink;
}
// TODO: replace with client fetch
const defaultClient: () => Promise<Client> = () => client("https://matrix.org");
const LOADING: JSX.Element = <>Generating invite</>;
const invite = async ({ link }: { link: SafeLink }): Promise<JSX.Element> => {
switch (link.kind) {
case LinkKind.Alias:
return (
<RoomPreviewWithTopic
room={
await searchPublicRooms(
await defaultClient(),
(
await getRoomIdFromAlias(
await defaultClient(),
link.identifier
)
).room_id
)
}
/>
);
case LinkKind.RoomId:
return (
<RoomPreviewWithTopic
room={
await searchPublicRooms(
await defaultClient(),
link.identifier
)
}
/>
);
case LinkKind.UserId:
return (
<UserPreview
user={
await getUserDetails(
await defaultClient(),
link.identifier
)
}
userId={link.identifier}
/>
);
case LinkKind.Permalink:
return (
<EventPreview
room={
await searchPublicRooms(
await defaultClient(),
link.identifier
)
}
event={
await getEvent(
await defaultClient(),
link.roomLink,
link.eventId
)
}
/>
);
default:
// Todo Implement events
return <></>;
}
};
const LinkPreview: React.FC<IProps> = ({ link }: IProps) => {
const [content, setContent] = useState(LOADING);
useEffect(() => {
(async (): Promise<void> => setContent(await invite({ link })))();
}, [link]);
return (
<InviteTile client={Clients[0]} link={link}>
{content}
</InviteTile>
);
};
export default LinkPreview;
+1 -1
View File
@@ -20,4 +20,4 @@ import MatrixTile from "./MatrixTile";
export default { title: "MatrixTile" };
export const Default = () => <MatrixTile />;
export const Default: React.FC = () => <MatrixTile />;
+2 -2
View File
@@ -15,7 +15,7 @@ limitations under the License.
*/
import React from "react";
import { Room } from "cypher/src/schemas/PublicRoomsSchema";
import { Room } from "matrix-cypher";
import { RoomAvatar } from "./Avatar";
@@ -30,7 +30,7 @@ const RoomPreview: React.FC<IProps> = ({ room }: IProps) => {
return (
<div className="roomPreview">
<RoomAvatar room={room} />
<h1>{room.name}</h1>
<h1>{room.name ? room.name : room.room_id}</h1>
<p>{room.num_joined_members.toLocaleString()} members</p>
<p>{roomAlias}</p>
</div>
+3 -1
View File
@@ -29,4 +29,6 @@ export default {
},
};
export const Default = () => <TextButton>This is a button?</TextButton>;
export const Default: React.FC = () => (
<TextButton>This is a button?</TextButton>
);
+1 -1
View File
@@ -29,7 +29,7 @@ export default {
},
};
export const Default = () => (
export const Default: React.FC = () => (
<Tile>
<h1>This is a tile</h1>
<p>Some text</p>
+3 -3
View File
@@ -15,7 +15,7 @@ limitations under the License.
*/
import React from "react";
import { User } from "cypher/src/schemas/UserSchema";
import { User } from "matrix-cypher";
import { UserAvatar } from "./Avatar";
@@ -28,7 +28,7 @@ interface IProps {
const UserPreview: React.FC<IProps> = ({ user, userId }: IProps) => (
<div className="userPreview">
<UserAvatar user={user} />
<UserAvatar user={user} userId={userId} />
<h1>{user.displayname} invites you to connect</h1>
<p>{userId}</p>
<hr />
@@ -45,6 +45,6 @@ export const InviterPreview: React.FC<IProps> = ({ user, userId }: IProps) => (
</h1>
<p>{userId}</p>
</div>
<UserAvatar user={user} />
<UserAvatar user={user} userId={userId} />
</div>
);