diff --git a/changelog.d/870.bugfix b/changelog.d/870.bugfix new file mode 100644 index 00000000..57f22413 --- /dev/null +++ b/changelog.d/870.bugfix @@ -0,0 +1 @@ +Fix widgets failing with "Request timed out". diff --git a/web/App.tsx b/web/App.tsx index b512c6d0..bac2078e 100644 --- a/web/App.tsx +++ b/web/App.tsx @@ -7,6 +7,7 @@ import { LoadingSpinner } from './components/elements/LoadingSpinner'; import AdminSettings from './components/AdminSettings'; import RoomConfigView from './components/RoomConfigView'; import { Alert } from '@vector-im/compound-web'; +import { BridgeContext } from './context'; interface IMinimalState { error: string|null, @@ -115,43 +116,39 @@ export default class App extends Component { } render() { + const style = { + padding: 'embedType' in this.state && this.state.embedType === EmbedType.IntegrationManager ? "0" : "16px", + }; + if (this.state.error) { + return
{this.state.error}
; + } else if (this.state.busy) { + return
; + } else if ("kind" in this.state === false) { + console.warn("invalid state", this.state); + return
Widget got into an invalid state.
; + } + // Return the App component. let content; - if (this.state.error) { - content = {this.state.error}; - } else if (this.state.busy) { - content = ; - } - if ("kind" in this.state) { - if (this.state.roomState && this.state.kind === "admin") { - content = ; - } 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 = ; - } + if (this.state.kind === "admin") { + content = ; + }else if (this.state.kind === "roomConfig") { + content = ; + } else { + return
Unknown widget kind.
; } - if (!content) { - console.warn("invalid state", this.state); - content = Invalid state; - } - - const embedType = 'embedType' in this.state ? this.state.embedType : EmbedType.Default; - return ( -
- {content} +
+ + {content} +
); } diff --git a/web/BridgeAPI.ts b/web/BridgeAPI.ts index 5e9d80ee..e6b5791e 100644 --- a/web/BridgeAPI.ts +++ b/web/BridgeAPI.ts @@ -171,7 +171,6 @@ export enum EmbedType { } export type BridgeConfig = FunctionComponent<{ - api: BridgeAPI, roomId: string, showHeader: boolean, }>; diff --git a/web/components/RoomConfigView.tsx b/web/components/RoomConfigView.tsx index 4cdc6d8d..4a8c23b6 100644 --- a/web/components/RoomConfigView.tsx +++ b/web/components/RoomConfigView.tsx @@ -1,6 +1,5 @@ -import { WidgetApi } from "matrix-widget-api"; import { useState } from "preact/hooks" -import { BridgeAPI, BridgeConfig, EmbedType } from "../BridgeAPI"; +import { BridgeConfig, EmbedType } from "../BridgeAPI"; import style from "./RoomConfigView.module.scss"; import { ConnectionCard } from "./ConnectionCard"; import { FeedsConfig } from "./roomConfig/FeedsConfig"; @@ -17,8 +16,6 @@ import WebhookIcon from "../icons/webhook.png"; interface IProps { - widgetApi: WidgetApi, - bridgeApi: BridgeAPI, supportedServices: {[service: string]: boolean}, serviceScope?: string, embedType: EmbedType, @@ -86,7 +83,6 @@ export default function RoomConfigView(props: IProps) { const ConfigComponent = connections[activeConnectionType].component; content = ; } else { @@ -109,6 +105,7 @@ export default function RoomConfigView(props: IProps) { } return
+ {!serviceScope && activeConnectionType &&
setActiveConnectionType(null)}> diff --git a/web/components/roomConfig/Auth.tsx b/web/components/roomConfig/Auth.tsx index ab562d9a..1cc27f44 100644 --- a/web/components/roomConfig/Auth.tsx +++ b/web/components/roomConfig/Auth.tsx @@ -1,24 +1,23 @@ -import { useCallback, useEffect, useState } from "preact/hooks"; +import { useCallback, useContext, useEffect, useState } from "preact/hooks"; import { GetAuthResponse } from "../../../src/Widgets/BridgeWidgetInterface"; -import { BridgeAPI } from "../../BridgeAPI"; import { Button } from "../elements"; +import { BridgeContext } from "../../context"; const PollAuthEveryMs = 3000; export const ServiceAuth = ({ - api, service, loginLabel = "Log in", authState, onAuthSucceeded, }: { - api: BridgeAPI, service: string, authState: GetAuthResponse, onAuthSucceeded: () => void, loginLabel?: string, }) => { + const api = useContext(BridgeContext).bridgeApi; const [pollStateId, setPollStateId] = useState(); const pollAuth = useCallback(async (pollId) => { diff --git a/web/components/roomConfig/FeedsConfig.tsx b/web/components/roomConfig/FeedsConfig.tsx index 0e8bf408..af5f23b4 100644 --- a/web/components/roomConfig/FeedsConfig.tsx +++ b/web/components/roomConfig/FeedsConfig.tsx @@ -92,12 +92,11 @@ const roomConfigText: IRoomConfigText = { const RoomConfigListItemFunc = (c: FeedResponseItem) => c.config.label || c.config.url; -export const FeedsConfig: BridgeConfig = ({ api, roomId, showHeader }) => { +export const FeedsConfig: BridgeConfig = ({ roomId, showHeader }) => { return headerImg={FeedsIcon} showHeader={showHeader} - api={api} roomId={roomId} type="feeds" connectionEventType="uk.half-shot.matrix-hookshot.feed" diff --git a/web/components/roomConfig/GenericWebhookConfig.tsx b/web/components/roomConfig/GenericWebhookConfig.tsx index 9a78129a..6a5ae3aa 100644 --- a/web/components/roomConfig/GenericWebhookConfig.tsx +++ b/web/components/roomConfig/GenericWebhookConfig.tsx @@ -111,12 +111,11 @@ const RoomConfigText = { const RoomConfigListItemFunc = (c: GenericHookResponseItem) => c.config.name; -export const GenericWebhookConfig: BridgeConfig = ({ api, roomId, showHeader }) => { +export const GenericWebhookConfig: BridgeConfig = ({ roomId, showHeader }) => { return headerImg={WebhookIcon} darkHeaderImg={true} showHeader={showHeader} - api={api} roomId={roomId} type="generic" connectionEventType="uk.half-shot.matrix-hookshot.generic.hook" diff --git a/web/components/roomConfig/GithubRepoConfig.tsx b/web/components/roomConfig/GithubRepoConfig.tsx index ecb52c29..434848b0 100644 --- a/web/components/roomConfig/GithubRepoConfig.tsx +++ b/web/components/roomConfig/GithubRepoConfig.tsx @@ -5,11 +5,12 @@ import { EventHookCheckbox } from '../elements/EventHookCheckbox'; import { FunctionComponent, createRef } from "preact"; import { GitHubRepoConnectionState, GitHubRepoResponseItem, GitHubRepoConnectionRepoTarget, GitHubRepoConnectionOrgTarget } from "../../../src/Connections/GithubRepo"; import { InputField, ButtonSet, Button } from "../elements"; -import { useState, useCallback, useMemo, useEffect } from "preact/hooks"; +import { useState, useCallback, useMemo, useEffect, useContext } from "preact/hooks"; import { DropItem } from "../elements/DropdownSearch"; import ConnectionSearch from "../elements/ConnectionSearch"; import { ServiceAuth } from "./Auth"; import { GetAuthResponse } from "../../../src/Widgets/BridgeWidgetInterface"; +import { BridgeContext } from "../../context"; const EventType = "uk.half-shot.matrix-hookshot.github.repository"; @@ -18,11 +19,12 @@ function getRepoFullName(state: GitHubRepoConnectionState) { } const ConnectionConfiguration: FunctionComponent> = ({ - showAuthPrompt, loginLabel, serviceConfig, api, existingConnection, onSave, onRemove, isUpdating + showAuthPrompt, loginLabel, serviceConfig, existingConnection, onSave, onRemove, isUpdating }) => { // Assume true if we have no auth prompt. const [authedResponse, setAuthResponse] = useState(null); const [enabledHooks, setEnabledHooks] = useState(existingConnection?.config.enableHooks || []); + const api = useContext(BridgeContext).bridgeApi; const checkAuth = useCallback(() => { api.getAuth("github").then((res) => { @@ -106,7 +108,7 @@ const ConnectionConfiguration: FunctionComponent - {authedResponse && } + {authedResponse && } {!existingConnection && consideredAuthenticated && getRepoFullName(c.config); -export const GithubRepoConfig: BridgeConfig = ({ api, roomId, showHeader }) => { +export const GithubRepoConfig: BridgeConfig = ({ roomId, showHeader }) => { return headerImg={GitHubIcon} darkHeaderImg={true} showHeader={showHeader} - api={api} roomId={roomId} type="github" showAuthPrompt={true} diff --git a/web/components/roomConfig/GitlabRepoConfig.tsx b/web/components/roomConfig/GitlabRepoConfig.tsx index 02be6e3f..9d2ed59f 100644 --- a/web/components/roomConfig/GitlabRepoConfig.tsx +++ b/web/components/roomConfig/GitlabRepoConfig.tsx @@ -5,13 +5,15 @@ import { EventHookCheckbox } from '../elements/EventHookCheckbox'; import { GitLabRepoConnectionState, GitLabRepoResponseItem, GitLabRepoConnectionProjectTarget, GitLabRepoConnectionInstanceTarget } from "../../../src/Connections/GitlabRepo"; import { InputField, ButtonSet, Button } from "../elements"; import { FunctionComponent, createRef } from "preact"; -import { useState, useCallback, useMemo } from "preact/hooks"; +import { useState, useCallback, useMemo, useContext } from "preact/hooks"; import { DropItem } from "../elements/DropdownSearch"; import { ConnectionSearch } from "../elements/ConnectionSearch"; +import { BridgeContext } from "../../context"; const EventType = "uk.half-shot.matrix-hookshot.gitlab.repository"; -const ConnectionConfiguration: FunctionComponent> = ({api, existingConnection, onSave, onRemove, isUpdating }) => { +const ConnectionConfiguration: FunctionComponent> = ({existingConnection, onSave, onRemove, isUpdating }) => { const [enabledHooks, setEnabledHooks] = useState(existingConnection?.config.enableHooks || []); + const api = useContext(BridgeContext).bridgeApi; const toggleEnabledHook = useCallback((evt: any) => { const key = (evt.target as HTMLElement).getAttribute('x-event-name'); @@ -123,11 +125,10 @@ const RoomConfigText = { const RoomConfigListItemFunc = (c: GitLabRepoResponseItem) => c.config.path; -export const GitlabRepoConfig: BridgeConfig = ({ api, roomId, showHeader }) => { +export const GitlabRepoConfig: BridgeConfig = ({ roomId, showHeader }) => { return headerImg={GitLabIcon} showHeader={showHeader} - api={api} roomId={roomId} type="gitlab" text={RoomConfigText} diff --git a/web/components/roomConfig/JiraProjectConfig.tsx b/web/components/roomConfig/JiraProjectConfig.tsx index 2261e3df..12796dac 100644 --- a/web/components/roomConfig/JiraProjectConfig.tsx +++ b/web/components/roomConfig/JiraProjectConfig.tsx @@ -1,5 +1,5 @@ import { FunctionComponent, createRef } from "preact"; -import { useState, useCallback, useMemo } from "preact/hooks"; +import { useState, useCallback, useMemo, useContext } from "preact/hooks"; import { BridgeConfig } from "../../BridgeAPI"; import { ConnectionConfigurationProps, RoomConfig } from "./RoomConfig"; import { JiraProjectConnectionState, JiraProjectResponseItem, JiraProjectConnectionProjectTarget, JiraProjectConnectionInstanceTarget } from "../../../src/Connections/JiraProject"; @@ -8,11 +8,13 @@ import { EventHookCheckbox } from '../elements/EventHookCheckbox'; import JiraIcon from "../../icons/jira.png"; import ConnectionSearch from "../elements/ConnectionSearch"; import { DropItem } from "../elements/DropdownSearch"; +import { BridgeContext } from "../../context"; const EventType = "uk.half-shot.matrix-hookshot.jira.project"; -const ConnectionConfiguration: FunctionComponent> = ({api, existingConnection, onSave, onRemove, isUpdating }) => { +const ConnectionConfiguration: FunctionComponent> = ({existingConnection, onSave, onRemove, isUpdating }) => { const [allowedEvents, setAllowedEvents] = useState(existingConnection?.config.events || ['issue_created']); + const api = useContext(BridgeContext).bridgeApi; const toggleEvent = useCallback((evt: Event) => { const key = (evt.target as HTMLElement).getAttribute('x-event-name'); @@ -108,11 +110,10 @@ const RoomConfigText = { const RoomConfigListItemFunc = (c: JiraProjectResponseItem) => c.config.url; -export const JiraProjectConfig: BridgeConfig = ({ api, roomId, showHeader }) => { +export const JiraProjectConfig: BridgeConfig = ({ roomId, showHeader }) => { return headerImg={JiraIcon} showHeader={showHeader} - api={api} roomId={roomId} type="jira" text={RoomConfigText} diff --git a/web/components/roomConfig/RoomConfig.tsx b/web/components/roomConfig/RoomConfig.tsx index 9522931a..a42e8d9c 100644 --- a/web/components/roomConfig/RoomConfig.tsx +++ b/web/components/roomConfig/RoomConfig.tsx @@ -1,6 +1,6 @@ import { FunctionComponent } from "preact"; -import { useCallback, useEffect, useReducer, useState } from "preact/hooks" -import { BridgeAPI, BridgeAPIError } from "../../BridgeAPI"; +import { useCallback, useContext, useEffect, useReducer, useState } from "preact/hooks" +import { BridgeAPIError } from "../../BridgeAPI"; import { ListItem, Card } from "../elements"; import style from "./RoomConfig.module.scss"; import { GetConnectionsResponseItem } from "../../../src/provisioning/api"; @@ -9,6 +9,7 @@ import { LoadingSpinner } from '../elements/LoadingSpinner'; import { ErrCode } from "../../../src/api"; import { retry } from "../../../src/PromiseUtil"; import { Alert } from "@vector-im/compound-web"; +import { BridgeContext } from "../../context"; export interface ConnectionConfigurationProps { serviceConfig: SConfig; @@ -19,7 +20,6 @@ export interface ConnectionConfigurationProps void, - api: BridgeAPI; } export interface IRoomConfigText { @@ -31,7 +31,6 @@ export interface IRoomConfigText { } interface IRoomConfigProps { - api: BridgeAPI; roomId: string; type: string; showAuthPrompt?: boolean; @@ -48,7 +47,6 @@ const MAX_CONNECTION_FETCH_ATTEMPTS = 10; export const RoomConfig = function(props: IRoomConfigProps) { const { - api, roomId, type, showAuthPrompt = false, @@ -59,6 +57,7 @@ export const RoomConfig = function(null); const [ connections, setConnections ] = useState(null); @@ -153,7 +152,6 @@ export const RoomConfig = function{text.createNew} {serviceConfig && { canEditRoom ? text.listCanEdit : text.listCantEdit } { serviceConfig && connections?.map(c => { diff --git a/web/context.ts b/web/context.ts new file mode 100644 index 00000000..8f66d701 --- /dev/null +++ b/web/context.ts @@ -0,0 +1,15 @@ +import { createContext } from "preact"; +import type { BridgeAPI } from "./BridgeAPI"; + +interface IBridgeContext { + bridgeApi: BridgeAPI; +} + +const fakeBridgeContext = { + get bridgeApi(): BridgeAPI { + throw Error('No context provided'); + } +} + +export const BridgeContext = createContext(fakeBridgeContext); +