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 errorPageData: ErrorPageData | undefined = undefined;
|
||||||
let try_site = await client.getSite(getSiteForm);
|
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(
|
console.error(
|
||||||
"Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
|
"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)) {
|
if (!auth && isAuthPath(path)) {
|
||||||
return res.redirect("/login");
|
return res.redirect(`/login?prev=${encodeURIComponent(url)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try_site.state === "success") {
|
if (try_site.state === "success") {
|
||||||
|
|
|
@ -72,10 +72,10 @@ export class App extends Component<AppProps, any> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorGuard>
|
<ErrorGuard>
|
||||||
<div tabIndex={-1}>
|
<div tabIndex={-1} className="h-100">
|
||||||
{RouteComponent &&
|
{RouteComponent &&
|
||||||
(isAuthPath(path ?? "") ? (
|
(isAuthPath(path ?? "") ? (
|
||||||
<AuthGuard>
|
<AuthGuard {...routeProps}>
|
||||||
<RouteComponent {...routeProps} />
|
<RouteComponent {...routeProps} />
|
||||||
</AuthGuard>
|
</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 { UserService } from "../../services";
|
||||||
import { Spinner } from "./icon";
|
import { Spinner } from "./icon";
|
||||||
|
|
||||||
|
@ -6,20 +7,26 @@ interface AuthGuardState {
|
||||||
hasRedirected: boolean;
|
hasRedirected: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthGuard extends Component<{ children?: InfernoNode }, AuthGuardState> {
|
class AuthGuard extends Component<
|
||||||
|
RouteComponentProps<Record<string, string>>,
|
||||||
|
AuthGuardState
|
||||||
|
> {
|
||||||
state = {
|
state = {
|
||||||
hasRedirected: false,
|
hasRedirected: false,
|
||||||
} as AuthGuardState;
|
} as AuthGuardState;
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(
|
||||||
|
props: RouteComponentProps<Record<string, string>>,
|
||||||
|
context: any
|
||||||
|
) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (!UserService.Instance.myUserInfo) {
|
if (!UserService.Instance.myUserInfo) {
|
||||||
|
const { pathname, search } = this.props.location;
|
||||||
this.context.router.history.replace(
|
this.context.router.history.replace(
|
||||||
"/login",
|
`/login?prev=${encodeURIComponent(pathname + search)}`
|
||||||
this.context.router.history.location
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.setState({ hasRedirected: true });
|
this.setState({ hasRedirected: true });
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { myAuth, setIsoData } from "@utils/app";
|
import { myAuth, setIsoData } from "@utils/app";
|
||||||
import { isBrowser } from "@utils/browser";
|
import { isBrowser } from "@utils/browser";
|
||||||
import { Location } from "history";
|
import { getQueryParams } from "@utils/helpers";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { RouteComponentProps } from "inferno-router/dist/Route";
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
|
import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
|
||||||
|
@ -11,6 +11,17 @@ import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import PasswordInput from "../common/password-input";
|
import PasswordInput from "../common/password-input";
|
||||||
|
|
||||||
|
interface LoginProps {
|
||||||
|
prev?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLoginQueryParams = () =>
|
||||||
|
getQueryParams<LoginProps>({
|
||||||
|
prev(param) {
|
||||||
|
return param ? decodeURIComponent(param) : undefined;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
loginRes: RequestState<LoginResponse>;
|
loginRes: RequestState<LoginResponse>;
|
||||||
form: {
|
form: {
|
||||||
|
@ -57,11 +68,10 @@ async function handleLoginSubmit(i: Login, event: any) {
|
||||||
UserService.Instance.myUserInfo = site.data.my_user;
|
UserService.Instance.myUserInfo = site.data.my_user;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hash, pathname, search } = (i.props.history.location.state ??
|
const { prev } = getLoginQueryParams();
|
||||||
{}) as Location;
|
|
||||||
|
|
||||||
i.props.history.location.state
|
prev
|
||||||
? i.props.history.replace({ hash, pathname, search })
|
? i.props.history.replace(prev)
|
||||||
: i.props.history.action === "PUSH"
|
: i.props.history.action === "PUSH"
|
||||||
? i.props.history.back()
|
? i.props.history.back()
|
||||||
: i.props.history.replace("/");
|
: 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 { CreatePost } from "./components/post/create-post";
|
||||||
import { Post } from "./components/post/post";
|
import { Post } from "./components/post/post";
|
||||||
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
|
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
|
||||||
|
import { RemoteFetch } from "./components/remote_fetch";
|
||||||
import { Search } from "./components/search";
|
import { Search } from "./components/search";
|
||||||
import { InitialFetchRequest, RouteData } from "./interfaces";
|
import { InitialFetchRequest, RouteData } from "./interfaces";
|
||||||
|
|
||||||
|
@ -140,4 +141,9 @@ export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
|
||||||
fetchInitialData: Instances.fetchInitialData,
|
fetchInitialData: Instances.fetchInitialData,
|
||||||
},
|
},
|
||||||
{ path: `/legal`, component: Legal },
|
{ path: `/legal`, component: Legal },
|
||||||
|
{
|
||||||
|
path: "/activitypub/externalInteraction",
|
||||||
|
component: RemoteFetch,
|
||||||
|
fetchInitialData: RemoteFetch.fetchInitialData,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export default function isAuthPath(pathname: string) {
|
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
|
pathname
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue