hookshot/web/App.tsx

153 lines
5.6 KiB
TypeScript
Raw Normal View History

/* eslint-disable no-console */
import { Component } from 'preact';
import WA, { MatrixCapabilities } from 'matrix-widget-api';
import { BridgeAPI, BridgeAPIError, EmbedType, embedTypeParameter } from './BridgeAPI';
2020-12-12 20:29:33 +00:00
import { BridgeRoomState } from '../src/Widgets/BridgeWidgetInterface';
import { LoadingSpinner } from './components/elements/LoadingSpinner';
import { Card, ErrorPane } from './components/elements';
2020-12-12 20:29:33 +00:00
import AdminSettings from './components/AdminSettings';
import RoomConfigView from './components/RoomConfigView';
interface IMinimalState {
2020-12-12 20:29:33 +00:00
error: string|null,
busy: boolean,
}
interface ICompleteState extends IMinimalState {
roomId: string,
userId: string,
roomState: BridgeRoomState,
supportedServices: {
[sectionName: string]: boolean;
},
serviceScope?: string,
embedType: EmbedType,
kind: "invite"|"admin"|"roomConfig",
2020-12-12 20:29:33 +00:00
}
type IState = IMinimalState|ICompleteState;
2020-12-12 20:29:33 +00:00
function parseFragment() {
const fragmentString = (window.location.hash || "?");
return new URLSearchParams(fragmentString.substring(Math.max(fragmentString.indexOf('?'), 0)));
}
2020-12-12 13:19:55 +00:00
2020-12-12 20:29:33 +00:00
function assertParam(fragment, name) {
const val = fragment.get(name);
if (!val) throw new Error(`${name} is not present in URL - cannot load widget`);
return val;
2020-12-12 13:19:55 +00:00
}
2020-12-12 20:29:33 +00:00
export default class App extends Component<void, IState> {
private widgetApi: WA.WidgetApi;
private bridgeApi: BridgeAPI;
constructor() {
super();
this.state = {
error: null,
busy: true,
};
}
async componentDidMount() {
try {
// Start widgeting
const qs = parseFragment();
const widgetId = assertParam(qs, 'widgetId');
const roomId = assertParam(qs, 'roomId');
const widgetKind = qs.get('kind') as "invite"|"admin"|"roomConfig";
const serviceScope = qs.get('serviceScope');
const embedType = qs.get(embedTypeParameter);
// Fetch via config.
2020-12-12 20:29:33 +00:00
this.widgetApi = new WA.WidgetApi(widgetId);
this.widgetApi.requestCapability(MatrixCapabilities.RequiresClient);
2020-12-12 20:29:33 +00:00
this.widgetApi.on("ready", () => {
console.log("Widget ready:", this);
});
this.widgetApi.on(`action:${WA.WidgetApiToWidgetAction.NotifyCapabilities}`, (ev) => {
console.log(`${WA.WidgetApiToWidgetAction.NotifyCapabilities}`, ev);
})
this.widgetApi.on(`action:${WA.WidgetApiToWidgetAction.SendEvent}`, (ev) => {
console.log(`${WA.WidgetApiToWidgetAction.SendEvent}`, ev);
})
// Start the widget as soon as possible too, otherwise the client might time us out.
2020-12-12 20:29:33 +00:00
this.widgetApi.start();
// Assuming the hosted widget is on the same API path.
const widgetApiUrl = new URL(`${window.location.origin}${window.location.pathname.replace("/widgetapi/v1/static", "")}`);
this.bridgeApi = await BridgeAPI.getBridgeAPI(widgetApiUrl.toString(), this.widgetApi);
const { userId } = await this.bridgeApi.verify();
const roomState = widgetKind === "admin" ? await this.bridgeApi.state() : undefined;
const supportedServices = await this.bridgeApi.getEnabledConfigSections();
// Calling setState is ok because we've awaited a network request.
// eslint-disable-next-line react/no-did-mount-set-state
2020-12-12 20:29:33 +00:00
this.setState({
userId,
2020-12-12 20:29:33 +00:00
roomState,
roomId,
supportedServices,
serviceScope: serviceScope || undefined,
embedType: embedType === EmbedType.IntegrationManager ? EmbedType.IntegrationManager : EmbedType.Default,
kind: widgetKind,
2020-12-12 20:29:33 +00:00
busy: false,
});
} catch (ex) {
console.error(`Failed to set up widget:`, ex);
let error: string = ex.message;
if (ex instanceof BridgeAPIError) {
if (ex.errcode === "M_AS_BAD_OPENID") {
error = "Could not contact your homeserver. Your instance may be misconfigured.";
}
}
// Calling setState is ok because we've awaited a network request.
// eslint-disable-next-line react/no-did-mount-set-state
2020-12-12 20:29:33 +00:00
this.setState({
error,
2020-12-12 20:29:33 +00:00
busy: false,
});
}
}
render() {
// Return the App component.
let content;
if (this.state.error) {
content = <ErrorPane>{this.state.error}</ErrorPane>;
} else if (this.state.busy) {
content = <Card>
<LoadingSpinner />
</Card>;
}
if ("kind" in this.state) {
if (this.state.roomState && this.state.kind === "admin") {
2022-04-08 16:20:40 +01:00
content = <AdminSettings bridgeApi={this.bridgeApi} roomState={this.state.roomState} />;
} else if (this.state.kind === "invite") {
// Fall through for now, we don't support invite widgets *just* yet.
} else if (this.state.kind === "roomConfig") {
content = <RoomConfigView
roomId={this.state.roomId}
supportedServices={this.state.supportedServices}
serviceScope={this.state.serviceScope}
embedType={this.state.embedType}
bridgeApi={this.bridgeApi}
widgetApi={this.widgetApi}
2022-04-08 16:20:40 +01:00
/>;
}
}
if (!content) {
console.warn("invalid state", this.state);
2020-12-12 20:29:33 +00:00
content = <b>Invalid state</b>;
}
2020-12-12 20:29:33 +00:00
return (
<div style={{
padding: this.state.embedType === "integration-manager" ? "0" : "16px",
}}>
2020-12-12 20:29:33 +00:00
{content}
</div>
);
}
}