mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-26 06:11:15 +00:00
Rework query parsing (#2396)
* Pass parsed query params as props to components * Pass parsed query params to fetchInitialData * Pass router Match to fetchInitialData * Cast individual routes to their concrete types Adds an IRoutePropsWithFetch definition for routes with getQueryParams or fetchInitialData to cause compiler errors when the types no longer match. * Don't double decode query parameters. Problem: A search for "%ab" produces a url with "%25ab". Refreshing the page results in URLSearchParams turning "%25ab" back into "%ab". decodeURIComponent() then complains about "%ab" being malformed. This removes decodeURIComponent() calls for query parameters and composes all query strings with getQueryString(), which now uses URLSearchParams. Query parsing already goes through getQueryParams() which also uses URLSearchParams. * Fix for PictrsImage when src also has query params * Small getQueryParams cleanup
This commit is contained in:
parent
579aea40d0
commit
70e382b3d9
32 changed files with 695 additions and 368 deletions
|
@ -3,6 +3,7 @@ import { getHttpBaseInternal } from "@utils/env";
|
||||||
import { ErrorPageData } from "@utils/types";
|
import { ErrorPageData } from "@utils/types";
|
||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
import { StaticRouter, matchPath } from "inferno-router";
|
import { StaticRouter, matchPath } from "inferno-router";
|
||||||
|
import { Match } from "inferno-router/dist/Route";
|
||||||
import { renderToString } from "inferno-server";
|
import { renderToString } from "inferno-server";
|
||||||
import { GetSiteResponse, LemmyHttp } from "lemmy-js-client";
|
import { GetSiteResponse, LemmyHttp } from "lemmy-js-client";
|
||||||
import { App } from "../../shared/components/app/app";
|
import { App } from "../../shared/components/app/app";
|
||||||
|
@ -25,6 +26,8 @@ import {
|
||||||
LanguageService,
|
LanguageService,
|
||||||
UserService,
|
UserService,
|
||||||
} from "../../shared/services/";
|
} from "../../shared/services/";
|
||||||
|
import { parsePath } from "history";
|
||||||
|
import { getQueryString } from "@utils/helpers";
|
||||||
|
|
||||||
export default async (req: Request, res: Response) => {
|
export default async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
@ -40,7 +43,10 @@ export default async (req: Request, res: Response) => {
|
||||||
.sort((a, b) => b.q - a.q)
|
.sort((a, b) => b.q - a.q)
|
||||||
.map(x => (x.lang === "*" ? "en" : x.lang)) ?? [];
|
.map(x => (x.lang === "*" ? "en" : x.lang)) ?? [];
|
||||||
|
|
||||||
const activeRoute = routes.find(route => matchPath(req.path, route));
|
let match: Match<any> | null | undefined;
|
||||||
|
const activeRoute = routes.find(
|
||||||
|
route => (match = matchPath(req.path, route)),
|
||||||
|
);
|
||||||
|
|
||||||
const headers = setForwardedHeaders(req.headers);
|
const headers = setForwardedHeaders(req.headers);
|
||||||
const auth = getJwtCookie(req.headers);
|
const auth = getJwtCookie(req.headers);
|
||||||
|
@ -49,7 +55,7 @@ export default async (req: Request, res: Response) => {
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { path, url, query } = req;
|
const { path, url } = req;
|
||||||
|
|
||||||
// Get site data first
|
// Get site data first
|
||||||
// This bypasses errors, so that the client can hit the error on its own,
|
// This bypasses errors, so that the client can hit the error on its own,
|
||||||
|
@ -71,7 +77,7 @@ export default async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!auth && isAuthPath(path)) {
|
if (!auth && isAuthPath(path)) {
|
||||||
return res.redirect(`/login?prev=${encodeURIComponent(url)}`);
|
return res.redirect(`/login${getQueryString({ prev: url })}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try_site.state === "success") {
|
if (try_site.state === "success") {
|
||||||
|
@ -83,10 +89,12 @@ export default async (req: Request, res: Response) => {
|
||||||
return res.redirect("/setup");
|
return res.redirect("/setup");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (site && activeRoute?.fetchInitialData) {
|
if (site && activeRoute?.fetchInitialData && match) {
|
||||||
const initialFetchReq: InitialFetchRequest = {
|
const { search } = parsePath(url);
|
||||||
|
const initialFetchReq: InitialFetchRequest<Record<string, any>> = {
|
||||||
path,
|
path,
|
||||||
query,
|
query: activeRoute.getQueryParams?.(search, site) ?? {},
|
||||||
|
match,
|
||||||
site,
|
site,
|
||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,7 +49,12 @@ export class App extends Component<any, any> {
|
||||||
<div className="mt-4 p-0 fl-1">
|
<div className="mt-4 p-0 fl-1">
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes.map(
|
{routes.map(
|
||||||
({ path, component: RouteComponent, fetchInitialData }) => (
|
({
|
||||||
|
path,
|
||||||
|
component: RouteComponent,
|
||||||
|
fetchInitialData,
|
||||||
|
getQueryParams,
|
||||||
|
}) => (
|
||||||
<Route
|
<Route
|
||||||
key={path}
|
key={path}
|
||||||
path={path}
|
path={path}
|
||||||
|
@ -59,20 +64,34 @@ export class App extends Component<any, any> {
|
||||||
FirstLoadService.falsify();
|
FirstLoadService.falsify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let queryProps = routeProps;
|
||||||
|
if (getQueryParams && this.isoData.site_res) {
|
||||||
|
// ErrorGuard will not render its children when
|
||||||
|
// site_res is missing, this guarantees that props
|
||||||
|
// will always contain the query params.
|
||||||
|
queryProps = {
|
||||||
|
...routeProps,
|
||||||
|
...getQueryParams(
|
||||||
|
routeProps.location.search,
|
||||||
|
this.isoData.site_res,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorGuard>
|
<ErrorGuard>
|
||||||
<div tabIndex={-1}>
|
<div tabIndex={-1}>
|
||||||
{RouteComponent &&
|
{RouteComponent &&
|
||||||
(isAuthPath(path ?? "") ? (
|
(isAuthPath(path ?? "") ? (
|
||||||
<AuthGuard {...routeProps}>
|
<AuthGuard {...routeProps}>
|
||||||
<RouteComponent {...routeProps} />
|
<RouteComponent {...queryProps} />
|
||||||
</AuthGuard>
|
</AuthGuard>
|
||||||
) : isAnonymousPath(path ?? "") ? (
|
) : isAnonymousPath(path ?? "") ? (
|
||||||
<AnonymousGuard>
|
<AnonymousGuard>
|
||||||
<RouteComponent {...routeProps} />
|
<RouteComponent {...queryProps} />
|
||||||
</AnonymousGuard>
|
</AnonymousGuard>
|
||||||
) : (
|
) : (
|
||||||
<RouteComponent {...routeProps} />
|
<RouteComponent {...queryProps} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ErrorGuard>
|
</ErrorGuard>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from "inferno";
|
||||||
import { RouteComponentProps } from "inferno-router/dist/Route";
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import { Spinner } from "./icon";
|
import { Spinner } from "./icon";
|
||||||
|
import { getQueryString } from "@utils/helpers";
|
||||||
|
|
||||||
interface AuthGuardState {
|
interface AuthGuardState {
|
||||||
hasRedirected: boolean;
|
hasRedirected: boolean;
|
||||||
|
@ -26,7 +27,7 @@ class AuthGuard extends Component<
|
||||||
if (!UserService.Instance.myUserInfo) {
|
if (!UserService.Instance.myUserInfo) {
|
||||||
const { pathname, search } = this.props.location;
|
const { pathname, search } = this.props.location;
|
||||||
this.context.router.history.replace(
|
this.context.router.history.replace(
|
||||||
`/login?prev=${encodeURIComponent(pathname + search)}`,
|
`/login${getQueryString({ prev: pathname + search })}`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.setState({ hasRedirected: true });
|
this.setState({ hasRedirected: true });
|
||||||
|
|
|
@ -68,28 +68,31 @@ export class PictrsImage extends Component<PictrsImageProps, any> {
|
||||||
// sample url:
|
// sample url:
|
||||||
// http://localhost:8535/pictrs/image/file.png?thumbnail=256&format=jpg
|
// http://localhost:8535/pictrs/image/file.png?thumbnail=256&format=jpg
|
||||||
|
|
||||||
const split = this.props.src.split("/pictrs/image/");
|
let url: URL | undefined;
|
||||||
|
try {
|
||||||
// If theres not multiple, then its not a pictrs image
|
url = new URL(this.props.src);
|
||||||
if (split.length === 1) {
|
} catch {
|
||||||
return this.props.src;
|
return this.props.src;
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = split[0];
|
// If theres no match, then its not a pictrs image
|
||||||
const path = split[1];
|
if (!url.pathname.includes("/pictrs/image/")) {
|
||||||
|
return this.props.src;
|
||||||
const params = { format };
|
|
||||||
|
|
||||||
if (this.props.thumbnail) {
|
|
||||||
params["thumbnail"] = thumbnailSize;
|
|
||||||
} else if (this.props.icon) {
|
|
||||||
params["thumbnail"] = iconThumbnailSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramsStr = new URLSearchParams(params).toString();
|
// Keeps original search params. Could probably do `url.search = ""` here.
|
||||||
const out = `${host}/pictrs/image/${path}?${paramsStr}`;
|
|
||||||
|
|
||||||
return out;
|
url.searchParams.set("format", format);
|
||||||
|
|
||||||
|
if (this.props.thumbnail) {
|
||||||
|
url.searchParams.set("thumbnail", thumbnailSize.toString());
|
||||||
|
} else if (this.props.icon) {
|
||||||
|
url.searchParams.set("thumbnail", iconThumbnailSize.toString());
|
||||||
|
} else {
|
||||||
|
url.searchParams.delete("thumbnail");
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
alt(): string {
|
alt(): string {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { validInstanceTLD } from "@utils/helpers";
|
import { getQueryString, validInstanceTLD } from "@utils/helpers";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { NoOptionI18nKeys } from "i18next";
|
import { NoOptionI18nKeys } from "i18next";
|
||||||
import { Component, MouseEventHandler, linkEvent } from "inferno";
|
import { Component, MouseEventHandler, linkEvent } from "inferno";
|
||||||
|
@ -134,8 +134,8 @@ function submitRemoteFollow(
|
||||||
instanceText = `http${VERSION !== "dev" ? "s" : ""}://${instanceText}`;
|
instanceText = `http${VERSION !== "dev" ? "s" : ""}://${instanceText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = `${instanceText}/activitypub/externalInteraction?uri=${encodeURIComponent(
|
window.location.href = `${instanceText}/activitypub/externalInteraction${getQueryString(
|
||||||
communityActorId,
|
{ uri: communityActorId },
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,8 @@ import { CommunityLink } from "./community-link";
|
||||||
import { communityLimit } from "../../config";
|
import { communityLimit } from "../../config";
|
||||||
import { SubscribeButton } from "../common/subscribe-button";
|
import { SubscribeButton } from "../common/subscribe-button";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
type CommunitiesData = RouteDataResponse<{
|
type CommunitiesData = RouteDataResponse<{
|
||||||
listCommunitiesResponse: ListCommunitiesResponse;
|
listCommunitiesResponse: ListCommunitiesResponse;
|
||||||
|
@ -62,15 +64,30 @@ function getSortTypeFromQuery(type?: string): SortType {
|
||||||
return type ? (type as SortType) : "TopMonth";
|
return type ? (type as SortType) : "TopMonth";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCommunitiesQueryParams() {
|
export function getCommunitiesQueryParams(source?: string): CommunitiesProps {
|
||||||
return getQueryParams<CommunitiesProps>({
|
return getQueryParams<CommunitiesProps>(
|
||||||
|
{
|
||||||
listingType: getListingTypeFromQuery,
|
listingType: getListingTypeFromQuery,
|
||||||
sort: getSortTypeFromQuery,
|
sort: getSortTypeFromQuery,
|
||||||
page: getPageFromString,
|
page: getPageFromString,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Communities extends Component<any, CommunitiesState> {
|
type CommunitiesPathProps = Record<string, never>;
|
||||||
|
type CommunitiesRouteProps = RouteComponentProps<CommunitiesPathProps> &
|
||||||
|
CommunitiesProps;
|
||||||
|
export type CommunitiesFetchConfig = IRoutePropsWithFetch<
|
||||||
|
CommunitiesData,
|
||||||
|
CommunitiesPathProps,
|
||||||
|
CommunitiesProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Communities extends Component<
|
||||||
|
CommunitiesRouteProps,
|
||||||
|
CommunitiesState
|
||||||
|
> {
|
||||||
private isoData = setIsoData<CommunitiesData>(this.context);
|
private isoData = setIsoData<CommunitiesData>(this.context);
|
||||||
state: CommunitiesState = {
|
state: CommunitiesState = {
|
||||||
listCommunitiesResponse: EMPTY_REQUEST,
|
listCommunitiesResponse: EMPTY_REQUEST,
|
||||||
|
@ -79,7 +96,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
isIsomorphic: false,
|
isIsomorphic: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: CommunitiesRouteProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
|
@ -118,7 +135,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
</h5>
|
</h5>
|
||||||
);
|
);
|
||||||
case "success": {
|
case "success": {
|
||||||
const { listingType, sort, page } = getCommunitiesQueryParams();
|
const { listingType, sort, page } = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="h4 mb-4">
|
<h1 className="h4 mb-4">
|
||||||
|
@ -268,7 +285,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
listingType: urlListingType,
|
listingType: urlListingType,
|
||||||
sort: urlSort,
|
sort: urlSort,
|
||||||
page: urlPage,
|
page: urlPage,
|
||||||
} = getCommunitiesQueryParams();
|
} = this.props;
|
||||||
|
|
||||||
const queryParams: QueryParams<CommunitiesProps> = {
|
const queryParams: QueryParams<CommunitiesProps> = {
|
||||||
listingType: listingType ?? urlListingType,
|
listingType: listingType ?? urlListingType,
|
||||||
|
@ -302,10 +319,10 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
handleSearchSubmit(i: Communities, event: any) {
|
handleSearchSubmit(i: Communities, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const searchParamEncoded = encodeURIComponent(i.state.searchText);
|
const searchParamEncoded = i.state.searchText;
|
||||||
const { listingType } = getCommunitiesQueryParams();
|
const { listingType } = i.props;
|
||||||
i.context.router.history.push(
|
i.context.router.history.push(
|
||||||
`/search?q=${searchParamEncoded}&type=Communities&listingType=${listingType}`,
|
`/search${getQueryString({ q: searchParamEncoded, type: "Communities", listingType })}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,16 +330,17 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
headers,
|
headers,
|
||||||
query: { listingType, sort, page },
|
query: { listingType, sort, page },
|
||||||
}: InitialFetchRequest<
|
}: InitialFetchRequest<
|
||||||
QueryParams<CommunitiesProps>
|
CommunitiesPathProps,
|
||||||
|
CommunitiesProps
|
||||||
>): Promise<CommunitiesData> {
|
>): Promise<CommunitiesData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
const listCommunitiesForm: ListCommunities = {
|
const listCommunitiesForm: ListCommunities = {
|
||||||
type_: getListingTypeFromQuery(listingType),
|
type_: listingType,
|
||||||
sort: getSortTypeFromQuery(sort),
|
sort,
|
||||||
limit: communityLimit,
|
limit: communityLimit,
|
||||||
page: getPageFromString(page),
|
page,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -346,7 +364,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
async refetch() {
|
async refetch() {
|
||||||
this.setState({ listCommunitiesResponse: LOADING_REQUEST });
|
this.setState({ listCommunitiesResponse: LOADING_REQUEST });
|
||||||
|
|
||||||
const { listingType, sort, page } = getCommunitiesQueryParams();
|
const { listingType, sort, page } = this.props;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
listCommunitiesResponse: await HttpService.client.listCommunities({
|
listCommunitiesResponse: await HttpService.client.listCommunities({
|
||||||
|
|
|
@ -101,6 +101,7 @@ import { CommunityLink } from "./community-link";
|
||||||
import { PaginatorCursor } from "../common/paginator-cursor";
|
import { PaginatorCursor } from "../common/paginator-cursor";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
import { Sidebar } from "./sidebar";
|
import { Sidebar } from "./sidebar";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
type CommunityData = RouteDataResponse<{
|
type CommunityData = RouteDataResponse<{
|
||||||
communityRes: GetCommunityResponse;
|
communityRes: GetCommunityResponse;
|
||||||
|
@ -124,12 +125,26 @@ interface CommunityProps {
|
||||||
pageCursor?: PaginationCursor;
|
pageCursor?: PaginationCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCommunityQueryParams() {
|
type Fallbacks = { sort: SortType };
|
||||||
return getQueryParams<CommunityProps>({
|
|
||||||
|
export function getCommunityQueryParams(
|
||||||
|
source: string | undefined,
|
||||||
|
siteRes: GetSiteResponse,
|
||||||
|
) {
|
||||||
|
const myUserInfo = siteRes.my_user ?? UserService.Instance.myUserInfo;
|
||||||
|
const local_user = myUserInfo?.local_user_view.local_user;
|
||||||
|
const local_site = siteRes.site_view.local_site;
|
||||||
|
return getQueryParams<CommunityProps, Fallbacks>(
|
||||||
|
{
|
||||||
dataType: getDataTypeFromQuery,
|
dataType: getDataTypeFromQuery,
|
||||||
pageCursor: cursor => cursor,
|
pageCursor: (cursor?: string) => cursor,
|
||||||
sort: getSortTypeFromQuery,
|
sort: getSortTypeFromQuery,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
{
|
||||||
|
sort: local_user?.default_sort_type ?? local_site.default_sort_type,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDataTypeFromQuery(type?: string): DataType {
|
function getDataTypeFromQuery(type?: string): DataType {
|
||||||
|
@ -144,10 +159,16 @@ function getSortTypeFromQuery(type?: string): SortType {
|
||||||
return type ? (type as SortType) : mySortType ?? "Active";
|
return type ? (type as SortType) : mySortType ?? "Active";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Community extends Component<
|
type CommunityPathProps = { name: string };
|
||||||
RouteComponentProps<{ name: string }>,
|
type CommunityRouteProps = RouteComponentProps<CommunityPathProps> &
|
||||||
State
|
CommunityProps;
|
||||||
> {
|
export type CommunityFetchConfig = IRoutePropsWithFetch<
|
||||||
|
CommunityData,
|
||||||
|
CommunityPathProps,
|
||||||
|
CommunityProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Community extends Component<CommunityRouteProps, State> {
|
||||||
private isoData = setIsoData<CommunityData>(this.context);
|
private isoData = setIsoData<CommunityData>(this.context);
|
||||||
state: State = {
|
state: State = {
|
||||||
communityRes: EMPTY_REQUEST,
|
communityRes: EMPTY_REQUEST,
|
||||||
|
@ -159,7 +180,7 @@ export class Community extends Component<
|
||||||
isIsomorphic: false,
|
isIsomorphic: false,
|
||||||
};
|
};
|
||||||
private readonly mainContentRef: RefObject<HTMLElement>;
|
private readonly mainContentRef: RefObject<HTMLElement>;
|
||||||
constructor(props: RouteComponentProps<{ name: string }>, context: any) {
|
constructor(props: CommunityRouteProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
|
@ -234,25 +255,22 @@ export class Community extends Component<
|
||||||
|
|
||||||
static async fetchInitialData({
|
static async fetchInitialData({
|
||||||
headers,
|
headers,
|
||||||
path,
|
query: { dataType, pageCursor, sort },
|
||||||
query: { dataType: urlDataType, pageCursor, sort: urlSort },
|
match: {
|
||||||
}: InitialFetchRequest<QueryParams<CommunityProps>>): Promise<
|
params: { name: communityName },
|
||||||
Promise<CommunityData>
|
},
|
||||||
> {
|
}: InitialFetchRequest<
|
||||||
|
CommunityPathProps,
|
||||||
|
CommunityProps
|
||||||
|
>): Promise<CommunityData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
const pathSplit = path.split("/");
|
|
||||||
|
|
||||||
const communityName = pathSplit[2];
|
|
||||||
const communityForm: GetCommunity = {
|
const communityForm: GetCommunity = {
|
||||||
name: communityName,
|
name: communityName,
|
||||||
};
|
};
|
||||||
|
|
||||||
const dataType = getDataTypeFromQuery(urlDataType);
|
|
||||||
|
|
||||||
const sort = getSortTypeFromQuery(urlSort);
|
|
||||||
|
|
||||||
let postsFetch: Promise<RequestState<GetPostsResponse>> =
|
let postsFetch: Promise<RequestState<GetPostsResponse>> =
|
||||||
Promise.resolve(EMPTY_REQUEST);
|
Promise.resolve(EMPTY_REQUEST);
|
||||||
let commentsFetch: Promise<RequestState<GetCommentsResponse>> =
|
let commentsFetch: Promise<RequestState<GetCommentsResponse>> =
|
||||||
|
@ -411,7 +429,7 @@ export class Community extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
listings(communityRes: GetCommunityResponse) {
|
listings(communityRes: GetCommunityResponse) {
|
||||||
const { dataType } = getCommunityQueryParams();
|
const { dataType } = this.props;
|
||||||
const { site_res } = this.isoData;
|
const { site_res } = this.isoData;
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
|
@ -534,7 +552,7 @@ export class Community extends Component<
|
||||||
// let communityRss = this.state.communityRes.map(r =>
|
// let communityRss = this.state.communityRes.map(r =>
|
||||||
// communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
|
// communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
|
||||||
// );
|
// );
|
||||||
const { dataType, sort } = getCommunityQueryParams();
|
const { dataType, sort } = this.props;
|
||||||
const communityRss = res
|
const communityRss = res
|
||||||
? communityRSSUrl(res.community_view.community.actor_id, sort)
|
? communityRSSUrl(res.community_view.community.actor_id, sort)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -592,7 +610,7 @@ export class Community extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateUrl({ dataType, pageCursor, sort }: Partial<CommunityProps>) {
|
async updateUrl({ dataType, pageCursor, sort }: Partial<CommunityProps>) {
|
||||||
const { dataType: urlDataType, sort: urlSort } = getCommunityQueryParams();
|
const { dataType: urlDataType, sort: urlSort } = this.props;
|
||||||
|
|
||||||
const queryParams: QueryParams<CommunityProps> = {
|
const queryParams: QueryParams<CommunityProps> = {
|
||||||
dataType: getDataTypeString(dataType ?? urlDataType),
|
dataType: getDataTypeString(dataType ?? urlDataType),
|
||||||
|
@ -608,7 +626,7 @@ export class Community extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
const { dataType, pageCursor, sort } = getCommunityQueryParams();
|
const { dataType, pageCursor, sort } = this.props;
|
||||||
const { name } = this.props.match.params;
|
const { name } = this.props.match.params;
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { hostname } from "@utils/helpers";
|
import { getQueryString, hostname } from "@utils/helpers";
|
||||||
import { amAdmin, amMod, amTopMod } from "@utils/roles";
|
import { amAdmin, amMod, amTopMod } from "@utils/roles";
|
||||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
|
@ -287,7 +287,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
className={`btn btn-secondary d-block mb-2 w-100 ${
|
className={`btn btn-secondary d-block mb-2 w-100 ${
|
||||||
cv.community.deleted || cv.community.removed ? "no-click" : ""
|
cv.community.deleted || cv.community.removed ? "no-click" : ""
|
||||||
}`}
|
}`}
|
||||||
to={`/create_post?communityId=${cv.community.id}`}
|
to={
|
||||||
|
"/create_post" +
|
||||||
|
getQueryString({ communityId: cv.community.id.toString() })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{I18NextService.i18n.t("create_a_post")}
|
{I18NextService.i18n.t("create_a_post")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -34,6 +34,8 @@ import RateLimitForm from "./rate-limit-form";
|
||||||
import { SiteForm } from "./site-form";
|
import { SiteForm } from "./site-form";
|
||||||
import { TaglineForm } from "./tagline-form";
|
import { TaglineForm } from "./tagline-form";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
type AdminSettingsData = RouteDataResponse<{
|
type AdminSettingsData = RouteDataResponse<{
|
||||||
bannedRes: BannedPersonsResponse;
|
bannedRes: BannedPersonsResponse;
|
||||||
|
@ -52,7 +54,18 @@ interface AdminSettingsState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AdminSettings extends Component<any, AdminSettingsState> {
|
type AdminSettingsRouteProps = RouteComponentProps<Record<string, never>> &
|
||||||
|
Record<string, never>;
|
||||||
|
export type AdminSettingsFetchConfig = IRoutePropsWithFetch<
|
||||||
|
AdminSettingsData,
|
||||||
|
Record<string, never>,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class AdminSettings extends Component<
|
||||||
|
AdminSettingsRouteProps,
|
||||||
|
AdminSettingsState
|
||||||
|
> {
|
||||||
private isoData = setIsoData<AdminSettingsData>(this.context);
|
private isoData = setIsoData<AdminSettingsData>(this.context);
|
||||||
state: AdminSettingsState = {
|
state: AdminSettingsState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
|
|
|
@ -100,6 +100,8 @@ import { PostListings } from "../post/post-listings";
|
||||||
import { SiteSidebar } from "./site-sidebar";
|
import { SiteSidebar } from "./site-sidebar";
|
||||||
import { PaginatorCursor } from "../common/paginator-cursor";
|
import { PaginatorCursor } from "../common/paginator-cursor";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
interface HomeState {
|
interface HomeState {
|
||||||
postsRes: RequestState<GetPostsResponse>;
|
postsRes: RequestState<GetPostsResponse>;
|
||||||
|
@ -129,23 +131,22 @@ type HomeData = RouteDataResponse<{
|
||||||
trendingCommunitiesRes: ListCommunitiesResponse;
|
trendingCommunitiesRes: ListCommunitiesResponse;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
function getRss(listingType: ListingType) {
|
function getRss(listingType: ListingType, sort: SortType) {
|
||||||
const { sort } = getHomeQueryParams();
|
|
||||||
|
|
||||||
let rss: string | undefined = undefined;
|
let rss: string | undefined = undefined;
|
||||||
|
|
||||||
|
const queryString = getQueryString({ sort });
|
||||||
switch (listingType) {
|
switch (listingType) {
|
||||||
case "All": {
|
case "All": {
|
||||||
rss = `/feeds/all.xml?sort=${sort}`;
|
rss = "/feeds/all.xml" + queryString;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Local": {
|
case "Local": {
|
||||||
rss = `/feeds/local.xml?sort=${sort}`;
|
rss = "/feeds/local.xml" + queryString;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Subscribed": {
|
case "Subscribed": {
|
||||||
const auth = myAuth();
|
const auth = myAuth();
|
||||||
rss = auth ? `/feeds/front/${auth}.xml?sort=${sort}` : undefined;
|
rss = auth ? `/feeds/front/${auth}.xml${queryString}` : undefined;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,31 +168,46 @@ function getDataTypeFromQuery(type?: string): DataType {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getListingTypeFromQuery(
|
function getListingTypeFromQuery(
|
||||||
type?: string,
|
type: string | undefined,
|
||||||
myUserInfo = UserService.Instance.myUserInfo,
|
fallback: ListingType,
|
||||||
): ListingType | undefined {
|
): ListingType {
|
||||||
const myListingType =
|
return type ? (type as ListingType) : fallback;
|
||||||
myUserInfo?.local_user_view?.local_user?.default_listing_type;
|
|
||||||
|
|
||||||
return type ? (type as ListingType) : myListingType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSortTypeFromQuery(
|
function getSortTypeFromQuery(
|
||||||
type?: string,
|
type: string | undefined,
|
||||||
myUserInfo = UserService.Instance.myUserInfo,
|
fallback: SortType,
|
||||||
): SortType {
|
): SortType {
|
||||||
const mySortType = myUserInfo?.local_user_view?.local_user?.default_sort_type;
|
return type ? (type as SortType) : fallback;
|
||||||
|
|
||||||
return (type ? (type as SortType) : mySortType) ?? "Active";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHomeQueryParams() {
|
type Fallbacks = {
|
||||||
return getQueryParams<HomeProps>({
|
sort: SortType;
|
||||||
|
listingType: ListingType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getHomeQueryParams(
|
||||||
|
source: string | undefined,
|
||||||
|
siteRes: GetSiteResponse,
|
||||||
|
): HomeProps {
|
||||||
|
const myUserInfo = siteRes.my_user ?? UserService.Instance.myUserInfo;
|
||||||
|
const local_user = myUserInfo?.local_user_view.local_user;
|
||||||
|
const local_site = siteRes.site_view.local_site;
|
||||||
|
return getQueryParams<HomeProps, Fallbacks>(
|
||||||
|
{
|
||||||
sort: getSortTypeFromQuery,
|
sort: getSortTypeFromQuery,
|
||||||
listingType: getListingTypeFromQuery,
|
listingType: getListingTypeFromQuery,
|
||||||
pageCursor: cursor => cursor,
|
pageCursor: (cursor?: string) => cursor,
|
||||||
dataType: getDataTypeFromQuery,
|
dataType: getDataTypeFromQuery,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
{
|
||||||
|
sort: local_user?.default_sort_type ?? local_site.default_sort_type,
|
||||||
|
listingType:
|
||||||
|
local_user?.default_listing_type ??
|
||||||
|
local_site.default_post_listing_type,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MobileButton = ({
|
const MobileButton = ({
|
||||||
|
@ -224,7 +240,15 @@ const LinkButton = ({
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
||||||
export class Home extends Component<any, HomeState> {
|
type HomePathProps = Record<string, never>;
|
||||||
|
type HomeRouteProps = RouteComponentProps<HomePathProps> & HomeProps;
|
||||||
|
export type HomeFetchConfig = IRoutePropsWithFetch<
|
||||||
|
HomeData,
|
||||||
|
HomePathProps,
|
||||||
|
HomeProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Home extends Component<HomeRouteProps, HomeState> {
|
||||||
private isoData = setIsoData<HomeData>(this.context);
|
private isoData = setIsoData<HomeData>(this.context);
|
||||||
state: HomeState = {
|
state: HomeState = {
|
||||||
postsRes: EMPTY_REQUEST,
|
postsRes: EMPTY_REQUEST,
|
||||||
|
@ -310,20 +334,13 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fetchInitialData({
|
static async fetchInitialData({
|
||||||
query: { dataType: urlDataType, listingType, pageCursor, sort: urlSort },
|
query: { listingType, dataType, sort, pageCursor },
|
||||||
site,
|
|
||||||
headers,
|
headers,
|
||||||
}: InitialFetchRequest<QueryParams<HomeProps>>): Promise<HomeData> {
|
}: InitialFetchRequest<HomePathProps, HomeProps>): Promise<HomeData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const dataType = getDataTypeFromQuery(urlDataType);
|
|
||||||
const type_ =
|
|
||||||
getListingTypeFromQuery(listingType, site.my_user) ??
|
|
||||||
site.site_view.local_site.default_post_listing_type;
|
|
||||||
const sort = getSortTypeFromQuery(urlSort, site.my_user);
|
|
||||||
|
|
||||||
let postsFetch: Promise<RequestState<GetPostsResponse>> =
|
let postsFetch: Promise<RequestState<GetPostsResponse>> =
|
||||||
Promise.resolve(EMPTY_REQUEST);
|
Promise.resolve(EMPTY_REQUEST);
|
||||||
let commentsFetch: Promise<RequestState<GetCommentsResponse>> =
|
let commentsFetch: Promise<RequestState<GetCommentsResponse>> =
|
||||||
|
@ -331,7 +348,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
const getPostsForm: GetPosts = {
|
const getPostsForm: GetPosts = {
|
||||||
type_,
|
type_: listingType,
|
||||||
page_cursor: pageCursor,
|
page_cursor: pageCursor,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
sort,
|
sort,
|
||||||
|
@ -343,7 +360,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
const getCommentsForm: GetComments = {
|
const getCommentsForm: GetComments = {
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
sort: postToCommentSortType(sort),
|
sort: postToCommentSortType(sort),
|
||||||
type_,
|
type_: listingType,
|
||||||
saved_only: false,
|
saved_only: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -635,7 +652,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
dataType: urlDataType,
|
dataType: urlDataType,
|
||||||
listingType: urlListingType,
|
listingType: urlListingType,
|
||||||
sort: urlSort,
|
sort: urlSort,
|
||||||
} = getHomeQueryParams();
|
} = this.props;
|
||||||
|
|
||||||
const queryParams: QueryParams<HomeProps> = {
|
const queryParams: QueryParams<HomeProps> = {
|
||||||
dataType: getDataTypeString(dataType ?? urlDataType),
|
dataType: getDataTypeString(dataType ?? urlDataType),
|
||||||
|
@ -679,7 +696,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get listings() {
|
get listings() {
|
||||||
const { dataType } = getHomeQueryParams();
|
const { dataType } = this.props;
|
||||||
const siteRes = this.state.siteRes;
|
const siteRes = this.state.siteRes;
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
|
@ -771,7 +788,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get selects() {
|
get selects() {
|
||||||
const { listingType, dataType, sort } = getHomeQueryParams();
|
const { listingType, dataType, sort } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row align-items-center mb-3 g-3">
|
<div className="row align-items-center mb-3 g-3">
|
||||||
|
@ -799,6 +816,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
{getRss(
|
{getRss(
|
||||||
listingType ??
|
listingType ??
|
||||||
this.state.siteRes.site_view.local_site.default_post_listing_type,
|
this.state.siteRes.site_view.local_site.default_post_listing_type,
|
||||||
|
sort,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -817,7 +835,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
const { dataType, pageCursor, listingType, sort } = getHomeQueryParams();
|
const { dataType, pageCursor, listingType, sort } = this.props;
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
this.setState({ postsRes: LOADING_REQUEST });
|
this.setState({ postsRes: LOADING_REQUEST });
|
||||||
|
|
|
@ -22,6 +22,8 @@ import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import Tabs from "../common/tabs";
|
import Tabs from "../common/tabs";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
type InstancesData = RouteDataResponse<{
|
type InstancesData = RouteDataResponse<{
|
||||||
federatedInstancesResponse: GetFederatedInstancesResponse;
|
federatedInstancesResponse: GetFederatedInstancesResponse;
|
||||||
|
@ -33,7 +35,15 @@ interface InstancesState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Instances extends Component<any, InstancesState> {
|
type InstancesRouteProps = RouteComponentProps<Record<string, never>> &
|
||||||
|
Record<string, never>;
|
||||||
|
export type InstancesFetchConfig = IRoutePropsWithFetch<
|
||||||
|
InstancesData,
|
||||||
|
Record<string, never>,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Instances extends Component<InstancesRouteProps, InstancesState> {
|
||||||
private isoData = setIsoData<InstancesData>(this.context);
|
private isoData = setIsoData<InstancesData>(this.context);
|
||||||
state: InstancesState = {
|
state: InstancesState = {
|
||||||
instancesRes: EMPTY_REQUEST,
|
instancesRes: EMPTY_REQUEST,
|
||||||
|
|
|
@ -17,17 +17,21 @@ import { Spinner } from "../common/icon";
|
||||||
import PasswordInput from "../common/password-input";
|
import PasswordInput from "../common/password-input";
|
||||||
import TotpModal from "../common/totp-modal";
|
import TotpModal from "../common/totp-modal";
|
||||||
import { UnreadCounterService } from "../../services";
|
import { UnreadCounterService } from "../../services";
|
||||||
|
import { RouteData } from "../../interfaces";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
interface LoginProps {
|
interface LoginProps {
|
||||||
prev?: string;
|
prev?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLoginQueryParams = () =>
|
export function getLoginQueryParams(source?: string): LoginProps {
|
||||||
getQueryParams<LoginProps>({
|
return getQueryParams<LoginProps>(
|
||||||
prev(param) {
|
{
|
||||||
return param ? decodeURIComponent(param) : undefined;
|
prev: (param?: string) => param,
|
||||||
},
|
},
|
||||||
});
|
source,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
loginRes: RequestState<LoginResponse>;
|
loginRes: RequestState<LoginResponse>;
|
||||||
|
@ -50,7 +54,7 @@ async function handleLoginSuccess(i: Login, loginRes: LoginResponse) {
|
||||||
refreshTheme();
|
refreshTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { prev } = getLoginQueryParams();
|
const { prev } = i.props;
|
||||||
|
|
||||||
prev
|
prev
|
||||||
? i.props.history.replace(prev)
|
? i.props.history.replace(prev)
|
||||||
|
@ -114,10 +118,14 @@ function handleClose2faModal(i: Login) {
|
||||||
i.setState({ show2faModal: false });
|
i.setState({ show2faModal: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Login extends Component<
|
type LoginRouteProps = RouteComponentProps<Record<string, never>> & LoginProps;
|
||||||
RouteComponentProps<Record<string, never>>,
|
export type LoginFetchConfig = IRoutePropsWithFetch<
|
||||||
State
|
RouteData,
|
||||||
> {
|
Record<string, never>,
|
||||||
|
LoginProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Login extends Component<LoginRouteProps, State> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
|
|
||||||
state: State = {
|
state: State = {
|
||||||
|
|
|
@ -63,6 +63,7 @@ import { SearchableSelect } from "./common/searchable-select";
|
||||||
import { CommunityLink } from "./community/community-link";
|
import { CommunityLink } from "./community/community-link";
|
||||||
import { PersonListing } from "./person/person-listing";
|
import { PersonListing } from "./person/person-listing";
|
||||||
import { getHttpBaseInternal } from "../utils/env";
|
import { getHttpBaseInternal } from "../utils/env";
|
||||||
|
import { IRoutePropsWithFetch } from "../routes";
|
||||||
|
|
||||||
type FilterType = "mod" | "user";
|
type FilterType = "mod" | "user";
|
||||||
|
|
||||||
|
@ -97,13 +98,17 @@ interface ModlogType {
|
||||||
when_: string;
|
when_: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getModlogQueryParams = () =>
|
export function getModlogQueryParams(source?: string): ModlogProps {
|
||||||
getQueryParams<ModlogProps>({
|
return getQueryParams<ModlogProps>(
|
||||||
|
{
|
||||||
actionType: getActionFromString,
|
actionType: getActionFromString,
|
||||||
modId: getIdFromString,
|
modId: getIdFromString,
|
||||||
userId: getIdFromString,
|
userId: getIdFromString,
|
||||||
page: getPageFromString,
|
page: getPageFromString,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface ModlogState {
|
interface ModlogState {
|
||||||
res: RequestState<GetModlogResponse>;
|
res: RequestState<GetModlogResponse>;
|
||||||
|
@ -117,8 +122,8 @@ interface ModlogState {
|
||||||
|
|
||||||
interface ModlogProps {
|
interface ModlogProps {
|
||||||
page: number;
|
page: number;
|
||||||
userId?: number | null;
|
userId?: number;
|
||||||
modId?: number | null;
|
modId?: number;
|
||||||
actionType: ModlogActionType;
|
actionType: ModlogActionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,10 +637,15 @@ async function createNewOptions({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Modlog extends Component<
|
type ModlogPathProps = { communityId?: string };
|
||||||
RouteComponentProps<{ communityId?: string }>,
|
type ModlogRouteProps = RouteComponentProps<ModlogPathProps> & ModlogProps;
|
||||||
ModlogState
|
export type ModlogFetchConfig = IRoutePropsWithFetch<
|
||||||
> {
|
ModlogData,
|
||||||
|
ModlogPathProps,
|
||||||
|
ModlogProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Modlog extends Component<ModlogRouteProps, ModlogState> {
|
||||||
private isoData = setIsoData<ModlogData>(this.context);
|
private isoData = setIsoData<ModlogData>(this.context);
|
||||||
|
|
||||||
state: ModlogState = {
|
state: ModlogState = {
|
||||||
|
@ -648,10 +658,7 @@ export class Modlog extends Component<
|
||||||
isIsomorphic: false,
|
isIsomorphic: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(props: ModlogRouteProps, context: any) {
|
||||||
props: RouteComponentProps<{ communityId?: string }>,
|
|
||||||
context: any,
|
|
||||||
) {
|
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
this.handleUserChange = this.handleUserChange.bind(this);
|
this.handleUserChange = this.handleUserChange.bind(this);
|
||||||
|
@ -687,7 +694,7 @@ export class Modlog extends Component<
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
if (!this.state.isIsomorphic) {
|
if (!this.state.isIsomorphic) {
|
||||||
const { modId, userId } = getModlogQueryParams();
|
const { modId, userId } = this.props;
|
||||||
const promises = [this.refetch()];
|
const promises = [this.refetch()];
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
|
@ -774,7 +781,7 @@ export class Modlog extends Component<
|
||||||
userSearchOptions,
|
userSearchOptions,
|
||||||
modSearchOptions,
|
modSearchOptions,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { actionType, modId, userId } = getModlogQueryParams();
|
const { actionType, modId, userId } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modlog container-lg">
|
<div className="modlog container-lg">
|
||||||
|
@ -873,7 +880,7 @@ export class Modlog extends Component<
|
||||||
</h5>
|
</h5>
|
||||||
);
|
);
|
||||||
case "success": {
|
case "success": {
|
||||||
const page = getModlogQueryParams().page;
|
const page = this.props.page;
|
||||||
return (
|
return (
|
||||||
<div className="table-responsive">
|
<div className="table-responsive">
|
||||||
<table id="modlog_table" className="table table-sm table-hover">
|
<table id="modlog_table" className="table table-sm table-hover">
|
||||||
|
@ -909,15 +916,15 @@ export class Modlog extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUserChange(option: Choice) {
|
handleUserChange(option: Choice) {
|
||||||
this.updateUrl({ userId: getIdFromString(option.value) ?? null, page: 1 });
|
this.updateUrl({ userId: getIdFromString(option.value), page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModChange(option: Choice) {
|
handleModChange(option: Choice) {
|
||||||
this.updateUrl({ modId: getIdFromString(option.value) ?? null, page: 1 });
|
this.updateUrl({ modId: getIdFromString(option.value), page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchUsers = debounce(async (text: string) => {
|
handleSearchUsers = debounce(async (text: string) => {
|
||||||
const { userId } = getModlogQueryParams();
|
const { userId } = this.props;
|
||||||
const { userSearchOptions } = this.state;
|
const { userSearchOptions } = this.state;
|
||||||
this.setState({ loadingUserSearch: true });
|
this.setState({ loadingUserSearch: true });
|
||||||
|
|
||||||
|
@ -934,7 +941,7 @@ export class Modlog extends Component<
|
||||||
});
|
});
|
||||||
|
|
||||||
handleSearchMods = debounce(async (text: string) => {
|
handleSearchMods = debounce(async (text: string) => {
|
||||||
const { modId } = getModlogQueryParams();
|
const { modId } = this.props;
|
||||||
const { modSearchOptions } = this.state;
|
const { modSearchOptions } = this.state;
|
||||||
this.setState({ loadingModSearch: true });
|
this.setState({ loadingModSearch: true });
|
||||||
|
|
||||||
|
@ -956,7 +963,7 @@ export class Modlog extends Component<
|
||||||
actionType: urlActionType,
|
actionType: urlActionType,
|
||||||
modId: urlModId,
|
modId: urlModId,
|
||||||
userId: urlUserId,
|
userId: urlUserId,
|
||||||
} = getModlogQueryParams();
|
} = this.props;
|
||||||
|
|
||||||
const queryParams: QueryParams<ModlogProps> = {
|
const queryParams: QueryParams<ModlogProps> = {
|
||||||
page: (page ?? urlPage).toString(),
|
page: (page ?? urlPage).toString(),
|
||||||
|
@ -977,7 +984,7 @@ export class Modlog extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
async refetch() {
|
async refetch() {
|
||||||
const { actionType, page, modId, userId } = getModlogQueryParams();
|
const { actionType, page, modId, userId } = this.props;
|
||||||
const { communityId: urlCommunityId } = this.props.match.params;
|
const { communityId: urlCommunityId } = this.props.match.params;
|
||||||
const communityId = getIdFromString(urlCommunityId);
|
const communityId = getIdFromString(urlCommunityId);
|
||||||
|
|
||||||
|
@ -988,10 +995,10 @@ export class Modlog extends Component<
|
||||||
page,
|
page,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
type_: actionType,
|
type_: actionType,
|
||||||
other_person_id: userId ?? undefined,
|
other_person_id: userId,
|
||||||
mod_person_id: !this.isoData.site_res.site_view.local_site
|
mod_person_id: !this.isoData.site_res.site_view.local_site
|
||||||
.hide_modlog_mod_names
|
.hide_modlog_mod_names
|
||||||
? modId ?? undefined
|
? modId
|
||||||
: undefined,
|
: undefined,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -1008,25 +1015,25 @@ export class Modlog extends Component<
|
||||||
|
|
||||||
static async fetchInitialData({
|
static async fetchInitialData({
|
||||||
headers,
|
headers,
|
||||||
path,
|
query: { page, userId, modId: modId_, actionType },
|
||||||
query: { modId: urlModId, page, userId: urlUserId, actionType },
|
match: {
|
||||||
|
params: { communityId: urlCommunityId },
|
||||||
|
},
|
||||||
site,
|
site,
|
||||||
}: InitialFetchRequest<QueryParams<ModlogProps>>): Promise<ModlogData> {
|
}: InitialFetchRequest<ModlogPathProps, ModlogProps>): Promise<ModlogData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
const pathSplit = path.split("/");
|
const communityId = getIdFromString(urlCommunityId);
|
||||||
const communityId = getIdFromString(pathSplit[2]);
|
|
||||||
const modId = !site.site_view.local_site.hide_modlog_mod_names
|
const modId = !site.site_view.local_site.hide_modlog_mod_names
|
||||||
? getIdFromString(urlModId)
|
? modId_
|
||||||
: undefined;
|
: undefined;
|
||||||
const userId = getIdFromString(urlUserId);
|
|
||||||
|
|
||||||
const modlogForm: GetModlog = {
|
const modlogForm: GetModlog = {
|
||||||
page: getPageFromString(page),
|
page,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
community_id: communityId,
|
community_id: communityId,
|
||||||
type_: getActionFromString(actionType),
|
type_: actionType,
|
||||||
mod_person_id: modId,
|
mod_person_id: modId,
|
||||||
other_person_id: userId,
|
other_person_id: userId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,6 +80,8 @@ import { Icon, Spinner } from "../common/icon";
|
||||||
import { Paginator } from "../common/paginator";
|
import { Paginator } from "../common/paginator";
|
||||||
import { PrivateMessage } from "../private_message/private-message";
|
import { PrivateMessage } from "../private_message/private-message";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
enum UnreadOrAll {
|
enum UnreadOrAll {
|
||||||
Unread,
|
Unread,
|
||||||
|
@ -126,7 +128,15 @@ interface InboxState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Inbox extends Component<any, InboxState> {
|
type InboxRouteProps = RouteComponentProps<Record<string, never>> &
|
||||||
|
Record<string, never>;
|
||||||
|
export type InboxFetchConfig = IRoutePropsWithFetch<
|
||||||
|
InboxData,
|
||||||
|
Record<string, never>,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
private isoData = setIsoData<InboxData>(this.context);
|
private isoData = setIsoData<InboxData>(this.context);
|
||||||
state: InboxState = {
|
state: InboxState = {
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
|
|
|
@ -94,6 +94,7 @@ import { CommunityLink } from "../community/community-link";
|
||||||
import { PersonDetails } from "./person-details";
|
import { PersonDetails } from "./person-details";
|
||||||
import { PersonListing } from "./person-listing";
|
import { PersonListing } from "./person-listing";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
type ProfileData = RouteDataResponse<{
|
type ProfileData = RouteDataResponse<{
|
||||||
personResponse: GetPersonDetailsResponse;
|
personResponse: GetPersonDetailsResponse;
|
||||||
|
@ -117,12 +118,15 @@ interface ProfileProps {
|
||||||
page: number;
|
page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProfileQueryParams() {
|
export function getProfileQueryParams(source?: string): ProfileProps {
|
||||||
return getQueryParams<ProfileProps>({
|
return getQueryParams<ProfileProps>(
|
||||||
|
{
|
||||||
view: getViewFromProps,
|
view: getViewFromProps,
|
||||||
page: getPageFromString,
|
page: getPageFromString,
|
||||||
sort: getSortTypeFromQuery,
|
sort: getSortTypeFromQuery,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSortTypeFromQuery(sort?: string): SortType {
|
function getSortTypeFromQuery(sort?: string): SortType {
|
||||||
|
@ -171,10 +175,15 @@ function isPersonBlocked(personRes: RequestState<GetPersonDetailsResponse>) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Profile extends Component<
|
type ProfilePathProps = { username: string };
|
||||||
RouteComponentProps<{ username: string }>,
|
type ProfileRouteProps = RouteComponentProps<ProfilePathProps> & ProfileProps;
|
||||||
ProfileState
|
export type ProfileFetchConfig = IRoutePropsWithFetch<
|
||||||
> {
|
ProfileData,
|
||||||
|
ProfilePathProps,
|
||||||
|
ProfileProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Profile extends Component<ProfileRouteProps, ProfileState> {
|
||||||
private isoData = setIsoData<ProfileData>(this.context);
|
private isoData = setIsoData<ProfileData>(this.context);
|
||||||
state: ProfileState = {
|
state: ProfileState = {
|
||||||
personRes: EMPTY_REQUEST,
|
personRes: EMPTY_REQUEST,
|
||||||
|
@ -186,7 +195,7 @@ export class Profile extends Component<
|
||||||
isIsomorphic: false,
|
isIsomorphic: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: RouteComponentProps<{ username: string }>, context: any) {
|
constructor(props: ProfileRouteProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
|
@ -248,7 +257,7 @@ export class Profile extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchUserData() {
|
async fetchUserData() {
|
||||||
const { page, sort, view } = getProfileQueryParams();
|
const { page, sort, view } = this.props;
|
||||||
|
|
||||||
this.setState({ personRes: LOADING_REQUEST });
|
this.setState({ personRes: LOADING_REQUEST });
|
||||||
const personRes = await HttpService.client.getPersonDetails({
|
const personRes = await HttpService.client.getPersonDetails({
|
||||||
|
@ -278,22 +287,23 @@ export class Profile extends Component<
|
||||||
|
|
||||||
static async fetchInitialData({
|
static async fetchInitialData({
|
||||||
headers,
|
headers,
|
||||||
path,
|
query: { view, sort, page },
|
||||||
query: { page, sort, view: urlView },
|
match: {
|
||||||
}: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<ProfileData> {
|
params: { username },
|
||||||
|
},
|
||||||
|
}: InitialFetchRequest<
|
||||||
|
ProfilePathProps,
|
||||||
|
ProfileProps
|
||||||
|
>): Promise<ProfileData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
const pathSplit = path.split("/");
|
|
||||||
|
|
||||||
const username = pathSplit[2];
|
|
||||||
const view = getViewFromProps(urlView);
|
|
||||||
|
|
||||||
const form: GetPersonDetails = {
|
const form: GetPersonDetails = {
|
||||||
username: username,
|
username: username,
|
||||||
sort: getSortTypeFromQuery(sort),
|
sort,
|
||||||
saved_only: view === PersonDetailsView.Saved,
|
saved_only: view === PersonDetailsView.Saved,
|
||||||
page: getPageFromString(page),
|
page,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -321,7 +331,7 @@ export class Profile extends Component<
|
||||||
case "success": {
|
case "success": {
|
||||||
const siteRes = this.state.siteRes;
|
const siteRes = this.state.siteRes;
|
||||||
const personRes = this.state.personRes.data;
|
const personRes = this.state.personRes.data;
|
||||||
const { page, sort, view } = getProfileQueryParams();
|
const { page, sort, view } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
|
@ -415,7 +425,7 @@ export class Profile extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
getRadio(view: PersonDetailsView) {
|
getRadio(view: PersonDetailsView) {
|
||||||
const { view: urlView } = getProfileQueryParams();
|
const { view: urlView } = this.props;
|
||||||
const active = view === urlView;
|
const active = view === urlView;
|
||||||
const radioId = randomStr();
|
const radioId = randomStr();
|
||||||
|
|
||||||
|
@ -442,10 +452,10 @@ export class Profile extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
get selects() {
|
get selects() {
|
||||||
const { sort } = getProfileQueryParams();
|
const { sort } = this.props;
|
||||||
const { username } = this.props.match.params;
|
const { username } = this.props.match.params;
|
||||||
|
|
||||||
const profileRss = `/feeds/u/${username}.xml?sort=${sort}`;
|
const profileRss = `/feeds/u/${username}.xml${getQueryString({ sort })}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
|
@ -713,11 +723,7 @@ export class Profile extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateUrl({ page, sort, view }: Partial<ProfileProps>) {
|
async updateUrl({ page, sort, view }: Partial<ProfileProps>) {
|
||||||
const {
|
const { page: urlPage, sort: urlSort, view: urlView } = this.props;
|
||||||
page: urlPage,
|
|
||||||
sort: urlSort,
|
|
||||||
view: urlView,
|
|
||||||
} = getProfileQueryParams();
|
|
||||||
|
|
||||||
const queryParams: QueryParams<ProfileProps> = {
|
const queryParams: QueryParams<ProfileProps> = {
|
||||||
page: (page ?? urlPage).toString(),
|
page: (page ?? urlPage).toString(),
|
||||||
|
|
|
@ -27,6 +27,8 @@ import { Paginator } from "../common/paginator";
|
||||||
import { RegistrationApplication } from "../common/registration-application";
|
import { RegistrationApplication } from "../common/registration-application";
|
||||||
import { UnreadCounterService } from "../../services";
|
import { UnreadCounterService } from "../../services";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
enum RegistrationState {
|
enum RegistrationState {
|
||||||
Unread,
|
Unread,
|
||||||
|
@ -46,8 +48,18 @@ interface RegistrationApplicationsState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegistrationApplicationsRouteProps = RouteComponentProps<
|
||||||
|
Record<string, never>
|
||||||
|
> &
|
||||||
|
Record<string, never>;
|
||||||
|
export type RegistrationApplicationsFetchConfig = IRoutePropsWithFetch<
|
||||||
|
RegistrationApplicationsData,
|
||||||
|
Record<string, never>,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
export class RegistrationApplications extends Component<
|
export class RegistrationApplications extends Component<
|
||||||
any,
|
RegistrationApplicationsRouteProps,
|
||||||
RegistrationApplicationsState
|
RegistrationApplicationsState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData<RegistrationApplicationsData>(this.context);
|
private isoData = setIsoData<RegistrationApplicationsData>(this.context);
|
||||||
|
|
|
@ -50,6 +50,8 @@ import { PostReport } from "../post/post-report";
|
||||||
import { PrivateMessageReport } from "../private_message/private-message-report";
|
import { PrivateMessageReport } from "../private_message/private-message-report";
|
||||||
import { UnreadCounterService } from "../../services";
|
import { UnreadCounterService } from "../../services";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
enum UnreadOrAll {
|
enum UnreadOrAll {
|
||||||
Unread,
|
Unread,
|
||||||
|
@ -93,7 +95,15 @@ interface ReportsState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Reports extends Component<any, ReportsState> {
|
type ReportsRouteProps = RouteComponentProps<Record<string, never>> &
|
||||||
|
Record<string, never>;
|
||||||
|
export type ReportsFetchConfig = IRoutePropsWithFetch<
|
||||||
|
ReportsData,
|
||||||
|
Record<string, never>,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Reports extends Component<ReportsRouteProps, ReportsState> {
|
||||||
private isoData = setIsoData<ReportsData>(this.context);
|
private isoData = setIsoData<ReportsData>(this.context);
|
||||||
state: ReportsState = {
|
state: ReportsState = {
|
||||||
commentReportsRes: EMPTY_REQUEST,
|
commentReportsRes: EMPTY_REQUEST,
|
||||||
|
|
|
@ -68,6 +68,8 @@ import TotpModal from "../common/totp-modal";
|
||||||
import { LoadingEllipses } from "../common/loading-ellipses";
|
import { LoadingEllipses } from "../common/loading-ellipses";
|
||||||
import { refreshTheme, setThemeOverride } from "../../utils/browser";
|
import { refreshTheme, setThemeOverride } from "../../utils/browser";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
|
||||||
type SettingsData = RouteDataResponse<{
|
type SettingsData = RouteDataResponse<{
|
||||||
instancesRes: GetFederatedInstancesResponse;
|
instancesRes: GetFederatedInstancesResponse;
|
||||||
|
@ -193,7 +195,15 @@ function handleClose2faModal(i: Settings) {
|
||||||
i.setState({ show2faModal: false });
|
i.setState({ show2faModal: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Settings extends Component<any, SettingsState> {
|
type SettingsRouteProps = RouteComponentProps<Record<string, never>> &
|
||||||
|
Record<string, never>;
|
||||||
|
export type SettingsFetchConfig = IRoutePropsWithFetch<
|
||||||
|
SettingsData,
|
||||||
|
Record<string, never>,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
private isoData = setIsoData<SettingsData>(this.context);
|
private isoData = setIsoData<SettingsData>(this.context);
|
||||||
exportSettingsLink = createRef<HTMLAnchorElement>();
|
exportSettingsLink = createRef<HTMLAnchorElement>();
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
setIsoData,
|
setIsoData,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import { getIdFromString, getQueryParams } from "@utils/helpers";
|
import { getIdFromString, getQueryParams } from "@utils/helpers";
|
||||||
import type { QueryParams } from "@utils/types";
|
|
||||||
import { Choice, RouteDataResponse } from "@utils/types";
|
import { Choice, RouteDataResponse } from "@utils/types";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { RouteComponentProps } from "inferno-router/dist/Route";
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
@ -30,6 +29,7 @@ import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import { PostForm } from "./post-form";
|
import { PostForm } from "./post-form";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
export interface CreatePostProps {
|
export interface CreatePostProps {
|
||||||
communityId?: number;
|
communityId?: number;
|
||||||
|
@ -40,10 +40,13 @@ type CreatePostData = RouteDataResponse<{
|
||||||
initialCommunitiesRes: ListCommunitiesResponse;
|
initialCommunitiesRes: ListCommunitiesResponse;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
function getCreatePostQueryParams() {
|
export function getCreatePostQueryParams(source?: string): CreatePostProps {
|
||||||
return getQueryParams<CreatePostProps>({
|
return getQueryParams<CreatePostProps>(
|
||||||
|
{
|
||||||
communityId: getIdFromString,
|
communityId: getIdFromString,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchCommunitiesForOptions(client: WrappedLemmyHttp) {
|
function fetchCommunitiesForOptions(client: WrappedLemmyHttp) {
|
||||||
|
@ -58,8 +61,17 @@ interface CreatePostState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreatePostPathProps = Record<string, never>;
|
||||||
|
type CreatePostRouteProps = RouteComponentProps<CreatePostPathProps> &
|
||||||
|
CreatePostProps;
|
||||||
|
export type CreatePostFetchConfig = IRoutePropsWithFetch<
|
||||||
|
CreatePostData,
|
||||||
|
CreatePostPathProps,
|
||||||
|
CreatePostProps
|
||||||
|
>;
|
||||||
|
|
||||||
export class CreatePost extends Component<
|
export class CreatePost extends Component<
|
||||||
RouteComponentProps<Record<string, never>>,
|
CreatePostRouteProps,
|
||||||
CreatePostState
|
CreatePostState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData<CreatePostData>(this.context);
|
private isoData = setIsoData<CreatePostData>(this.context);
|
||||||
|
@ -70,7 +82,7 @@ export class CreatePost extends Component<
|
||||||
isIsomorphic: false,
|
isIsomorphic: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: RouteComponentProps<Record<string, never>>, context: any) {
|
constructor(props: CreatePostRouteProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.handlePostCreate = this.handlePostCreate.bind(this);
|
this.handlePostCreate = this.handlePostCreate.bind(this);
|
||||||
|
@ -102,9 +114,7 @@ export class CreatePost extends Component<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchCommunity() {
|
async fetchCommunity({ communityId }: CreatePostProps) {
|
||||||
const { communityId } = getCreatePostQueryParams();
|
|
||||||
|
|
||||||
if (communityId) {
|
if (communityId) {
|
||||||
const res = await HttpService.client.getCommunity({
|
const res = await HttpService.client.getCommunity({
|
||||||
id: communityId,
|
id: communityId,
|
||||||
|
@ -121,7 +131,7 @@ export class CreatePost extends Component<
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
// TODO test this
|
// TODO test this
|
||||||
if (!this.state.isIsomorphic) {
|
if (!this.state.isIsomorphic) {
|
||||||
const { communityId } = getCreatePostQueryParams();
|
const { communityId } = this.props;
|
||||||
|
|
||||||
const initialCommunitiesRes = await fetchCommunitiesForOptions(
|
const initialCommunitiesRes = await fetchCommunitiesForOptions(
|
||||||
HttpService.client,
|
HttpService.client,
|
||||||
|
@ -134,7 +144,7 @@ export class CreatePost extends Component<
|
||||||
if (
|
if (
|
||||||
communityId?.toString() !== this.state.selectedCommunityChoice?.value
|
communityId?.toString() !== this.state.selectedCommunityChoice?.value
|
||||||
) {
|
) {
|
||||||
await this.fetchCommunity();
|
await this.fetchCommunity({ communityId });
|
||||||
} else if (!communityId) {
|
} else if (!communityId) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedCommunityChoice: undefined,
|
selectedCommunityChoice: undefined,
|
||||||
|
@ -199,15 +209,13 @@ export class CreatePost extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateUrl({ communityId }: Partial<CreatePostProps>) {
|
async updateUrl({ communityId }: Partial<CreatePostProps>) {
|
||||||
const { communityId: urlCommunityId } = getCreatePostQueryParams();
|
|
||||||
|
|
||||||
const locationState = this.props.history.location.state as
|
const locationState = this.props.history.location.state as
|
||||||
| PostFormParams
|
| PostFormParams
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
const url = new URL(location.href);
|
const url = new URL(location.href);
|
||||||
|
|
||||||
const newId = (communityId ?? urlCommunityId)?.toString();
|
const newId = communityId?.toString();
|
||||||
|
|
||||||
if (newId !== undefined) {
|
if (newId !== undefined) {
|
||||||
url.searchParams.set("communityId", newId);
|
url.searchParams.set("communityId", newId);
|
||||||
|
@ -215,9 +223,10 @@ export class CreatePost extends Component<
|
||||||
url.searchParams.delete("communityId");
|
url.searchParams.delete("communityId");
|
||||||
}
|
}
|
||||||
|
|
||||||
history.replaceState(locationState, "", url);
|
// This bypasses the router and doesn't update the query props.
|
||||||
|
window.history.replaceState(locationState, "", url);
|
||||||
|
|
||||||
await this.fetchCommunity();
|
await this.fetchCommunity({ communityId });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectedCommunityChange(choice: Choice) {
|
handleSelectedCommunityChange(choice: Choice) {
|
||||||
|
@ -243,7 +252,8 @@ export class CreatePost extends Component<
|
||||||
headers,
|
headers,
|
||||||
query: { communityId },
|
query: { communityId },
|
||||||
}: InitialFetchRequest<
|
}: InitialFetchRequest<
|
||||||
QueryParams<CreatePostProps>
|
CreatePostPathProps,
|
||||||
|
CreatePostProps
|
||||||
>): Promise<CreatePostData> {
|
>): Promise<CreatePostData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
|
@ -255,7 +265,7 @@ export class CreatePost extends Component<
|
||||||
|
|
||||||
if (communityId) {
|
if (communityId) {
|
||||||
const form: GetCommunity = {
|
const form: GetCommunity = {
|
||||||
id: getIdFromString(communityId),
|
id: communityId,
|
||||||
};
|
};
|
||||||
|
|
||||||
data.communityResponse = await client.getCommunity(form);
|
data.communityResponse = await client.getCommunity(form);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
debounce,
|
debounce,
|
||||||
getIdFromString,
|
getIdFromString,
|
||||||
|
getQueryString,
|
||||||
validTitle,
|
validTitle,
|
||||||
validURL,
|
validURL,
|
||||||
} from "@utils/helpers";
|
} from "@utils/helpers";
|
||||||
|
@ -380,18 +381,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
archive.org {I18NextService.i18n.t("archive_link")}
|
archive.org {I18NextService.i18n.t("archive_link")}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
|
href={`${ghostArchiveUrl}/search${getQueryString({ term: url })}`}
|
||||||
url,
|
|
||||||
)}`}
|
|
||||||
className="me-2 d-inline-block float-right text-muted small fw-bold"
|
className="me-2 d-inline-block float-right text-muted small fw-bold"
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
>
|
>
|
||||||
ghostarchive.org {I18NextService.i18n.t("archive_link")}
|
ghostarchive.org {I18NextService.i18n.t("archive_link")}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
|
href={`${archiveTodayUrl}/${getQueryString({ run: "1", url })}`}
|
||||||
url,
|
|
||||||
)}`}
|
|
||||||
className="me-2 d-inline-block float-right text-muted small fw-bold"
|
className="me-2 d-inline-block float-right text-muted small fw-bold"
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
>
|
>
|
||||||
|
|
|
@ -98,6 +98,8 @@ import { Icon, Spinner } from "../common/icon";
|
||||||
import { Sidebar } from "../community/sidebar";
|
import { Sidebar } from "../community/sidebar";
|
||||||
import { PostListing } from "./post-listing";
|
import { PostListing } from "./post-listing";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
const commentsShownInterval = 15;
|
const commentsShownInterval = 15;
|
||||||
|
|
||||||
|
@ -122,7 +124,18 @@ interface PostState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Post extends Component<any, PostState> {
|
type PostPathProps =
|
||||||
|
| { post_id: string; comment_id: never }
|
||||||
|
| { post_id: never; comment_id: string };
|
||||||
|
type PostRouteProps = RouteComponentProps<PostPathProps> &
|
||||||
|
Record<string, never>;
|
||||||
|
export type PostFetchConfig = IRoutePropsWithFetch<
|
||||||
|
PostData,
|
||||||
|
PostPathProps,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Post extends Component<PostRouteProps, PostState> {
|
||||||
private isoData = setIsoData<PostData>(this.context);
|
private isoData = setIsoData<PostData>(this.context);
|
||||||
private commentScrollDebounced: () => void;
|
private commentScrollDebounced: () => void;
|
||||||
state: PostState = {
|
state: PostState = {
|
||||||
|
@ -235,15 +248,13 @@ export class Post extends Component<any, PostState> {
|
||||||
|
|
||||||
static async fetchInitialData({
|
static async fetchInitialData({
|
||||||
headers,
|
headers,
|
||||||
path,
|
match,
|
||||||
}: InitialFetchRequest): Promise<PostData> {
|
}: InitialFetchRequest<PostPathProps>): Promise<PostData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
const pathSplit = path.split("/");
|
const postId = getIdFromProps({ match });
|
||||||
|
const commentId = getCommentIdFromProps({ match });
|
||||||
const pathType = pathSplit.at(1);
|
|
||||||
const id = pathSplit.at(2) ? Number(pathSplit.at(2)) : undefined;
|
|
||||||
|
|
||||||
const postForm: GetPost = {};
|
const postForm: GetPost = {};
|
||||||
|
|
||||||
|
@ -254,14 +265,11 @@ export class Post extends Component<any, PostState> {
|
||||||
saved_only: false,
|
saved_only: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set the correct id based on the path type
|
postForm.id = postId;
|
||||||
if (pathType === "post") {
|
postForm.comment_id = commentId;
|
||||||
postForm.id = id;
|
|
||||||
commentsForm.post_id = id;
|
commentsForm.post_id = postId;
|
||||||
} else {
|
commentsForm.parent_id = commentId;
|
||||||
postForm.comment_id = id;
|
|
||||||
commentsForm.parent_id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [postRes, commentsRes] = await Promise.all([
|
const [postRes, commentsRes] = await Promise.all([
|
||||||
client.getPost(postForm),
|
client.getPost(postForm),
|
||||||
|
|
|
@ -22,6 +22,8 @@ import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import { PrivateMessageForm } from "./private-message-form";
|
import { PrivateMessageForm } from "./private-message-form";
|
||||||
import { getHttpBaseInternal } from "../../utils/env";
|
import { getHttpBaseInternal } from "../../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../../routes";
|
||||||
|
|
||||||
type CreatePrivateMessageData = RouteDataResponse<{
|
type CreatePrivateMessageData = RouteDataResponse<{
|
||||||
recipientDetailsResponse: GetPersonDetailsResponse;
|
recipientDetailsResponse: GetPersonDetailsResponse;
|
||||||
|
@ -34,8 +36,17 @@ interface CreatePrivateMessageState {
|
||||||
isIsomorphic: boolean;
|
isIsomorphic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreatePrivateMessagePathProps = { recipient_id: string };
|
||||||
|
type CreatePrivateMessageRouteProps =
|
||||||
|
RouteComponentProps<CreatePrivateMessagePathProps> & Record<string, never>;
|
||||||
|
export type CreatePrivateMessageFetchConfig = IRoutePropsWithFetch<
|
||||||
|
CreatePrivateMessageData,
|
||||||
|
CreatePrivateMessagePathProps,
|
||||||
|
Record<string, never>
|
||||||
|
>;
|
||||||
|
|
||||||
export class CreatePrivateMessage extends Component<
|
export class CreatePrivateMessage extends Component<
|
||||||
any,
|
CreatePrivateMessageRouteProps,
|
||||||
CreatePrivateMessageState
|
CreatePrivateMessageState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData<CreatePrivateMessageData>(this.context);
|
private isoData = setIsoData<CreatePrivateMessageData>(this.context);
|
||||||
|
@ -69,12 +80,12 @@ export class CreatePrivateMessage extends Component<
|
||||||
|
|
||||||
static async fetchInitialData({
|
static async fetchInitialData({
|
||||||
headers,
|
headers,
|
||||||
path,
|
match,
|
||||||
}: InitialFetchRequest): Promise<CreatePrivateMessageData> {
|
}: InitialFetchRequest<CreatePrivateMessagePathProps>): Promise<CreatePrivateMessageData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
const person_id = Number(path.split("/").pop());
|
const person_id = getRecipientIdFromProps({ match });
|
||||||
|
|
||||||
const form: GetPersonDetails = {
|
const form: GetPersonDetails = {
|
||||||
person_id,
|
person_id,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { setIsoData } from "@utils/app";
|
import { setIsoData } from "@utils/app";
|
||||||
import { getQueryParams } from "@utils/helpers";
|
import { getQueryParams } from "@utils/helpers";
|
||||||
import { QueryParams, RouteDataResponse } from "@utils/types";
|
import { RouteDataResponse } from "@utils/types";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommunityView,
|
CommunityView,
|
||||||
|
@ -22,6 +22,8 @@ import { PictrsImage } from "./common/pictrs-image";
|
||||||
import { SubscribeButton } from "./common/subscribe-button";
|
import { SubscribeButton } from "./common/subscribe-button";
|
||||||
import { CommunityLink } from "./community/community-link";
|
import { CommunityLink } from "./community/community-link";
|
||||||
import { getHttpBaseInternal } from "../utils/env";
|
import { getHttpBaseInternal } from "../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../routes";
|
||||||
|
|
||||||
interface RemoteFetchProps {
|
interface RemoteFetchProps {
|
||||||
uri?: string;
|
uri?: string;
|
||||||
|
@ -37,16 +39,19 @@ interface RemoteFetchState {
|
||||||
followCommunityLoading: boolean;
|
followCommunityLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUriFromQuery = (uri?: string): string | undefined =>
|
const getUriFromQuery = (uri?: string): string | undefined => uri;
|
||||||
uri ? decodeURIComponent(uri) : undefined;
|
|
||||||
|
|
||||||
const getRemoteFetchQueryParams = () =>
|
export function getRemoteFetchQueryParams(source?: string): RemoteFetchProps {
|
||||||
getQueryParams<RemoteFetchProps>({
|
return getQueryParams<RemoteFetchProps>(
|
||||||
|
{
|
||||||
uri: getUriFromQuery,
|
uri: getUriFromQuery,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function uriToQuery(uri: string) {
|
function uriToQuery(uri: string) {
|
||||||
const match = decodeURIComponent(uri).match(/https?:\/\/(.+)\/c\/(.+)/);
|
const match = uri.match(/https?:\/\/(.+)\/c\/(.+)/);
|
||||||
|
|
||||||
return match ? `!${match[2]}@${match[1]}` : "";
|
return match ? `!${match[2]}@${match[1]}` : "";
|
||||||
}
|
}
|
||||||
|
@ -83,7 +88,19 @@ async function handleToggleFollow(i: RemoteFetch, follow: boolean) {
|
||||||
const handleFollow = (i: RemoteFetch) => handleToggleFollow(i, true);
|
const handleFollow = (i: RemoteFetch) => handleToggleFollow(i, true);
|
||||||
const handleUnfollow = (i: RemoteFetch) => handleToggleFollow(i, false);
|
const handleUnfollow = (i: RemoteFetch) => handleToggleFollow(i, false);
|
||||||
|
|
||||||
export class RemoteFetch extends Component<any, RemoteFetchState> {
|
type RemoteFetchPathProps = Record<string, never>;
|
||||||
|
type RemoteFetchRouteProps = RouteComponentProps<RemoteFetchPathProps> &
|
||||||
|
RemoteFetchProps;
|
||||||
|
export type RemoteFetchFetchConfig = IRoutePropsWithFetch<
|
||||||
|
RemoteFetchData,
|
||||||
|
RemoteFetchPathProps,
|
||||||
|
RemoteFetchProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class RemoteFetch extends Component<
|
||||||
|
RemoteFetchRouteProps,
|
||||||
|
RemoteFetchState
|
||||||
|
> {
|
||||||
private isoData = setIsoData<RemoteFetchData>(this.context);
|
private isoData = setIsoData<RemoteFetchData>(this.context);
|
||||||
state: RemoteFetchState = {
|
state: RemoteFetchState = {
|
||||||
resolveObjectRes: EMPTY_REQUEST,
|
resolveObjectRes: EMPTY_REQUEST,
|
||||||
|
@ -91,7 +108,7 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
|
||||||
followCommunityLoading: false,
|
followCommunityLoading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: RemoteFetchRouteProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
|
@ -107,7 +124,7 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
if (!this.state.isIsomorphic) {
|
if (!this.state.isIsomorphic) {
|
||||||
const { uri } = getRemoteFetchQueryParams();
|
const { uri } = this.props;
|
||||||
|
|
||||||
if (uri) {
|
if (uri) {
|
||||||
this.setState({ resolveObjectRes: LOADING_REQUEST });
|
this.setState({ resolveObjectRes: LOADING_REQUEST });
|
||||||
|
@ -139,7 +156,7 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
|
||||||
get content() {
|
get content() {
|
||||||
const res = this.state.resolveObjectRes;
|
const res = this.state.resolveObjectRes;
|
||||||
|
|
||||||
const { uri } = getRemoteFetchQueryParams();
|
const { uri } = this.props;
|
||||||
const remoteCommunityName = uri ? uriToQuery(uri) : "remote community";
|
const remoteCommunityName = uri ? uriToQuery(uri) : "remote community";
|
||||||
|
|
||||||
switch (res.state) {
|
switch (res.state) {
|
||||||
|
@ -204,7 +221,7 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
const { uri } = getRemoteFetchQueryParams();
|
const { uri } = this.props;
|
||||||
const name = this.isoData.site_res.site_view.site.name;
|
const name = this.isoData.site_res.site_view.site.name;
|
||||||
return `${I18NextService.i18n.t("remote_follow")} - ${
|
return `${I18NextService.i18n.t("remote_follow")} - ${
|
||||||
uri ? `${uri} - ` : ""
|
uri ? `${uri} - ` : ""
|
||||||
|
@ -215,7 +232,8 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
|
||||||
headers,
|
headers,
|
||||||
query: { uri },
|
query: { uri },
|
||||||
}: InitialFetchRequest<
|
}: InitialFetchRequest<
|
||||||
QueryParams<RemoteFetchProps>
|
RemoteFetchPathProps,
|
||||||
|
RemoteFetchProps
|
||||||
>): Promise<RemoteFetchData> {
|
>): Promise<RemoteFetchData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
|
|
|
@ -67,14 +67,16 @@ import { CommunityLink } from "./community/community-link";
|
||||||
import { PersonListing } from "./person/person-listing";
|
import { PersonListing } from "./person/person-listing";
|
||||||
import { PostListing } from "./post/post-listing";
|
import { PostListing } from "./post/post-listing";
|
||||||
import { getHttpBaseInternal } from "../utils/env";
|
import { getHttpBaseInternal } from "../utils/env";
|
||||||
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
import { IRoutePropsWithFetch } from "../routes";
|
||||||
|
|
||||||
interface SearchProps {
|
interface SearchProps {
|
||||||
q?: string;
|
q?: string;
|
||||||
type: SearchType;
|
type: SearchType;
|
||||||
sort: SortType;
|
sort: SortType;
|
||||||
listingType: ListingType;
|
listingType: ListingType;
|
||||||
communityId?: number | null;
|
communityId?: number;
|
||||||
creatorId?: number | null;
|
creatorId?: number;
|
||||||
page: number;
|
page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +114,9 @@ const defaultListingType = "All";
|
||||||
|
|
||||||
const searchTypes = ["All", "Comments", "Posts", "Communities", "Users", "Url"];
|
const searchTypes = ["All", "Comments", "Posts", "Communities", "Users", "Url"];
|
||||||
|
|
||||||
const getSearchQueryParams = () =>
|
export function getSearchQueryParams(source?: string): SearchProps {
|
||||||
getQueryParams<SearchProps>({
|
return getQueryParams<SearchProps>(
|
||||||
|
{
|
||||||
q: getSearchQueryFromQuery,
|
q: getSearchQueryFromQuery,
|
||||||
type: getSearchTypeFromQuery,
|
type: getSearchTypeFromQuery,
|
||||||
sort: getSortTypeFromQuery,
|
sort: getSortTypeFromQuery,
|
||||||
|
@ -121,10 +124,12 @@ const getSearchQueryParams = () =>
|
||||||
communityId: getIdFromString,
|
communityId: getIdFromString,
|
||||||
creatorId: getIdFromString,
|
creatorId: getIdFromString,
|
||||||
page: getPageFromString,
|
page: getPageFromString,
|
||||||
});
|
},
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const getSearchQueryFromQuery = (q?: string): string | undefined =>
|
const getSearchQueryFromQuery = (q?: string): string | undefined => q;
|
||||||
q ? decodeURIComponent(q) : undefined;
|
|
||||||
|
|
||||||
function getSearchTypeFromQuery(type_?: string): SearchType {
|
function getSearchTypeFromQuery(type_?: string): SearchType {
|
||||||
return type_ ? (type_ as SearchType) : defaultSearchType;
|
return type_ ? (type_ as SearchType) : defaultSearchType;
|
||||||
|
@ -240,7 +245,15 @@ function getListing(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Search extends Component<any, SearchState> {
|
type SearchPathProps = Record<string, never>;
|
||||||
|
type SearchRouteProps = RouteComponentProps<SearchPathProps> & SearchProps;
|
||||||
|
export type SearchFetchConfig = IRoutePropsWithFetch<
|
||||||
|
SearchData,
|
||||||
|
SearchPathProps,
|
||||||
|
SearchProps
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class Search extends Component<SearchRouteProps, SearchState> {
|
||||||
private isoData = setIsoData<SearchData>(this.context);
|
private isoData = setIsoData<SearchData>(this.context);
|
||||||
searchInput = createRef<HTMLInputElement>();
|
searchInput = createRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
@ -255,7 +268,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
isIsomorphic: false,
|
isIsomorphic: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: SearchRouteProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
|
@ -265,7 +278,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
this.handleCommunityFilterChange.bind(this);
|
this.handleCommunityFilterChange.bind(this);
|
||||||
this.handleCreatorFilterChange = this.handleCreatorFilterChange.bind(this);
|
this.handleCreatorFilterChange = this.handleCreatorFilterChange.bind(this);
|
||||||
|
|
||||||
const { q } = getSearchQueryParams();
|
const { q } = this.props;
|
||||||
|
|
||||||
this.state.searchText = q;
|
this.state.searchText = q;
|
||||||
|
|
||||||
|
@ -335,7 +348,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
const { communityId, creatorId } = getSearchQueryParams();
|
const { communityId, creatorId } = this.props;
|
||||||
|
|
||||||
if (communityId) {
|
if (communityId) {
|
||||||
promises.push(
|
promises.push(
|
||||||
|
@ -390,12 +403,19 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
static async fetchInitialData({
|
static async fetchInitialData({
|
||||||
headers,
|
headers,
|
||||||
query: { communityId, creatorId, q, type, sort, listingType, page },
|
query: {
|
||||||
}: InitialFetchRequest<QueryParams<SearchProps>>): Promise<SearchData> {
|
q: query,
|
||||||
|
type: searchType,
|
||||||
|
sort,
|
||||||
|
listingType: listing_type,
|
||||||
|
communityId: community_id,
|
||||||
|
creatorId: creator_id,
|
||||||
|
page,
|
||||||
|
},
|
||||||
|
}: InitialFetchRequest<SearchPathProps, SearchProps>): Promise<SearchData> {
|
||||||
const client = wrapClient(
|
const client = wrapClient(
|
||||||
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
new LemmyHttp(getHttpBaseInternal(), { headers }),
|
||||||
);
|
);
|
||||||
const community_id = getIdFromString(communityId);
|
|
||||||
let communityResponse: RequestState<GetCommunityResponse> = EMPTY_REQUEST;
|
let communityResponse: RequestState<GetCommunityResponse> = EMPTY_REQUEST;
|
||||||
if (community_id) {
|
if (community_id) {
|
||||||
const getCommunityForm: GetCommunity = {
|
const getCommunityForm: GetCommunity = {
|
||||||
|
@ -411,7 +431,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
});
|
});
|
||||||
|
|
||||||
const creator_id = getIdFromString(creatorId);
|
|
||||||
let creatorDetailsResponse: RequestState<GetPersonDetailsResponse> =
|
let creatorDetailsResponse: RequestState<GetPersonDetailsResponse> =
|
||||||
EMPTY_REQUEST;
|
EMPTY_REQUEST;
|
||||||
if (creator_id) {
|
if (creator_id) {
|
||||||
|
@ -422,8 +441,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
creatorDetailsResponse = await client.getPersonDetails(getCreatorForm);
|
creatorDetailsResponse = await client.getPersonDetails(getCreatorForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = getSearchQueryFromQuery(q);
|
|
||||||
|
|
||||||
let searchResponse: RequestState<SearchResponse> = EMPTY_REQUEST;
|
let searchResponse: RequestState<SearchResponse> = EMPTY_REQUEST;
|
||||||
let resolveObjectResponse: RequestState<ResolveObjectResponse> =
|
let resolveObjectResponse: RequestState<ResolveObjectResponse> =
|
||||||
EMPTY_REQUEST;
|
EMPTY_REQUEST;
|
||||||
|
@ -433,10 +450,10 @@ export class Search extends Component<any, SearchState> {
|
||||||
q: query,
|
q: query,
|
||||||
community_id,
|
community_id,
|
||||||
creator_id,
|
creator_id,
|
||||||
type_: getSearchTypeFromQuery(type),
|
type_: searchType,
|
||||||
sort: getSortTypeFromQuery(sort),
|
sort,
|
||||||
listing_type: getListingTypeFromQuery(listingType),
|
listing_type,
|
||||||
page: getPageFromString(page),
|
page,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -466,13 +483,13 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
const { q } = getSearchQueryParams();
|
const { q } = this.props;
|
||||||
const name = this.state.siteRes.site_view.site.name;
|
const name = this.state.siteRes.site_view.site.name;
|
||||||
return `${I18NextService.i18n.t("search")} - ${q ? `${q} - ` : ""}${name}`;
|
return `${I18NextService.i18n.t("search")} - ${q ? `${q} - ` : ""}${name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { type, page } = getSearchQueryParams();
|
const { type, page } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="search container-lg">
|
<div className="search container-lg">
|
||||||
|
@ -555,8 +572,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get selects() {
|
get selects() {
|
||||||
const { type, listingType, sort, communityId, creatorId } =
|
const { type, listingType, sort, communityId, creatorId } = this.props;
|
||||||
getSearchQueryParams();
|
|
||||||
const {
|
const {
|
||||||
communitySearchOptions,
|
communitySearchOptions,
|
||||||
creatorSearchOptions,
|
creatorSearchOptions,
|
||||||
|
@ -664,7 +680,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { sort } = getSearchQueryParams();
|
const { sort } = this.props;
|
||||||
|
|
||||||
// Sort it
|
// Sort it
|
||||||
if (sort === "New") {
|
if (sort === "New") {
|
||||||
|
@ -959,7 +975,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
async search() {
|
async search() {
|
||||||
const { searchText: q } = this.state;
|
const { searchText: q } = this.state;
|
||||||
const { communityId, creatorId, type, sort, listingType, page } =
|
const { communityId, creatorId, type, sort, listingType, page } =
|
||||||
getSearchQueryParams();
|
this.props;
|
||||||
|
|
||||||
if (q) {
|
if (q) {
|
||||||
this.setState({ searchRes: LOADING_REQUEST });
|
this.setState({ searchRes: LOADING_REQUEST });
|
||||||
|
@ -991,7 +1007,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
handleCreatorSearch = debounce(async (text: string) => {
|
handleCreatorSearch = debounce(async (text: string) => {
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
const { creatorId } = getSearchQueryParams();
|
const { creatorId } = this.props;
|
||||||
const { creatorSearchOptions } = this.state;
|
const { creatorSearchOptions } = this.state;
|
||||||
|
|
||||||
this.setState({ searchCreatorLoading: true });
|
this.setState({ searchCreatorLoading: true });
|
||||||
|
@ -1009,7 +1025,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
handleCommunitySearch = debounce(async (text: string) => {
|
handleCommunitySearch = debounce(async (text: string) => {
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
const { communityId } = getSearchQueryParams();
|
const { communityId } = this.props;
|
||||||
const { communitySearchOptions } = this.state;
|
const { communitySearchOptions } = this.state;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -1053,14 +1069,14 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
handleCommunityFilterChange({ value }: Choice) {
|
handleCommunityFilterChange({ value }: Choice) {
|
||||||
this.updateUrl({
|
this.updateUrl({
|
||||||
communityId: getIdFromString(value) ?? null,
|
communityId: getIdFromString(value),
|
||||||
page: 1,
|
page: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCreatorFilterChange({ value }: Choice) {
|
handleCreatorFilterChange({ value }: Choice) {
|
||||||
this.updateUrl({
|
this.updateUrl({
|
||||||
creatorId: getIdFromString(value) ?? null,
|
creatorId: getIdFromString(value),
|
||||||
page: 1,
|
page: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1095,13 +1111,9 @@ export class Search extends Component<any, SearchState> {
|
||||||
sort: urlSort,
|
sort: urlSort,
|
||||||
creatorId: urlCreatorId,
|
creatorId: urlCreatorId,
|
||||||
page: urlPage,
|
page: urlPage,
|
||||||
} = getSearchQueryParams();
|
} = this.props;
|
||||||
|
|
||||||
let query = q ?? this.state.searchText ?? urlQ;
|
const query = q ?? this.state.searchText ?? urlQ;
|
||||||
|
|
||||||
if (query && query.length > 0) {
|
|
||||||
query = encodeURIComponent(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryParams: QueryParams<SearchProps> = {
|
const queryParams: QueryParams<SearchProps> = {
|
||||||
q: query,
|
q: query,
|
||||||
|
|
|
@ -5,8 +5,8 @@ import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
PersonMention,
|
PersonMention,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import type { ParsedQs } from "qs";
|
|
||||||
import { RequestState } from "./services/HttpService";
|
import { RequestState } from "./services/HttpService";
|
||||||
|
import { Match } from "inferno-router/dist/Route";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This contains serialized data, it needs to be deserialized before use.
|
* This contains serialized data, it needs to be deserialized before use.
|
||||||
|
@ -30,9 +30,13 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InitialFetchRequest<T extends ParsedQs = ParsedQs> {
|
export interface InitialFetchRequest<
|
||||||
|
P extends Record<string, string> = Record<string, never>,
|
||||||
|
T extends Record<string, any> = Record<string, never>,
|
||||||
|
> {
|
||||||
path: string;
|
path: string;
|
||||||
query: T;
|
query: T;
|
||||||
|
match: Match<P>;
|
||||||
site: GetSiteResponse;
|
site: GetSiteResponse;
|
||||||
headers: { [key: string]: string };
|
headers: { [key: string]: string };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +1,107 @@
|
||||||
import { IRouteProps } from "inferno-router/dist/Route";
|
import { IRouteProps, RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
import { Communities } from "./components/community/communities";
|
import {
|
||||||
import { Community } from "./components/community/community";
|
Communities,
|
||||||
|
CommunitiesFetchConfig,
|
||||||
|
getCommunitiesQueryParams,
|
||||||
|
} from "./components/community/communities";
|
||||||
|
import {
|
||||||
|
Community,
|
||||||
|
CommunityFetchConfig,
|
||||||
|
getCommunityQueryParams,
|
||||||
|
} from "./components/community/community";
|
||||||
import { CreateCommunity } from "./components/community/create-community";
|
import { CreateCommunity } from "./components/community/create-community";
|
||||||
import { AdminSettings } from "./components/home/admin-settings";
|
import {
|
||||||
import { Home } from "./components/home/home";
|
AdminSettings,
|
||||||
import { Instances } from "./components/home/instances";
|
AdminSettingsFetchConfig,
|
||||||
|
} from "./components/home/admin-settings";
|
||||||
|
import {
|
||||||
|
Home,
|
||||||
|
HomeFetchConfig,
|
||||||
|
getHomeQueryParams,
|
||||||
|
} from "./components/home/home";
|
||||||
|
import { Instances, InstancesFetchConfig } from "./components/home/instances";
|
||||||
import { Legal } from "./components/home/legal";
|
import { Legal } from "./components/home/legal";
|
||||||
import { Login } from "./components/home/login";
|
import {
|
||||||
|
Login,
|
||||||
|
LoginFetchConfig,
|
||||||
|
getLoginQueryParams,
|
||||||
|
} from "./components/home/login";
|
||||||
import { LoginReset } from "./components/home/login-reset";
|
import { LoginReset } from "./components/home/login-reset";
|
||||||
import { Setup } from "./components/home/setup";
|
import { Setup } from "./components/home/setup";
|
||||||
import { Signup } from "./components/home/signup";
|
import { Signup } from "./components/home/signup";
|
||||||
import { Modlog } from "./components/modlog";
|
import {
|
||||||
import { Inbox } from "./components/person/inbox";
|
Modlog,
|
||||||
|
ModlogFetchConfig,
|
||||||
|
getModlogQueryParams,
|
||||||
|
} from "./components/modlog";
|
||||||
|
import { Inbox, InboxFetchConfig } from "./components/person/inbox";
|
||||||
import { PasswordChange } from "./components/person/password-change";
|
import { PasswordChange } from "./components/person/password-change";
|
||||||
import { Profile } from "./components/person/profile";
|
import {
|
||||||
import { RegistrationApplications } from "./components/person/registration-applications";
|
Profile,
|
||||||
import { Reports } from "./components/person/reports";
|
ProfileFetchConfig,
|
||||||
import { Settings } from "./components/person/settings";
|
getProfileQueryParams,
|
||||||
|
} from "./components/person/profile";
|
||||||
|
import {
|
||||||
|
RegistrationApplications,
|
||||||
|
RegistrationApplicationsFetchConfig,
|
||||||
|
} from "./components/person/registration-applications";
|
||||||
|
import { Reports, ReportsFetchConfig } from "./components/person/reports";
|
||||||
|
import { Settings, SettingsFetchConfig } from "./components/person/settings";
|
||||||
import { VerifyEmail } from "./components/person/verify-email";
|
import { VerifyEmail } from "./components/person/verify-email";
|
||||||
import { CreatePost } from "./components/post/create-post";
|
import {
|
||||||
import { Post } from "./components/post/post";
|
CreatePostFetchConfig,
|
||||||
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
|
CreatePost,
|
||||||
import { RemoteFetch } from "./components/remote-fetch";
|
getCreatePostQueryParams,
|
||||||
import { Search } from "./components/search";
|
} from "./components/post/create-post";
|
||||||
|
import { Post, PostFetchConfig } from "./components/post/post";
|
||||||
|
import {
|
||||||
|
CreatePrivateMessage,
|
||||||
|
CreatePrivateMessageFetchConfig,
|
||||||
|
} from "./components/private_message/create-private-message";
|
||||||
|
import {
|
||||||
|
RemoteFetch,
|
||||||
|
RemoteFetchFetchConfig,
|
||||||
|
getRemoteFetchQueryParams,
|
||||||
|
} from "./components/remote-fetch";
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
SearchFetchConfig,
|
||||||
|
getSearchQueryParams,
|
||||||
|
} from "./components/search";
|
||||||
import { InitialFetchRequest, RouteData } from "./interfaces";
|
import { InitialFetchRequest, RouteData } from "./interfaces";
|
||||||
|
import { GetSiteResponse } from "lemmy-js-client";
|
||||||
|
import { Inferno } from "inferno";
|
||||||
|
|
||||||
interface IRoutePropsWithFetch<T extends RouteData> extends IRouteProps {
|
export interface IRoutePropsWithFetch<
|
||||||
fetchInitialData?(req: InitialFetchRequest): Promise<T>;
|
DataT extends RouteData,
|
||||||
|
PathPropsT extends Record<string, string>,
|
||||||
|
QueryPropsT extends Record<string, any>,
|
||||||
|
> extends IRouteProps {
|
||||||
|
fetchInitialData?(
|
||||||
|
req: InitialFetchRequest<PathPropsT, QueryPropsT>,
|
||||||
|
): Promise<DataT>;
|
||||||
|
getQueryParams?(
|
||||||
|
source: string | undefined,
|
||||||
|
siteRes: GetSiteResponse,
|
||||||
|
): QueryPropsT;
|
||||||
|
component: Inferno.ComponentClass<
|
||||||
|
RouteComponentProps<PathPropsT> & QueryPropsT
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
|
export const routes: IRoutePropsWithFetch<RouteData, any, any>[] = [
|
||||||
{
|
{
|
||||||
path: `/`,
|
path: `/`,
|
||||||
component: Home,
|
component: Home,
|
||||||
fetchInitialData: Home.fetchInitialData,
|
fetchInitialData: Home.fetchInitialData,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
getQueryParams: getHomeQueryParams,
|
||||||
|
} as HomeFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/login`,
|
path: `/login`,
|
||||||
component: Login,
|
component: Login,
|
||||||
},
|
getQueryParams: getLoginQueryParams,
|
||||||
|
} as LoginFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/login_reset`,
|
path: `/login_reset`,
|
||||||
component: LoginReset,
|
component: LoginReset,
|
||||||
|
@ -52,7 +114,8 @@ export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
|
||||||
path: `/create_post`,
|
path: `/create_post`,
|
||||||
component: CreatePost,
|
component: CreatePost,
|
||||||
fetchInitialData: CreatePost.fetchInitialData,
|
fetchInitialData: CreatePost.fetchInitialData,
|
||||||
},
|
getQueryParams: getCreatePostQueryParams,
|
||||||
|
} as CreatePostFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/create_community`,
|
path: `/create_community`,
|
||||||
component: CreateCommunity,
|
component: CreateCommunity,
|
||||||
|
@ -61,73 +124,79 @@ export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
|
||||||
path: `/create_private_message/:recipient_id`,
|
path: `/create_private_message/:recipient_id`,
|
||||||
component: CreatePrivateMessage,
|
component: CreatePrivateMessage,
|
||||||
fetchInitialData: CreatePrivateMessage.fetchInitialData,
|
fetchInitialData: CreatePrivateMessage.fetchInitialData,
|
||||||
},
|
} as CreatePrivateMessageFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/communities`,
|
path: `/communities`,
|
||||||
component: Communities,
|
component: Communities,
|
||||||
fetchInitialData: Communities.fetchInitialData,
|
fetchInitialData: Communities.fetchInitialData,
|
||||||
},
|
getQueryParams: getCommunitiesQueryParams,
|
||||||
|
} as CommunitiesFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/post/:post_id`,
|
path: `/post/:post_id`,
|
||||||
component: Post,
|
component: Post,
|
||||||
fetchInitialData: Post.fetchInitialData,
|
fetchInitialData: Post.fetchInitialData,
|
||||||
},
|
} as PostFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/comment/:comment_id`,
|
path: `/comment/:comment_id`,
|
||||||
component: Post,
|
component: Post,
|
||||||
fetchInitialData: Post.fetchInitialData,
|
fetchInitialData: Post.fetchInitialData,
|
||||||
},
|
} as PostFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/c/:name`,
|
path: `/c/:name`,
|
||||||
component: Community,
|
component: Community,
|
||||||
fetchInitialData: Community.fetchInitialData,
|
fetchInitialData: Community.fetchInitialData,
|
||||||
},
|
getQueryParams: getCommunityQueryParams,
|
||||||
|
} as CommunityFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/u/:username`,
|
path: `/u/:username`,
|
||||||
component: Profile,
|
component: Profile,
|
||||||
fetchInitialData: Profile.fetchInitialData,
|
fetchInitialData: Profile.fetchInitialData,
|
||||||
},
|
getQueryParams: getProfileQueryParams,
|
||||||
|
} as ProfileFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/inbox`,
|
path: `/inbox`,
|
||||||
component: Inbox,
|
component: Inbox,
|
||||||
fetchInitialData: Inbox.fetchInitialData,
|
fetchInitialData: Inbox.fetchInitialData,
|
||||||
},
|
} as InboxFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/settings`,
|
path: `/settings`,
|
||||||
component: Settings,
|
component: Settings,
|
||||||
fetchInitialData: Settings.fetchInitialData,
|
fetchInitialData: Settings.fetchInitialData,
|
||||||
},
|
} as SettingsFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/modlog/:communityId`,
|
path: `/modlog/:communityId`,
|
||||||
component: Modlog,
|
component: Modlog,
|
||||||
fetchInitialData: Modlog.fetchInitialData,
|
fetchInitialData: Modlog.fetchInitialData,
|
||||||
},
|
getQueryParams: getModlogQueryParams,
|
||||||
|
} as ModlogFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/modlog`,
|
path: `/modlog`,
|
||||||
component: Modlog,
|
component: Modlog,
|
||||||
fetchInitialData: Modlog.fetchInitialData,
|
fetchInitialData: Modlog.fetchInitialData,
|
||||||
},
|
getQueryParams: getModlogQueryParams,
|
||||||
|
} as ModlogFetchConfig,
|
||||||
{ path: `/setup`, component: Setup },
|
{ path: `/setup`, component: Setup },
|
||||||
{
|
{
|
||||||
path: `/admin`,
|
path: `/admin`,
|
||||||
component: AdminSettings,
|
component: AdminSettings,
|
||||||
fetchInitialData: AdminSettings.fetchInitialData,
|
fetchInitialData: AdminSettings.fetchInitialData,
|
||||||
},
|
} as AdminSettingsFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/reports`,
|
path: `/reports`,
|
||||||
component: Reports,
|
component: Reports,
|
||||||
fetchInitialData: Reports.fetchInitialData,
|
fetchInitialData: Reports.fetchInitialData,
|
||||||
},
|
} as ReportsFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/registration_applications`,
|
path: `/registration_applications`,
|
||||||
component: RegistrationApplications,
|
component: RegistrationApplications,
|
||||||
fetchInitialData: RegistrationApplications.fetchInitialData,
|
fetchInitialData: RegistrationApplications.fetchInitialData,
|
||||||
},
|
} as RegistrationApplicationsFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/search`,
|
path: `/search`,
|
||||||
component: Search,
|
component: Search,
|
||||||
fetchInitialData: Search.fetchInitialData,
|
fetchInitialData: Search.fetchInitialData,
|
||||||
},
|
getQueryParams: getSearchQueryParams,
|
||||||
|
} as SearchFetchConfig,
|
||||||
{
|
{
|
||||||
path: `/password_change/:token`,
|
path: `/password_change/:token`,
|
||||||
component: PasswordChange,
|
component: PasswordChange,
|
||||||
|
@ -140,11 +209,12 @@ export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
|
||||||
path: `/instances`,
|
path: `/instances`,
|
||||||
component: Instances,
|
component: Instances,
|
||||||
fetchInitialData: Instances.fetchInitialData,
|
fetchInitialData: Instances.fetchInitialData,
|
||||||
},
|
} as InstancesFetchConfig,
|
||||||
{ path: `/legal`, component: Legal },
|
{ path: `/legal`, component: Legal },
|
||||||
{
|
{
|
||||||
path: "/activitypub/externalInteraction",
|
path: "/activitypub/externalInteraction",
|
||||||
component: RemoteFetch,
|
component: RemoteFetch,
|
||||||
fetchInitialData: RemoteFetch.fetchInitialData,
|
fetchInitialData: RemoteFetch.fetchInitialData,
|
||||||
},
|
getQueryParams: getRemoteFetchQueryParams,
|
||||||
|
} as RemoteFetchFetchConfig,
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { getQueryString } from "@utils/helpers";
|
||||||
|
|
||||||
export default function communityRSSUrl(actorId: string, sort: string): string {
|
export default function communityRSSUrl(actorId: string, sort: string): string {
|
||||||
const url = new URL(actorId);
|
const url = new URL(actorId);
|
||||||
return `${url.origin}/feeds${url.pathname}.xml?sort=${sort}`;
|
return `${url.origin}/feeds${url.pathname}.xml${getQueryString({ sort })}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
export default function getCommentIdFromProps(props: any): number | undefined {
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
|
||||||
|
export default function getCommentIdFromProps(
|
||||||
|
props: Pick<RouteComponentProps<{ comment_id?: string }>, "match">,
|
||||||
|
): number | undefined {
|
||||||
const id = props.match.params.comment_id;
|
const id = props.match.params.comment_id;
|
||||||
return id ? Number(id) : undefined;
|
return id ? Number(id) : undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
export default function getIdFromProps(props: any): number | undefined {
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
|
||||||
|
export default function getIdFromProps(
|
||||||
|
props: Pick<RouteComponentProps<{ post_id?: string }>, "match">,
|
||||||
|
): number | undefined {
|
||||||
const id = props.match.params.post_id;
|
const id = props.match.params.post_id;
|
||||||
return id ? Number(id) : undefined;
|
return id ? Number(id) : undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
export default function getRecipientIdFromProps(props: any): number {
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
|
|
||||||
|
export default function getRecipientIdFromProps(
|
||||||
|
props: Pick<RouteComponentProps<{ recipient_id: string }>, "match">,
|
||||||
|
): number {
|
||||||
return props.match.params.recipient_id
|
return props.match.params.recipient_id
|
||||||
? Number(props.match.params.recipient_id)
|
? Number(props.match.params.recipient_id)
|
||||||
: 1;
|
: 1;
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
import { isBrowser } from "@utils/browser";
|
type Empty = NonNullable<unknown>;
|
||||||
|
|
||||||
|
type QueryMapping<PropsT, FallbacksT extends Empty> = {
|
||||||
|
[K in keyof PropsT]-?: (
|
||||||
|
input: string | undefined,
|
||||||
|
fallback: K extends keyof FallbacksT ? FallbacksT[K] : undefined,
|
||||||
|
) => PropsT[K];
|
||||||
|
};
|
||||||
|
|
||||||
export default function getQueryParams<
|
export default function getQueryParams<
|
||||||
T extends Record<string, any>,
|
PropsT,
|
||||||
>(processors: {
|
FallbacksT extends Empty = Empty,
|
||||||
[K in keyof T]: (param: string) => T[K];
|
>(
|
||||||
}): T {
|
processors: QueryMapping<PropsT, FallbacksT>,
|
||||||
if (isBrowser()) {
|
source?: string,
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
fallbacks: FallbacksT = {} as FallbacksT,
|
||||||
|
): PropsT {
|
||||||
|
const searchParams = new URLSearchParams(source);
|
||||||
|
|
||||||
return Array.from(Object.entries(processors)).reduce(
|
const ret: Partial<PropsT> = {};
|
||||||
(acc, [key, process]) => ({
|
for (const key in processors) {
|
||||||
...acc,
|
ret[key as string] = processors[key](
|
||||||
[key]: process(searchParams.get(key)),
|
searchParams.get(key) ?? undefined,
|
||||||
}),
|
fallbacks[key as string],
|
||||||
{} as T,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return ret as PropsT;
|
||||||
return {} as T;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
export default function getQueryString<
|
export default function getQueryString<
|
||||||
T extends Record<string, string | undefined>,
|
T extends Record<string, string | undefined>,
|
||||||
>(obj: T) {
|
>(obj: T) {
|
||||||
return Object.entries(obj)
|
const searchParams = new URLSearchParams();
|
||||||
|
Object.entries(obj)
|
||||||
.filter(([, val]) => val !== undefined && val !== null)
|
.filter(([, val]) => val !== undefined && val !== null)
|
||||||
.reduce(
|
.forEach(([key, val]) => searchParams.set(key, val ?? ""));
|
||||||
(acc, [key, val], index) => `${acc}${index > 0 ? "&" : ""}${key}=${val}`,
|
if (searchParams.size) {
|
||||||
"?",
|
return "?" + searchParams.toString();
|
||||||
);
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue