Move matrix-cypher into matrix.to

This commit is contained in:
Jorik Schellekens 2020-09-16 14:57:16 +01:00
parent 91cbb73ac8
commit cf23f0a85e
20 changed files with 531 additions and 9 deletions

View file

@ -6,7 +6,6 @@
"@quentin-sommer/react-useragent": "^3.1.0",
"classnames": "^2.2.6",
"formik": "^2.1.4",
"matrix-cypher": "^0.1.12",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",

View file

@ -16,7 +16,7 @@ limitations under the License.
import React, { useEffect, useState } from 'react';
import classNames from 'classnames';
import { Room, User } from 'matrix-cypher';
import { Room, User } from '../matrix-cypher';
import { getMediaQueryFromMCX } from '../utils/cypher-wrapper';
import logo from '../imgs/chat-icon.svg';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React from 'react';
import { Room, Event } from 'matrix-cypher';
import { Room, Event } from '../matrix-cypher';
import RoomPreview from './RoomPreview';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React, { useState, useEffect, useContext } from 'react';
import { getEvent, client } from 'matrix-cypher';
import { getEvent, client } from '../matrix-cypher';
import { RoomPreviewWithTopic } from './RoomPreview';
import InviteTile from './InviteTile';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React from 'react';
import { Room } from 'matrix-cypher';
import { Room } from '../matrix-cypher';
import { RoomAvatar } from './Avatar';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React, { useState, useEffect } from 'react';
import { client, User, getUserDetails } from 'matrix-cypher';
import { client, User, getUserDetails } from '../matrix-cypher';
import classNames from 'classnames';
import icon from '../imgs/chat-icon.svg';

View file

@ -19,8 +19,6 @@ import { string, object, union, literal, TypeOf } from 'zod';
import { persistReducer } from '../utils/localStorage';
//import { prefixFetch, Client, discoverServer } from 'matrix-cypher';
export enum HSOptions {
// The homeserver contact policy hasn't
// been set yet.

View file

@ -0,0 +1,19 @@
/*
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.
*/
export * from './matrix-cypher';
export * from './utils';
export * from './schemas';

View file

@ -0,0 +1,193 @@
/*
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.
*/
/* eslint-disable import/first */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import any from 'promise.any';
any.shim()
import VersionSchema from './schemas/VersionSchema';
import WellKnownSchema from './schemas/WellKnownSchema';
import UserSchema, { User } from './schemas/UserSchema';
import RoomAliasSchema, {
RoomAlias,
} from './schemas/RoomAliasSchema';
import PublicRoomsSchema, {
PublicRooms,
Room,
} from './schemas/PublicRoomsSchema';
import EventSchema, {
Event,
} from './schemas/EventSchema';
import { ensure } from './utils/promises';
import { prefixFetch, parseJSON } from './utils/fetch';
/*
* A client is a resolved homeserver name wrapped in a lambda'd fetch
*/
export type Client = (path: string) => Promise<Response>;
/*
* Confirms that the target homeserver is properly configured and operational
*/
export const validateHS = (host: string) =>
prefixFetch(host)('/_matrix/client/versions')
.then(parseJSON)
.then(VersionSchema.parse)
.then(() => host);
/*
* Discovers the correct domain name for the host according to the spec's
* discovery rules
*/
export const discoverServer = (host: string) =>
prefixFetch(host)('/.well-known/matrix/client')
.then(resp => resp.ok
? resp.json()
.then(WellKnownSchema.parse)
.then(content => {
if (content === undefined) return host;
else if (
'm.homeserver' in content && content['m.homeserver']
) {
return content['m.homeserver'].base_url
} else {
return host
}
})
: ensure(
resp.status === 404,
() => host,
),
)
.then(validateHS)
/*
* Takes a hs domain and resolves it to it's current domain and returns a
* client
*/
export async function client(host: string): Promise<Client> {
return prefixFetch(await discoverServer(host))
}
/*
* Gets the details for a user
*/
export function getUserDetails(
client: Client,
userId: string,
): Promise<User> {
return client(`/_matrix/client/r0/profile/${userId}`)
.then(parseJSON)
.then(UserSchema.parse)
}
/*
* Gets the roomId of a room by resolving it's alias
*/
export function getRoomIdFromAlias(
client: Client,
roomAlias: string,
): Promise<RoomAlias> {
const encodedRoomAlias = encodeURIComponent(roomAlias);
return client(`/_matrix/client/r0/directory/room/${encodedRoomAlias}`)
.then(parseJSON)
.then(RoomAliasSchema.parse);
}
/*
* Gets the details of a room if that room is public
*/
export function getRoomDetails(clients: Client[], roomId: string): Promise<Room> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return Promise.any(clients.map(client => searchPublicRooms(client, roomId)));
}
/*
* Gets a list of all public rooms on a hs
*/
export function getPublicRooms(client: Client): Promise<PublicRooms> {
return getPublicRoomsUnsafe(client)
.then(PublicRoomsSchema.parse)
}
/*
* Similar to getPubliRooms however id doesn't confirm the data returned from
* the hs is correct
*
* This is used because the room list can be huge and validating it all takes
* a long time
*/
export function getPublicRoomsUnsafe(client: Client): Promise<PublicRooms> {
// TODO: Do not assume server will return all results in one go
return client('/_matrix/client/r0/publicRooms')
.then(parseJSON)
}
/*
* Searches the public rooms of a homeserver for the metadata of a particular
*/
export function searchPublicRooms(
client: Client,
roomId: string,
): Promise<Room> {
// we use the unsage version here because the safe one is sloooow
return getPublicRoomsUnsafe(client)
.then(rooms => {
const [match] = rooms.chunk.filter(
chunk => chunk.room_id === roomId,
);
return match !== undefined
? Promise.resolve(match)
: Promise.reject(new Error(
`This server knowns no public room with id ${roomId}`,
));
});
}
/*
* Gets the details of an event from the homeserver
*/
export async function getEvent(
client: Client,
roomIdOrAlias: string,
eventId: string,
): Promise<Event> {
return client(`/_matrix/client/r0/rooms/${roomIdOrAlias}/event/${eventId}`)
.then(parseJSON)
.then(EventSchema.parse);
}
/*
* Gets an mxc resource
*/
export function convertMXCtoMediaQuery(
clientURL: string,
mxc: string,
): string {
// mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf
const matches = mxc.match(/mxc:\/\/(.+)\/(.+)/)
if (!matches) {
throw new Error(`mxc invalid: ${JSON.stringify({mxc})}`);
}
return `${clientURL}/_matrix/media/r0/download/${matches[1]}/${matches[2]}`;
}

View file

@ -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.
*/
import { object, string, TypeOf } from 'zod';
const EventSchema = object({
content: object({}).nonstrict(),
type: string(),
event_id: string(),
sender: string(),
origin_server_ts: string(),
unsigned: object({}).nonstrict().optional(),
room_id: string(),
});
export type Event = TypeOf<typeof EventSchema>;
export default EventSchema;

View file

@ -0,0 +1,43 @@
/*
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 { object, array, string, boolean, number, TypeOf } from 'zod';
export const RoomSchema = object({
aliases: array(string()).optional(),
canonical_alias: string().optional(),
name: string().optional(),
num_joined_members: number(),
room_id: string(),
topic: string().optional(),
world_readable: boolean(),
guest_can_join: boolean(),
avatar_url: string().optional(),
});
const PublicRoomsSchema = object({
chunk: array(RoomSchema),
next_batch: string().optional(),
prev_batch: string().optional(),
total_room_count_estimate: number().optional(),
});
export type Room = TypeOf<typeof RoomSchema>;
export type PublicRooms = TypeOf<typeof PublicRoomsSchema>;
export default PublicRoomsSchema;

View file

@ -0,0 +1,26 @@
/*
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 { object, array, string, TypeOf } from 'zod';
const RoomAliasSchema = object({
room_id: string(),
servers: array(string()),
});
export type RoomAlias = TypeOf<typeof RoomAliasSchema>;
export default RoomAliasSchema;

View file

@ -0,0 +1,26 @@
/*
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 { object, string, TypeOf } from 'zod';
const UserSchema = object({
avatar_url: string().optional(),
displayname: string().optional(),
})
export type User = TypeOf<typeof UserSchema>;
export default UserSchema;

View file

@ -0,0 +1,21 @@
/*
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 { object, string, array } from 'zod';
export default object({
versions: array(string()),
}).nonstrict()

View file

@ -0,0 +1,29 @@
/*
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 { object, string, TypeOf } from 'zod';
const WellKnownSchema = object({
'm.homeserver': object({
'base_url': string().url(),
}),
'm.identity_server': object({
'base_url': string().url(),
}),
});
export type WellKnown = TypeOf<typeof WellKnownSchema>;
export default WellKnownSchema;

View file

@ -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.
*/
export * from './EventSchema';
export * from './PublicRoomsSchema';
export * from './RoomAliasSchema';
export * from './UserSchema';
export * from './VersionSchema';
export * from './WellKnownSchema';
export * from './index';

View file

@ -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 fetch from 'cross-fetch';
import { ensure } from './promises';
/*
* Wraps a fetch with a domain for easy reuse.
*/
export function prefixFetch(host: string) {
return (path: string) => fetch(
new URL(path, host).toString(),
);
}
export function parseJSON(resp: Response) {
return ensure(
resp.ok,
() => resp.json(),
`Error from Homeserver. Error code: ${resp.status}`,
);
}

View file

@ -0,0 +1,18 @@
/*
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.
*/
export * from './fetch';
export * from './promises';

View file

@ -0,0 +1,60 @@
/*
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.
*/
/*
* Conditional promises
*/
/*
* If the condition is false reject with rejectReason
* If it's true resolve with the result = resultThunk()
*/
export function ensure<T>(condition: boolean, resultThunk: () => T | PromiseLike<T>, rejectReason?: string) {
return condition
? Promise.resolve(resultThunk())
: Promise.reject(new Error(rejectReason));
}
/*
* Loggin utilities
*/
/*
* Logs a then using "success: {label: successArg}"
*/
export function logThen<T>(label: string): (v: T) => T | PromiseLike<T> {
return (v: T) => {
console.log('success:', {[`${label}`]: v}); return v
}
}
/*
* Logs a catch using "fail: {label: failArg}"
*/
export function logCatch<T>(label: string): (v: T) => T | PromiseLike<T> {
return (v: T) => {
console.log('fail:', {[`${label}`]: v});
return Promise.reject(v)
}
}
/*
* inserts loggers for both callbacks of a then
*/
export function logThens<T1, T2 = T1>(label: string) {
return [logThen<T1>(label), logCatch<T2>(label)]
}

View file

@ -28,7 +28,7 @@ import {
searchPublicRooms,
getUserDetails,
convertMXCtoMediaQuery,
} from 'matrix-cypher';
} from '../matrix-cypher';
import { LinkKind, Permalink } from '../parser/types';
/* This is a collection of methods for providing fallback metadata