mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-23 12:51:13 +00:00
Add external interaction page
This commit is contained in:
parent
dcc17375b4
commit
6f6acbb0a5
7 changed files with 173 additions and 15 deletions
|
@ -48,7 +48,7 @@ export default async (req: Request, res: Response) => {
|
|||
let errorPageData: ErrorPageData | undefined = undefined;
|
||||
let try_site = await client.getSite(getSiteForm);
|
||||
|
||||
if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
|
||||
if (try_site.state === "failed" && try_site.msg === "not_logged_in") {
|
||||
console.error(
|
||||
"Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
|
||||
);
|
||||
|
@ -58,7 +58,7 @@ export default async (req: Request, res: Response) => {
|
|||
}
|
||||
|
||||
if (!auth && isAuthPath(path)) {
|
||||
return res.redirect("/login");
|
||||
return res.redirect(`/login?prev=${encodeURIComponent(url)}`);
|
||||
}
|
||||
|
||||
if (try_site.state === "success") {
|
||||
|
|
|
@ -72,10 +72,10 @@ export class App extends Component<AppProps, any> {
|
|||
|
||||
return (
|
||||
<ErrorGuard>
|
||||
<div tabIndex={-1}>
|
||||
<div tabIndex={-1} className="h-100">
|
||||
{RouteComponent &&
|
||||
(isAuthPath(path ?? "") ? (
|
||||
<AuthGuard>
|
||||
<AuthGuard {...routeProps}>
|
||||
<RouteComponent {...routeProps} />
|
||||
</AuthGuard>
|
||||
) : (
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, InfernoNode } from "inferno";
|
||||
import { Component } from "inferno";
|
||||
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||
import { UserService } from "../../services";
|
||||
import { Spinner } from "./icon";
|
||||
|
||||
|
@ -6,20 +7,26 @@ interface AuthGuardState {
|
|||
hasRedirected: boolean;
|
||||
}
|
||||
|
||||
class AuthGuard extends Component<{ children?: InfernoNode }, AuthGuardState> {
|
||||
class AuthGuard extends Component<
|
||||
RouteComponentProps<Record<string, string>>,
|
||||
AuthGuardState
|
||||
> {
|
||||
state = {
|
||||
hasRedirected: false,
|
||||
} as AuthGuardState;
|
||||
|
||||
constructor(props: any, context: any) {
|
||||
constructor(
|
||||
props: RouteComponentProps<Record<string, string>>,
|
||||
context: any
|
||||
) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!UserService.Instance.myUserInfo) {
|
||||
const { pathname, search } = this.props.location;
|
||||
this.context.router.history.replace(
|
||||
"/login",
|
||||
this.context.router.history.location
|
||||
`/login?prev=${encodeURIComponent(pathname + search)}`
|
||||
);
|
||||
} else {
|
||||
this.setState({ hasRedirected: true });
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { myAuth, setIsoData } from "@utils/app";
|
||||
import { isBrowser } from "@utils/browser";
|
||||
import { Location } from "history";
|
||||
import { getQueryParams } from "@utils/helpers";
|
||||
import { Component, linkEvent } from "inferno";
|
||||
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||
import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
|
||||
|
@ -11,6 +11,17 @@ import { HtmlTags } from "../common/html-tags";
|
|||
import { Spinner } from "../common/icon";
|
||||
import PasswordInput from "../common/password-input";
|
||||
|
||||
interface LoginProps {
|
||||
prev?: string;
|
||||
}
|
||||
|
||||
const getLoginQueryParams = () =>
|
||||
getQueryParams<LoginProps>({
|
||||
prev(param) {
|
||||
return param ? decodeURIComponent(param) : undefined;
|
||||
},
|
||||
});
|
||||
|
||||
interface State {
|
||||
loginRes: RequestState<LoginResponse>;
|
||||
form: {
|
||||
|
@ -57,11 +68,10 @@ async function handleLoginSubmit(i: Login, event: any) {
|
|||
UserService.Instance.myUserInfo = site.data.my_user;
|
||||
}
|
||||
|
||||
const { hash, pathname, search } = (i.props.history.location.state ??
|
||||
{}) as Location;
|
||||
const { prev } = getLoginQueryParams();
|
||||
|
||||
i.props.history.location.state
|
||||
? i.props.history.replace({ hash, pathname, search })
|
||||
prev
|
||||
? i.props.history.replace(prev)
|
||||
: i.props.history.action === "PUSH"
|
||||
? i.props.history.back()
|
||||
: i.props.history.replace("/");
|
||||
|
|
135
src/shared/components/remote_fetch.tsx
Normal file
135
src/shared/components/remote_fetch.tsx
Normal file
|
@ -0,0 +1,135 @@
|
|||
import { myAuthRequired, setIsoData } from "@utils/app";
|
||||
import { getQueryParams } from "@utils/helpers";
|
||||
import { QueryParams, RouteDataResponse } from "@utils/types";
|
||||
import { Component } from "inferno";
|
||||
import { Link } from "inferno-router";
|
||||
import { ResolveObjectResponse } from "lemmy-js-client";
|
||||
import { InitialFetchRequest } from "../interfaces";
|
||||
import { FirstLoadService, HttpService, I18NextService } from "../services";
|
||||
import { RequestState } from "../services/HttpService";
|
||||
import { HtmlTags } from "./common/html-tags";
|
||||
|
||||
interface RemoteFetchProps {
|
||||
uri?: string;
|
||||
}
|
||||
|
||||
type RemoteFetchData = RouteDataResponse<{
|
||||
resolveObjectRes: ResolveObjectResponse;
|
||||
}>;
|
||||
|
||||
interface RemoteFetchState {
|
||||
resolveObjectRes: RequestState<ResolveObjectResponse>;
|
||||
isIsomorphic: boolean;
|
||||
}
|
||||
|
||||
const getUriFromQuery = (uri?: string): string | undefined =>
|
||||
uri ? decodeURIComponent(uri) : undefined;
|
||||
|
||||
const getRemoteFetchQueryParams = () =>
|
||||
getQueryParams<RemoteFetchProps>({
|
||||
uri: getUriFromQuery,
|
||||
});
|
||||
|
||||
function uriToQuery(uri: string) {
|
||||
const match = decodeURIComponent(uri).match(/https?:\/\/(.+)\/c\/(.+)/);
|
||||
|
||||
return match ? `!${match[2]}@${match[1]}` : "";
|
||||
}
|
||||
|
||||
export class RemoteFetch extends Component<any, RemoteFetchState> {
|
||||
private isoData = setIsoData<RemoteFetchData>(this.context);
|
||||
state: RemoteFetchState = {
|
||||
resolveObjectRes: { state: "empty" },
|
||||
isIsomorphic: false,
|
||||
};
|
||||
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
|
||||
if (FirstLoadService.isFirstLoad) {
|
||||
// const { resolveObjectRes } = this.isoData.routeData;
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
isIsomorphic: true,
|
||||
// resolveObjectRes,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
if (!this.state.isIsomorphic) {
|
||||
const { uri } = getRemoteFetchQueryParams();
|
||||
|
||||
if (uri) {
|
||||
this.setState({ resolveObjectRes: { state: "loading" } });
|
||||
this.setState({
|
||||
resolveObjectRes: await HttpService.client.resolveObject({
|
||||
auth: myAuthRequired(),
|
||||
q: uriToQuery(uri),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="remote-fetch container-lg">
|
||||
<HtmlTags
|
||||
title={this.documentTitle}
|
||||
path={this.context.router.route.match.url}
|
||||
/>
|
||||
<div className="row">
|
||||
<div className="col-12 col-lg-6 offset-lg-3 text-center">
|
||||
{this.content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
get content() {
|
||||
const status: "success" | "loading" | "empty" = "success";
|
||||
|
||||
switch (status) {
|
||||
case "success": {
|
||||
return (
|
||||
<>
|
||||
<h1>Community Federated!</h1>
|
||||
<Link href="/" className="btn btn-lg bt-link btn-primary mt-auto">
|
||||
Click to visit com
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get documentTitle(): string {
|
||||
const { uri } = getRemoteFetchQueryParams();
|
||||
const name = this.isoData.site_res.site_view.site.name;
|
||||
return `${I18NextService.i18n.t("search")} - ${
|
||||
uri ? `${uri} - ` : ""
|
||||
}${name}`;
|
||||
}
|
||||
|
||||
static async fetchInitialData({
|
||||
client,
|
||||
auth,
|
||||
query: { uri },
|
||||
}: InitialFetchRequest<
|
||||
QueryParams<RemoteFetchProps>
|
||||
>): Promise<RemoteFetchData> {
|
||||
const data: RemoteFetchData = { resolveObjectRes: { state: "empty" } };
|
||||
|
||||
if (uri && auth) {
|
||||
data.resolveObjectRes = await client.resolveObject({
|
||||
auth,
|
||||
q: uriToQuery(uri),
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import { VerifyEmail } from "./components/person/verify-email";
|
|||
import { CreatePost } from "./components/post/create-post";
|
||||
import { Post } from "./components/post/post";
|
||||
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
|
||||
import { RemoteFetch } from "./components/remote_fetch";
|
||||
import { Search } from "./components/search";
|
||||
import { InitialFetchRequest, RouteData } from "./interfaces";
|
||||
|
||||
|
@ -140,4 +141,9 @@ export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
|
|||
fetchInitialData: Instances.fetchInitialData,
|
||||
},
|
||||
{ path: `/legal`, component: Legal },
|
||||
{
|
||||
path: "/activitypub/externalInteraction",
|
||||
component: RemoteFetch,
|
||||
fetchInitialData: RemoteFetch.fetchInitialData,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export default function isAuthPath(pathname: string) {
|
||||
return /^\/(create_.*?|inbox|settings|admin|reports|registration_applications)\b/g.test(
|
||||
return /^\/(create_.*?|inbox|settings|admin|reports|registration_applications|activitypub.*?)\b/g.test(
|
||||
pathname
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue