Show sharer preview for matrix.to links

This commit is contained in:
Jorik Schellekens 2020-09-13 17:39:39 +01:00
parent 85fab36308
commit 74b790927e
5 changed files with 128 additions and 62 deletions

View file

@ -19,4 +19,8 @@ limitations under the License.
border-radius: 0; border-radius: 0;
border: 0; border: 0;
} }
h1 {
word-break: break-all;
}
} }

View file

@ -20,10 +20,11 @@ import { getEvent, client } from 'matrix-cypher';
import { RoomPreviewWithTopic } from './RoomPreview'; import { RoomPreviewWithTopic } from './RoomPreview';
import InviteTile from './InviteTile'; import InviteTile from './InviteTile';
import { SafeLink, LinkKind } from '../parser/types'; import { SafeLink, LinkKind } from '../parser/types';
import UserPreview from './UserPreview'; import UserPreview, { WrappedInviterPreview } from './UserPreview';
import EventPreview from './EventPreview'; import EventPreview from './EventPreview';
import HomeserverOptions from './HomeserverOptions'; import HomeserverOptions from './HomeserverOptions';
import DefaultPreview from './DefaultPreview'; import DefaultPreview from './DefaultPreview';
import Toggle from './Toggle';
import { clientMap } from '../clients'; import { clientMap } from '../clients';
import { import {
getRoomFromId, getRoomFromId,
@ -32,12 +33,7 @@ import {
getUser, getUser,
} from '../utils/cypher-wrapper'; } from '../utils/cypher-wrapper';
import { ClientContext } from '../contexts/ClientContext'; import { ClientContext } from '../contexts/ClientContext';
import HSContext, { import useHSs from '../utils/getHS';
TempHSContext,
HSOptions,
State as HSState,
} from '../contexts/HSContext';
import Toggle from './Toggle';
interface IProps { interface IProps {
link: SafeLink; link: SafeLink;
@ -118,32 +114,14 @@ const Preview: React.FC<PreviewProps> = ({ link, client }: PreviewProps) => {
return content; return content;
}; };
function selectedClient(link: SafeLink, hsOptions: HSState): string[] {
switch (hsOptions.option) {
case HSOptions.Unset:
return [];
case HSOptions.None:
return [];
case HSOptions.TrustedHSOnly:
return [hsOptions.hs];
case HSOptions.Any:
return [
'https://' + link.identifier.split(':')[1],
...link.arguments.vias,
];
}
}
const LinkPreview: React.FC<IProps> = ({ link }: IProps) => { const LinkPreview: React.FC<IProps> = ({ link }: IProps) => {
let content: JSX.Element; let content: JSX.Element;
const [showHSOptions, setShowHSOPtions] = useState(false); const [showHSOptions, setShowHSOPtions] = useState(false);
const [hsOptions] = useContext(HSContext);
const [tempHSState] = useContext(TempHSContext);
if ( const hses = useHSs(link);
hsOptions.option === HSOptions.Unset && console.log(hses);
tempHSState.option === HSOptions.Unset
) { if (!hses) {
content = ( content = (
<> <>
<DefaultPreview link={link} /> <DefaultPreview link={link} />
@ -164,11 +142,7 @@ const LinkPreview: React.FC<IProps> = ({ link }: IProps) => {
); );
} }
} else { } else {
const clients = content = <Preview link={link} client={hses[0]} />;
tempHSState.option !== HSOptions.Unset
? selectedClient(link, tempHSState)
: selectedClient(link, hsOptions);
content = <Preview link={link} client={clients[0]} />;
} }
const [{ clientId }] = useContext(ClientContext); const [{ clientId }] = useContext(ClientContext);
@ -182,8 +156,20 @@ const LinkPreview: React.FC<IProps> = ({ link }: IProps) => {
const client = displayClientId ? clientMap[displayClientId] : null; const client = displayClientId ? clientMap[displayClientId] : null;
const sharer = link.arguments.sharer ? (
<WrappedInviterPreview
link={{
kind: LinkKind.UserId,
identifier: link.arguments.sharer,
arguments: { vias: [] },
originalLink: '',
}}
/>
) : null;
return ( return (
<InviteTile client={client} link={link}> <InviteTile client={client} link={link}>
{sharer}
{content} {content}
</InviteTile> </InviteTile>
); );

View file

@ -14,10 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, { useState, useEffect } from 'react';
import { User } from 'matrix-cypher'; import { client, User, getUserDetails } from 'matrix-cypher';
import icon from '../imgs/chat-icon.svg';
import { UserAvatar } from './Avatar'; import Avatar, { UserAvatar } from './Avatar';
import useHSs from '../utils/getHS';
import { UserId } from '../parser/types';
import './UserPreview.scss'; import './UserPreview.scss';
@ -37,14 +40,49 @@ const UserPreview: React.FC<IProps> = ({ user, userId }: IProps) => (
export default UserPreview; export default UserPreview;
export const InviterPreview: React.FC<IProps> = ({ user, userId }: IProps) => ( interface InviterPreviewProps {
<div className="miniUserPreview"> user?: User;
<div> userId: string;
<h1> }
Invited by <b>{user.displayname}</b>
</h1> export const InviterPreview: React.FC<InviterPreviewProps> = ({
<p>{userId}</p> user,
</div> userId,
}: InviterPreviewProps) => {
const avatar = user ? (
<UserAvatar user={user} userId={userId} /> <UserAvatar user={user} userId={userId} />
</div> ) : (
); <Avatar label={`Placeholder icon for ${userId}`} avatarUrl={icon} />
);
return (
<div className="miniUserPreview">
<div>
<h1>
Invited by <b>{user ? user.displayname : userId}</b>
</h1>
{user ? <p>{userId}</p> : null}
</div>
{avatar}
</div>
);
};
interface WrappedInviterProps {
link: UserId;
}
export const WrappedInviterPreview: React.FC<WrappedInviterProps> = ({
link,
}: WrappedInviterProps) => {
const [user, setUser] = useState<User | undefined>(undefined);
const hss = useHSs(link);
useEffect(() => {
if (hss) {
client(hss[0])
.then((c) => getUserDetails(c, link.identifier))
.then(setUser)
.catch((x) => console.log("couldn't fetch user preview", x));
}
}, [hss, link]);
return <InviterPreview user={user} userId={link.identifier} />;
};

View file

@ -29,17 +29,12 @@ export enum HSOptions {
TrustedHSOnly = 'TRUSTED_CLIENT_ONLY', TrustedHSOnly = 'TRUSTED_CLIENT_ONLY',
// Matrix.to may contact any homeserver it requires // Matrix.to may contact any homeserver it requires
Any = 'ANY', Any = 'ANY',
// Matrix.to may not contact any homeservers
None = 'NONE',
} }
const STATE_SCHEMA = union([ const STATE_SCHEMA = union([
object({ object({
option: literal(HSOptions.Unset), option: literal(HSOptions.Unset),
}), }),
object({
option: literal(HSOptions.None),
}),
object({ object({
option: literal(HSOptions.Any), option: literal(HSOptions.Any),
}), }),
@ -55,7 +50,6 @@ export type State = TypeOf<typeof STATE_SCHEMA>;
export enum ActionType { export enum ActionType {
SetHS = 'SET_HS', SetHS = 'SET_HS',
SetAny = 'SET_ANY', SetAny = 'SET_ANY',
SetNone = 'SET_NONE',
} }
export interface SetHS { export interface SetHS {
@ -67,11 +61,7 @@ export interface SetAny {
action: ActionType.SetAny; action: ActionType.SetAny;
} }
export interface SetNone { export type Action = SetHS | SetAny;
action: ActionType.SetNone;
}
export type Action = SetHS | SetAny | SetNone;
export const INITIAL_STATE: State = { export const INITIAL_STATE: State = {
option: HSOptions.Unset, option: HSOptions.Unset,
@ -81,10 +71,6 @@ export const unpersistedReducer = (state: State, action: Action): State => {
console.log('reducing'); console.log('reducing');
console.log(action); console.log(action);
switch (action.action) { switch (action.action) {
case ActionType.SetNone:
return {
option: HSOptions.None,
};
case ActionType.SetAny: case ActionType.SetAny:
return { return {
option: HSOptions.Any, option: HSOptions.Any,

52
src/utils/getHS.ts Normal file
View file

@ -0,0 +1,52 @@
/*
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 { useContext } from 'react';
import HSContext, {
TempHSContext,
State,
HSOptions,
} from '../contexts/HSContext';
import { SafeLink } from '../parser/types';
function selectedClient(link: SafeLink, hsOptions: State): string[] {
switch (hsOptions.option) {
case HSOptions.Unset:
return [];
case HSOptions.TrustedHSOnly:
return [hsOptions.hs];
case HSOptions.Any:
return [
...link.identifier
.split('/')
.map((i) => 'https://' + i.split(':')[1]),
...link.arguments.vias,
];
}
}
export default function useHSs(link: SafeLink): string[] {
const [HSState] = useContext(HSContext);
const [TempHSState] = useContext(TempHSContext);
if (HSState.option !== HSOptions.Unset) {
return selectedClient(link, HSState);
} else if (TempHSState.option !== HSOptions.Unset) {
return selectedClient(link, TempHSState);
} else {
return [];
}
}