mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-26 06:11:15 +00:00
Merge pull request #1043 from LemmyNet/route-data-refactor
Route data refactor
This commit is contained in:
commit
56500b6f0d
20 changed files with 525 additions and 373 deletions
13
package.json
13
package.json
|
@ -19,9 +19,16 @@
|
||||||
"themes:watch": "sass --watch src/assets/css/themes/:src/assets/css/themes"
|
"themes:watch": "sass --watch src/assets/css/themes/:src/assets/css/themes"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{ts,tsx,js}": ["prettier --write", "eslint --fix"],
|
"*.{ts,tsx,js}": [
|
||||||
"*.{css, scss}": ["prettier --write"],
|
"prettier --write",
|
||||||
"package.json": ["sortpack"]
|
"eslint --fix"
|
||||||
|
],
|
||||||
|
"*.{css, scss}": [
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"package.json": [
|
||||||
|
"sortpack"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-decorators": "^7.21.0",
|
"@babel/plugin-proposal-decorators": "^7.21.0",
|
||||||
|
|
|
@ -17,9 +17,10 @@ import {
|
||||||
ILemmyConfig,
|
ILemmyConfig,
|
||||||
InitialFetchRequest,
|
InitialFetchRequest,
|
||||||
IsoDataOptionalSite,
|
IsoDataOptionalSite,
|
||||||
|
RouteData,
|
||||||
} from "../shared/interfaces";
|
} from "../shared/interfaces";
|
||||||
import { routes } from "../shared/routes";
|
import { routes } from "../shared/routes";
|
||||||
import { RequestState, wrapClient } from "../shared/services/HttpService";
|
import { FailedRequestState, wrapClient } from "../shared/services/HttpService";
|
||||||
import {
|
import {
|
||||||
ErrorPageData,
|
ErrorPageData,
|
||||||
favIconPngUrl,
|
favIconPngUrl,
|
||||||
|
@ -136,7 +137,7 @@ server.get("/*", async (req, res) => {
|
||||||
// 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,
|
||||||
// in order to remove the jwt on the browser. Necessary for wrong jwts
|
// in order to remove the jwt on the browser. Necessary for wrong jwts
|
||||||
let site: GetSiteResponse | undefined = undefined;
|
let site: GetSiteResponse | undefined = undefined;
|
||||||
const routeData: RequestState<any>[] = [];
|
let routeData: RouteData = {};
|
||||||
let errorPageData: ErrorPageData | undefined = undefined;
|
let errorPageData: ErrorPageData | undefined = undefined;
|
||||||
let try_site = await client.getSite(getSiteForm);
|
let try_site = await client.getSite(getSiteForm);
|
||||||
if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
|
if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
|
||||||
|
@ -160,7 +161,7 @@ server.get("/*", async (req, res) => {
|
||||||
return res.redirect("/setup");
|
return res.redirect("/setup");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (site) {
|
if (site && activeRoute?.fetchInitialData) {
|
||||||
const initialFetchReq: InitialFetchRequest = {
|
const initialFetchReq: InitialFetchRequest = {
|
||||||
client,
|
client,
|
||||||
auth,
|
auth,
|
||||||
|
@ -169,26 +170,23 @@ server.get("/*", async (req, res) => {
|
||||||
site,
|
site,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (activeRoute?.fetchInitialData) {
|
routeData = await activeRoute.fetchInitialData(initialFetchReq);
|
||||||
routeData.push(
|
|
||||||
...(await Promise.all([
|
|
||||||
...activeRoute.fetchInitialData(initialFetchReq),
|
|
||||||
]))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (try_site.state === "failed") {
|
} else if (try_site.state === "failed") {
|
||||||
errorPageData = getErrorPageData(new Error(try_site.msg), site);
|
errorPageData = getErrorPageData(new Error(try_site.msg), site);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const error = Object.values(routeData).find(
|
||||||
|
res => res.state === "failed"
|
||||||
|
) as FailedRequestState | undefined;
|
||||||
|
|
||||||
// Redirect to the 404 if there's an API error
|
// Redirect to the 404 if there's an API error
|
||||||
if (routeData[0] && routeData[0].state === "failed") {
|
if (error) {
|
||||||
const error = routeData[0].msg;
|
console.error(error.msg);
|
||||||
console.error(error);
|
if (error.msg === "instance_is_private") {
|
||||||
if (error === "instance_is_private") {
|
|
||||||
return res.redirect(`/signup`);
|
return res.redirect(`/signup`);
|
||||||
} else {
|
} else {
|
||||||
errorPageData = getErrorPageData(new Error(error), site);
|
errorPageData = getErrorPageData(new Error(error.msg), site);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
QueryParams,
|
QueryParams,
|
||||||
|
RouteDataResponse,
|
||||||
editCommunity,
|
editCommunity,
|
||||||
getPageFromString,
|
getPageFromString,
|
||||||
getQueryParams,
|
getQueryParams,
|
||||||
|
@ -30,6 +31,10 @@ import { CommunityLink } from "./community-link";
|
||||||
|
|
||||||
const communityLimit = 50;
|
const communityLimit = 50;
|
||||||
|
|
||||||
|
type CommunitiesData = RouteDataResponse<{
|
||||||
|
listCommunitiesResponse: ListCommunitiesResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface CommunitiesState {
|
interface CommunitiesState {
|
||||||
listCommunitiesResponse: RequestState<ListCommunitiesResponse>;
|
listCommunitiesResponse: RequestState<ListCommunitiesResponse>;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
|
@ -47,7 +52,7 @@ function getListingTypeFromQuery(listingType?: string): ListingType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Communities extends Component<any, CommunitiesState> {
|
export class Communities extends Component<any, CommunitiesState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<CommunitiesData>(this.context);
|
||||||
state: CommunitiesState = {
|
state: CommunitiesState = {
|
||||||
listCommunitiesResponse: { state: "empty" },
|
listCommunitiesResponse: { state: "empty" },
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
|
@ -62,9 +67,11 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
|
const { listCommunitiesResponse } = this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
listCommunitiesResponse: this.isoData.routeData[0],
|
listCommunitiesResponse,
|
||||||
isIsomorphic: true,
|
isIsomorphic: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -274,13 +281,13 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
i.context.router.history.push(`/search?q=${searchParamEncoded}`);
|
i.context.router.history.push(`/search?q=${searchParamEncoded}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
query: { listingType, page },
|
query: { listingType, page },
|
||||||
client,
|
client,
|
||||||
auth,
|
auth,
|
||||||
}: InitialFetchRequest<QueryParams<CommunitiesProps>>): Promise<
|
}: InitialFetchRequest<
|
||||||
RequestState<any>
|
QueryParams<CommunitiesProps>
|
||||||
>[] {
|
>): Promise<CommunitiesData> {
|
||||||
const listCommunitiesForm: ListCommunities = {
|
const listCommunitiesForm: ListCommunities = {
|
||||||
type_: getListingTypeFromQuery(listingType),
|
type_: getListingTypeFromQuery(listingType),
|
||||||
sort: "TopMonth",
|
sort: "TopMonth",
|
||||||
|
@ -289,7 +296,11 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
auth: auth,
|
auth: auth,
|
||||||
};
|
};
|
||||||
|
|
||||||
return [client.listCommunities(listCommunitiesForm)];
|
return {
|
||||||
|
listCommunitiesResponse: await client.listCommunities(
|
||||||
|
listCommunitiesForm
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommunitiesQueryParams() {
|
getCommunitiesQueryParams() {
|
||||||
|
|
|
@ -63,6 +63,7 @@ import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
QueryParams,
|
QueryParams,
|
||||||
|
RouteDataResponse,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
communityRSSUrl,
|
communityRSSUrl,
|
||||||
editComment,
|
editComment,
|
||||||
|
@ -100,6 +101,12 @@ import { SiteSidebar } from "../home/site-sidebar";
|
||||||
import { PostListings } from "../post/post-listings";
|
import { PostListings } from "../post/post-listings";
|
||||||
import { CommunityLink } from "./community-link";
|
import { CommunityLink } from "./community-link";
|
||||||
|
|
||||||
|
type CommunityData = RouteDataResponse<{
|
||||||
|
communityRes: GetCommunityResponse;
|
||||||
|
postsRes: GetPostsResponse;
|
||||||
|
commentsRes: GetCommentsResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
communityRes: RequestState<GetCommunityResponse>;
|
communityRes: RequestState<GetCommunityResponse>;
|
||||||
postsRes: RequestState<GetPostsResponse>;
|
postsRes: RequestState<GetPostsResponse>;
|
||||||
|
@ -140,7 +147,7 @@ export class Community extends Component<
|
||||||
RouteComponentProps<{ name: string }>,
|
RouteComponentProps<{ name: string }>,
|
||||||
State
|
State
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<CommunityData>(this.context);
|
||||||
state: State = {
|
state: State = {
|
||||||
communityRes: { state: "empty" },
|
communityRes: { state: "empty" },
|
||||||
postsRes: { state: "empty" },
|
postsRes: { state: "empty" },
|
||||||
|
@ -194,13 +201,14 @@ export class Community extends Component<
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [communityRes, postsRes, commentsRes] = this.isoData.routeData;
|
const { communityRes, commentsRes, postsRes } = this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
|
isIsomorphic: true,
|
||||||
|
commentsRes,
|
||||||
communityRes,
|
communityRes,
|
||||||
postsRes,
|
postsRes,
|
||||||
commentsRes,
|
|
||||||
isIsomorphic: true,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,23 +235,21 @@ export class Community extends Component<
|
||||||
saveScrollPosition(this.context);
|
saveScrollPosition(this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
client,
|
client,
|
||||||
path,
|
path,
|
||||||
query: { dataType: urlDataType, page: urlPage, sort: urlSort },
|
query: { dataType: urlDataType, page: urlPage, sort: urlSort },
|
||||||
auth,
|
auth,
|
||||||
}: InitialFetchRequest<QueryParams<CommunityProps>>): Promise<
|
}: InitialFetchRequest<QueryParams<CommunityProps>>): Promise<
|
||||||
RequestState<any>
|
Promise<CommunityData>
|
||||||
>[] {
|
> {
|
||||||
const pathSplit = path.split("/");
|
const pathSplit = path.split("/");
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
|
||||||
|
|
||||||
const communityName = pathSplit[2];
|
const communityName = pathSplit[2];
|
||||||
const communityForm: GetCommunity = {
|
const communityForm: GetCommunity = {
|
||||||
name: communityName,
|
name: communityName,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(client.getCommunity(communityForm));
|
|
||||||
|
|
||||||
const dataType = getDataTypeFromQuery(urlDataType);
|
const dataType = getDataTypeFromQuery(urlDataType);
|
||||||
|
|
||||||
|
@ -251,6 +257,11 @@ export class Community extends Component<
|
||||||
|
|
||||||
const page = getPageFromString(urlPage);
|
const page = getPageFromString(urlPage);
|
||||||
|
|
||||||
|
let postsResponse: RequestState<GetPostsResponse> = { state: "empty" };
|
||||||
|
let commentsResponse: RequestState<GetCommentsResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
const getPostsForm: GetPosts = {
|
const getPostsForm: GetPosts = {
|
||||||
community_name: communityName,
|
community_name: communityName,
|
||||||
|
@ -261,8 +272,8 @@ export class Community extends Component<
|
||||||
saved_only: false,
|
saved_only: false,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(client.getPosts(getPostsForm));
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
postsResponse = await client.getPosts(getPostsForm);
|
||||||
} else {
|
} else {
|
||||||
const getCommentsForm: GetComments = {
|
const getCommentsForm: GetComments = {
|
||||||
community_name: communityName,
|
community_name: communityName,
|
||||||
|
@ -273,11 +284,15 @@ export class Community extends Component<
|
||||||
saved_only: false,
|
saved_only: false,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
promises.push(client.getComments(getCommentsForm));
|
commentsResponse = await client.getComments(getCommentsForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promises;
|
return {
|
||||||
|
communityRes: await client.getCommunity(communityForm),
|
||||||
|
commentsRes: commentsResponse,
|
||||||
|
postsRes: postsResponse,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
|
RouteDataResponse,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
fetchThemeList,
|
fetchThemeList,
|
||||||
myAuthRequired,
|
myAuthRequired,
|
||||||
|
@ -32,6 +33,11 @@ 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";
|
||||||
|
|
||||||
|
type AdminSettingsData = RouteDataResponse<{
|
||||||
|
bannedRes: BannedPersonsResponse;
|
||||||
|
instancesRes: GetFederatedInstancesResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface AdminSettingsState {
|
interface AdminSettingsState {
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
banned: PersonView[];
|
banned: PersonView[];
|
||||||
|
@ -46,7 +52,7 @@ interface AdminSettingsState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AdminSettings extends Component<any, AdminSettingsState> {
|
export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<AdminSettingsData>(this.context);
|
||||||
state: AdminSettingsState = {
|
state: AdminSettingsState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
banned: [],
|
banned: [],
|
||||||
|
@ -70,7 +76,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [bannedRes, instancesRes] = this.isoData.routeData;
|
const { bannedRes, instancesRes } = this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
bannedRes,
|
bannedRes,
|
||||||
|
@ -80,47 +87,18 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchData() {
|
static async fetchInitialData({
|
||||||
this.setState({
|
|
||||||
bannedRes: { state: "loading" },
|
|
||||||
instancesRes: { state: "loading" },
|
|
||||||
themeList: [],
|
|
||||||
loading: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const auth = myAuthRequired();
|
|
||||||
|
|
||||||
const [bannedRes, instancesRes, themeList] = await Promise.all([
|
|
||||||
HttpService.client.getBannedPersons({ auth }),
|
|
||||||
HttpService.client.getFederatedInstances({ auth }),
|
|
||||||
fetchThemeList(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
bannedRes,
|
|
||||||
instancesRes,
|
|
||||||
themeList,
|
|
||||||
loading: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static fetchInitialData({
|
|
||||||
auth,
|
auth,
|
||||||
client,
|
client,
|
||||||
}: InitialFetchRequest): Promise<any>[] {
|
}: InitialFetchRequest): Promise<AdminSettingsData> {
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
return {
|
||||||
|
bannedRes: await client.getBannedPersons({
|
||||||
if (auth) {
|
auth: auth as string,
|
||||||
promises.push(client.getBannedPersons({ auth }));
|
}),
|
||||||
promises.push(client.getFederatedInstances({ auth }));
|
instancesRes: await client.getFederatedInstances({
|
||||||
} else {
|
auth: auth as string,
|
||||||
promises.push(
|
}),
|
||||||
Promise.resolve({ state: "empty" }),
|
};
|
||||||
Promise.resolve({ state: "empty" })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return promises;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
@ -218,6 +196,28 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchData() {
|
||||||
|
this.setState({
|
||||||
|
bannedRes: { state: "loading" },
|
||||||
|
instancesRes: { state: "loading" },
|
||||||
|
themeList: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const auth = myAuthRequired();
|
||||||
|
|
||||||
|
const [bannedRes, instancesRes, themeList] = await Promise.all([
|
||||||
|
HttpService.client.getBannedPersons({ auth }),
|
||||||
|
HttpService.client.getFederatedInstances({ auth }),
|
||||||
|
fetchThemeList(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
bannedRes,
|
||||||
|
instancesRes,
|
||||||
|
themeList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
admins() {
|
admins() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -77,6 +77,7 @@ import {
|
||||||
QueryParams,
|
QueryParams,
|
||||||
relTags,
|
relTags,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
|
RouteDataResponse,
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
|
@ -117,6 +118,45 @@ interface HomeProps {
|
||||||
page: number;
|
page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HomeData = RouteDataResponse<{
|
||||||
|
postsRes: GetPostsResponse;
|
||||||
|
commentsRes: GetCommentsResponse;
|
||||||
|
trendingCommunitiesRes: ListCommunitiesResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
function getRss(listingType: ListingType) {
|
||||||
|
const { sort } = getHomeQueryParams();
|
||||||
|
const auth = myAuth();
|
||||||
|
|
||||||
|
let rss: string | undefined = undefined;
|
||||||
|
|
||||||
|
switch (listingType) {
|
||||||
|
case "All": {
|
||||||
|
rss = `/feeds/all.xml?sort=${sort}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Local": {
|
||||||
|
rss = `/feeds/local.xml?sort=${sort}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Subscribed": {
|
||||||
|
rss = auth ? `/feeds/front/${auth}.xml?sort=${sort}` : undefined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
rss && (
|
||||||
|
<>
|
||||||
|
<a href={rss} rel={relTags} title="RSS">
|
||||||
|
<Icon icon="rss" classes="text-muted small" />
|
||||||
|
</a>
|
||||||
|
<link rel="alternate" type="application/atom+xml" href={rss} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getDataTypeFromQuery(type?: string): DataType {
|
function getDataTypeFromQuery(type?: string): DataType {
|
||||||
return type ? DataType[type] : DataType.Post;
|
return type ? DataType[type] : DataType.Post;
|
||||||
}
|
}
|
||||||
|
@ -176,7 +216,7 @@ const LinkButton = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
export class Home extends Component<any, HomeState> {
|
export class Home extends Component<any, HomeState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<HomeData>(this.context);
|
||||||
state: HomeState = {
|
state: HomeState = {
|
||||||
postsRes: { state: "empty" },
|
postsRes: { state: "empty" },
|
||||||
commentsRes: { state: "empty" },
|
commentsRes: { state: "empty" },
|
||||||
|
@ -228,14 +268,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [postsRes, commentsRes, trendingCommunitiesRes] =
|
const { trendingCommunitiesRes, commentsRes, postsRes } =
|
||||||
this.isoData.routeData;
|
this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
postsRes,
|
|
||||||
commentsRes,
|
|
||||||
trendingCommunitiesRes,
|
trendingCommunitiesRes,
|
||||||
|
commentsRes,
|
||||||
|
postsRes,
|
||||||
tagline: getRandomFromList(this.state?.siteRes?.taglines ?? [])
|
tagline: getRandomFromList(this.state?.siteRes?.taglines ?? [])
|
||||||
?.content,
|
?.content,
|
||||||
isIsomorphic: true,
|
isIsomorphic: true,
|
||||||
|
@ -244,7 +284,12 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
if (!this.state.isIsomorphic || !this.isoData.routeData.length) {
|
if (
|
||||||
|
!this.state.isIsomorphic ||
|
||||||
|
!Object.values(this.isoData.routeData).some(
|
||||||
|
res => res.state === "success" || res.state === "failed"
|
||||||
|
)
|
||||||
|
) {
|
||||||
await Promise.all([this.fetchTrendingCommunities(), this.fetchData()]);
|
await Promise.all([this.fetchTrendingCommunities(), this.fetchData()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,13 +300,11 @@ export class Home extends Component<any, HomeState> {
|
||||||
saveScrollPosition(this.context);
|
saveScrollPosition(this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
client,
|
client,
|
||||||
auth,
|
auth,
|
||||||
query: { dataType: urlDataType, listingType, page: urlPage, sort: urlSort },
|
query: { dataType: urlDataType, listingType, page: urlPage, sort: urlSort },
|
||||||
}: InitialFetchRequest<QueryParams<HomeProps>>): Promise<
|
}: InitialFetchRequest<QueryParams<HomeProps>>): Promise<HomeData> {
|
||||||
RequestState<any>
|
|
||||||
>[] {
|
|
||||||
const dataType = getDataTypeFromQuery(urlDataType);
|
const dataType = getDataTypeFromQuery(urlDataType);
|
||||||
|
|
||||||
// TODO figure out auth default_listingType, default_sort_type
|
// TODO figure out auth default_listingType, default_sort_type
|
||||||
|
@ -270,7 +313,10 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
const page = urlPage ? Number(urlPage) : 1;
|
const page = urlPage ? Number(urlPage) : 1;
|
||||||
|
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
let postsRes: RequestState<GetPostsResponse> = { state: "empty" };
|
||||||
|
let commentsRes: RequestState<GetCommentsResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
const getPostsForm: GetPosts = {
|
const getPostsForm: GetPosts = {
|
||||||
|
@ -282,8 +328,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(client.getPosts(getPostsForm));
|
postsRes = await client.getPosts(getPostsForm);
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
} else {
|
} else {
|
||||||
const getCommentsForm: GetComments = {
|
const getCommentsForm: GetComments = {
|
||||||
page,
|
page,
|
||||||
|
@ -293,8 +338,8 @@ export class Home extends Component<any, HomeState> {
|
||||||
saved_only: false,
|
saved_only: false,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
promises.push(client.getComments(getCommentsForm));
|
commentsRes = await client.getComments(getCommentsForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
const trendingCommunitiesForm: ListCommunities = {
|
const trendingCommunitiesForm: ListCommunities = {
|
||||||
|
@ -303,9 +348,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
limit: trendingFetchLimit,
|
limit: trendingFetchLimit,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(client.listCommunities(trendingCommunitiesForm));
|
|
||||||
|
|
||||||
return promises;
|
return {
|
||||||
|
trendingCommunitiesRes: await client.listCommunities(
|
||||||
|
trendingCommunitiesForm
|
||||||
|
),
|
||||||
|
commentsRes,
|
||||||
|
postsRes,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
@ -340,7 +390,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
></div>
|
></div>
|
||||||
)}
|
)}
|
||||||
<div className="d-block d-md-none">{this.mobileView}</div>
|
<div className="d-block d-md-none">{this.mobileView}</div>
|
||||||
{this.posts()}
|
{this.posts}
|
||||||
</main>
|
</main>
|
||||||
<aside className="d-none d-md-block col-md-4">
|
<aside className="d-none d-md-block col-md-4">
|
||||||
{this.mySidebar}
|
{this.mySidebar}
|
||||||
|
@ -552,7 +602,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
await this.fetchData();
|
await this.fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
posts() {
|
get posts() {
|
||||||
const { page } = getHomeQueryParams();
|
const { page } = getHomeQueryParams();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -571,7 +621,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
const siteRes = this.state.siteRes;
|
const siteRes = this.state.siteRes;
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
switch (this.state.postsRes?.state) {
|
switch (this.state.postsRes.state) {
|
||||||
case "loading":
|
case "loading":
|
||||||
return (
|
return (
|
||||||
<h5>
|
<h5>
|
||||||
|
@ -677,44 +727,11 @@ export class Home extends Component<any, HomeState> {
|
||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
<SortSelect sort={sort} onChange={this.handleSortChange} />
|
<SortSelect sort={sort} onChange={this.handleSortChange} />
|
||||||
</span>
|
</span>
|
||||||
{this.getRss(listingType)}
|
{getRss(listingType)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRss(listingType: ListingType) {
|
|
||||||
const { sort } = getHomeQueryParams();
|
|
||||||
const auth = myAuth();
|
|
||||||
|
|
||||||
let rss: string | undefined = undefined;
|
|
||||||
|
|
||||||
switch (listingType) {
|
|
||||||
case "All": {
|
|
||||||
rss = `/feeds/all.xml?sort=${sort}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Local": {
|
|
||||||
rss = `/feeds/local.xml?sort=${sort}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Subscribed": {
|
|
||||||
rss = auth ? `/feeds/front/${auth}.xml?sort=${sort}` : undefined;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
rss && (
|
|
||||||
<>
|
|
||||||
<a href={rss} rel={relTags} title="RSS">
|
|
||||||
<Icon icon="rss" classes="text-muted small" />
|
|
||||||
</a>
|
|
||||||
<link rel="alternate" type="application/atom+xml" href={rss} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchTrendingCommunities() {
|
async fetchTrendingCommunities() {
|
||||||
this.setState({ trendingCommunitiesRes: { state: "loading" } });
|
this.setState({ trendingCommunitiesRes: { state: "loading" } });
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|
|
@ -8,10 +8,14 @@ import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import { relTags, setIsoData } from "../../utils";
|
import { RouteDataResponse, relTags, setIsoData } from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
|
||||||
|
type InstancesData = RouteDataResponse<{
|
||||||
|
federatedInstancesResponse: GetFederatedInstancesResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface InstancesState {
|
interface InstancesState {
|
||||||
instancesRes: RequestState<GetFederatedInstancesResponse>;
|
instancesRes: RequestState<GetFederatedInstancesResponse>;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
|
@ -19,7 +23,7 @@ interface InstancesState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Instances extends Component<any, InstancesState> {
|
export class Instances extends Component<any, InstancesState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<InstancesData>(this.context);
|
||||||
state: InstancesState = {
|
state: InstancesState = {
|
||||||
instancesRes: { state: "empty" },
|
instancesRes: { state: "empty" },
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
|
@ -33,7 +37,7 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
instancesRes: this.isoData.routeData[0],
|
instancesRes: this.isoData.routeData.federatedInstancesResponse,
|
||||||
isIsomorphic: true,
|
isIsomorphic: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -55,10 +59,12 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData(
|
static async fetchInitialData({
|
||||||
req: InitialFetchRequest
|
client,
|
||||||
): Promise<RequestState<any>>[] {
|
}: InitialFetchRequest): Promise<InstancesData> {
|
||||||
return [req.client.getFederatedInstances({})];
|
return {
|
||||||
|
federatedInstancesResponse: await client.getFederatedInstances({}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
GetModlog,
|
GetModlog,
|
||||||
GetModlogResponse,
|
GetModlogResponse,
|
||||||
GetPersonDetails,
|
GetPersonDetails,
|
||||||
|
GetPersonDetailsResponse,
|
||||||
ModAddCommunityView,
|
ModAddCommunityView,
|
||||||
ModAddView,
|
ModAddView,
|
||||||
ModBanFromCommunityView,
|
ModBanFromCommunityView,
|
||||||
|
@ -34,6 +35,7 @@ import { HttpService, RequestState } from "../services/HttpService";
|
||||||
import {
|
import {
|
||||||
Choice,
|
Choice,
|
||||||
QueryParams,
|
QueryParams,
|
||||||
|
RouteDataResponse,
|
||||||
amAdmin,
|
amAdmin,
|
||||||
amMod,
|
amMod,
|
||||||
debounce,
|
debounce,
|
||||||
|
@ -74,6 +76,13 @@ type View =
|
||||||
| AdminPurgePostView
|
| AdminPurgePostView
|
||||||
| AdminPurgeCommentView;
|
| AdminPurgeCommentView;
|
||||||
|
|
||||||
|
type ModlogData = RouteDataResponse<{
|
||||||
|
res: GetModlogResponse;
|
||||||
|
communityRes: GetCommunityResponse;
|
||||||
|
modUserResponse: GetPersonDetailsResponse;
|
||||||
|
userResponse: GetPersonDetailsResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface ModlogType {
|
interface ModlogType {
|
||||||
id: number;
|
id: number;
|
||||||
type_: ModlogActionType;
|
type_: ModlogActionType;
|
||||||
|
@ -631,7 +640,7 @@ export class Modlog extends Component<
|
||||||
RouteComponentProps<{ communityId?: string }>,
|
RouteComponentProps<{ communityId?: string }>,
|
||||||
ModlogState
|
ModlogState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<ModlogData>(this.context);
|
||||||
|
|
||||||
state: ModlogState = {
|
state: ModlogState = {
|
||||||
res: { state: "empty" },
|
res: { state: "empty" },
|
||||||
|
@ -653,25 +662,26 @@ export class Modlog extends Component<
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [res, communityRes, filteredModRes, filteredUserRes] =
|
const { res, communityRes, modUserResponse, userResponse } =
|
||||||
this.isoData.routeData;
|
this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
res,
|
res,
|
||||||
communityRes,
|
communityRes,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (filteredModRes.state === "success") {
|
if (modUserResponse.state === "success") {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
modSearchOptions: [personToChoice(filteredModRes.data.person_view)],
|
modSearchOptions: [personToChoice(modUserResponse.data.person_view)],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filteredUserRes.state === "success") {
|
if (userResponse.state === "success") {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
userSearchOptions: [personToChoice(filteredUserRes.data.person_view)],
|
userSearchOptions: [personToChoice(userResponse.data.person_view)],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -958,17 +968,14 @@ export class Modlog extends Component<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
client,
|
client,
|
||||||
path,
|
path,
|
||||||
query: { modId: urlModId, page, userId: urlUserId, actionType },
|
query: { modId: urlModId, page, userId: urlUserId, actionType },
|
||||||
auth,
|
auth,
|
||||||
site,
|
site,
|
||||||
}: InitialFetchRequest<QueryParams<ModlogProps>>): Promise<
|
}: InitialFetchRequest<QueryParams<ModlogProps>>): Promise<ModlogData> {
|
||||||
RequestState<any>
|
|
||||||
>[] {
|
|
||||||
const pathSplit = path.split("/");
|
const pathSplit = path.split("/");
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
|
||||||
const communityId = getIdFromString(pathSplit[2]);
|
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)
|
? getIdFromString(urlModId)
|
||||||
|
@ -985,40 +992,50 @@ export class Modlog extends Component<
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(client.getModlog(modlogForm));
|
let communityResponse: RequestState<GetCommunityResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
|
|
||||||
if (communityId) {
|
if (communityId) {
|
||||||
const communityForm: GetCommunity = {
|
const communityForm: GetCommunity = {
|
||||||
id: communityId,
|
id: communityId,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(client.getCommunity(communityForm));
|
|
||||||
} else {
|
communityResponse = await client.getCommunity(communityForm);
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let modUserResponse: RequestState<GetPersonDetailsResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
|
|
||||||
if (modId) {
|
if (modId) {
|
||||||
const getPersonForm: GetPersonDetails = {
|
const getPersonForm: GetPersonDetails = {
|
||||||
person_id: modId,
|
person_id: modId,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(client.getPersonDetails(getPersonForm));
|
modUserResponse = await client.getPersonDetails(getPersonForm);
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let userResponse: RequestState<GetPersonDetailsResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
const getPersonForm: GetPersonDetails = {
|
const getPersonForm: GetPersonDetails = {
|
||||||
person_id: userId,
|
person_id: userId,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(client.getPersonDetails(getPersonForm));
|
userResponse = await client.getPersonDetails(getPersonForm);
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return promises;
|
return {
|
||||||
|
res: await client.getModlog(modlogForm),
|
||||||
|
communityRes: communityResponse,
|
||||||
|
modUserResponse,
|
||||||
|
userResponse,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,7 @@ import {
|
||||||
DistinguishComment,
|
DistinguishComment,
|
||||||
EditComment,
|
EditComment,
|
||||||
EditPrivateMessage,
|
EditPrivateMessage,
|
||||||
GetPersonMentions,
|
|
||||||
GetPersonMentionsResponse,
|
GetPersonMentionsResponse,
|
||||||
GetPrivateMessages,
|
|
||||||
GetReplies,
|
|
||||||
GetRepliesResponse,
|
GetRepliesResponse,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
MarkCommentReplyAsRead,
|
MarkCommentReplyAsRead,
|
||||||
|
@ -53,6 +50,7 @@ import { UserService } from "../../services";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
|
RouteDataResponse,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
editCommentReply,
|
editCommentReply,
|
||||||
editMention,
|
editMention,
|
||||||
|
@ -92,6 +90,13 @@ enum ReplyEnum {
|
||||||
Mention,
|
Mention,
|
||||||
Message,
|
Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InboxData = RouteDataResponse<{
|
||||||
|
repliesRes: GetRepliesResponse;
|
||||||
|
mentionsRes: GetPersonMentionsResponse;
|
||||||
|
messagesRes: PrivateMessagesResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
type ReplyType = {
|
type ReplyType = {
|
||||||
id: number;
|
id: number;
|
||||||
type_: ReplyEnum;
|
type_: ReplyEnum;
|
||||||
|
@ -114,7 +119,7 @@ interface InboxState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Inbox extends Component<any, InboxState> {
|
export class Inbox extends Component<any, InboxState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<InboxData>(this.context);
|
||||||
state: InboxState = {
|
state: InboxState = {
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
messageType: MessageType.All,
|
messageType: MessageType.All,
|
||||||
|
@ -162,7 +167,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [repliesRes, mentionsRes, messagesRes] = this.isoData.routeData;
|
const { mentionsRes, messagesRes, repliesRes } = this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
|
@ -686,50 +691,40 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
await i.refetch();
|
await i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
client,
|
client,
|
||||||
auth,
|
auth,
|
||||||
}: InitialFetchRequest): Promise<any>[] {
|
}: InitialFetchRequest): Promise<InboxData> {
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
|
||||||
|
|
||||||
const sort: CommentSortType = "New";
|
const sort: CommentSortType = "New";
|
||||||
|
|
||||||
if (auth) {
|
return {
|
||||||
// It can be /u/me, or /username/1
|
mentionsRes: auth
|
||||||
const repliesForm: GetReplies = {
|
? await client.getPersonMentions({
|
||||||
sort,
|
sort,
|
||||||
unread_only: true,
|
unread_only: true,
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
auth,
|
auth,
|
||||||
};
|
})
|
||||||
promises.push(client.getReplies(repliesForm));
|
: { state: "empty" },
|
||||||
|
messagesRes: auth
|
||||||
const personMentionsForm: GetPersonMentions = {
|
? await client.getPrivateMessages({
|
||||||
sort,
|
unread_only: true,
|
||||||
unread_only: true,
|
page: 1,
|
||||||
page: 1,
|
limit: fetchLimit,
|
||||||
limit: fetchLimit,
|
auth,
|
||||||
auth,
|
})
|
||||||
};
|
: { state: "empty" },
|
||||||
promises.push(client.getPersonMentions(personMentionsForm));
|
repliesRes: auth
|
||||||
|
? await client.getReplies({
|
||||||
const privateMessagesForm: GetPrivateMessages = {
|
sort,
|
||||||
unread_only: true,
|
unread_only: true,
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
auth,
|
auth,
|
||||||
};
|
})
|
||||||
promises.push(client.getPrivateMessages(privateMessagesForm));
|
: { state: "empty" },
|
||||||
} else {
|
};
|
||||||
promises.push(
|
|
||||||
Promise.resolve({ state: "empty" }),
|
|
||||||
Promise.resolve({ state: "empty" }),
|
|
||||||
Promise.resolve({ state: "empty" })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return promises;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async refetch() {
|
async refetch() {
|
||||||
|
|
|
@ -54,6 +54,7 @@ import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
QueryParams,
|
QueryParams,
|
||||||
|
RouteDataResponse,
|
||||||
canMod,
|
canMod,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
editComment,
|
editComment,
|
||||||
|
@ -90,6 +91,10 @@ 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";
|
||||||
|
|
||||||
|
type ProfileData = RouteDataResponse<{
|
||||||
|
personResponse: GetPersonDetailsResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface ProfileState {
|
interface ProfileState {
|
||||||
personRes: RequestState<GetPersonDetailsResponse>;
|
personRes: RequestState<GetPersonDetailsResponse>;
|
||||||
personBlocked: boolean;
|
personBlocked: boolean;
|
||||||
|
@ -156,7 +161,7 @@ export class Profile extends Component<
|
||||||
RouteComponentProps<{ username: string }>,
|
RouteComponentProps<{ username: string }>,
|
||||||
ProfileState
|
ProfileState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<ProfileData>(this.context);
|
||||||
state: ProfileState = {
|
state: ProfileState = {
|
||||||
personRes: { state: "empty" },
|
personRes: { state: "empty" },
|
||||||
personBlocked: false,
|
personBlocked: false,
|
||||||
|
@ -208,7 +213,7 @@ export class Profile extends Component<
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
personRes: this.isoData.routeData[0],
|
personRes: this.isoData.routeData.personResponse,
|
||||||
isIsomorphic: true,
|
isIsomorphic: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -267,14 +272,12 @@ export class Profile extends Component<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
client,
|
client,
|
||||||
path,
|
path,
|
||||||
query: { page, sort, view: urlView },
|
query: { page, sort, view: urlView },
|
||||||
auth,
|
auth,
|
||||||
}: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<
|
}: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<ProfileData> {
|
||||||
RequestState<any>
|
|
||||||
>[] {
|
|
||||||
const pathSplit = path.split("/");
|
const pathSplit = path.split("/");
|
||||||
|
|
||||||
const username = pathSplit[2];
|
const username = pathSplit[2];
|
||||||
|
@ -289,7 +292,9 @@ export class Profile extends Component<
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
|
|
||||||
return [client.getPersonDetails(form)];
|
return {
|
||||||
|
personResponse: await client.getPersonDetails(form),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
ApproveRegistrationApplication,
|
ApproveRegistrationApplication,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
ListRegistrationApplications,
|
|
||||||
ListRegistrationApplicationsResponse,
|
ListRegistrationApplicationsResponse,
|
||||||
RegistrationApplicationView,
|
RegistrationApplicationView,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
|
@ -12,6 +11,7 @@ import { UserService } from "../../services";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
|
RouteDataResponse,
|
||||||
editRegistrationApplication,
|
editRegistrationApplication,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
myAuthRequired,
|
myAuthRequired,
|
||||||
|
@ -28,6 +28,10 @@ enum UnreadOrAll {
|
||||||
All,
|
All,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegistrationApplicationsData = RouteDataResponse<{
|
||||||
|
listRegistrationApplicationsResponse: ListRegistrationApplicationsResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface RegistrationApplicationsState {
|
interface RegistrationApplicationsState {
|
||||||
appsRes: RequestState<ListRegistrationApplicationsResponse>;
|
appsRes: RequestState<ListRegistrationApplicationsResponse>;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
|
@ -40,7 +44,7 @@ export class RegistrationApplications extends Component<
|
||||||
any,
|
any,
|
||||||
RegistrationApplicationsState
|
RegistrationApplicationsState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<RegistrationApplicationsData>(this.context);
|
||||||
state: RegistrationApplicationsState = {
|
state: RegistrationApplicationsState = {
|
||||||
appsRes: { state: "empty" },
|
appsRes: { state: "empty" },
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
|
@ -59,7 +63,7 @@ export class RegistrationApplications extends Component<
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
appsRes: this.isoData.routeData[0],
|
appsRes: this.isoData.routeData.listRegistrationApplicationsResponse,
|
||||||
isIsomorphic: true,
|
isIsomorphic: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -184,25 +188,20 @@ export class RegistrationApplications extends Component<
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
auth,
|
auth,
|
||||||
client,
|
client,
|
||||||
}: InitialFetchRequest): Promise<any>[] {
|
}: InitialFetchRequest): Promise<RegistrationApplicationsData> {
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
return {
|
||||||
|
listRegistrationApplicationsResponse: auth
|
||||||
if (auth) {
|
? await client.listRegistrationApplications({
|
||||||
const form: ListRegistrationApplications = {
|
unread_only: true,
|
||||||
unread_only: true,
|
page: 1,
|
||||||
page: 1,
|
limit: fetchLimit,
|
||||||
limit: fetchLimit,
|
auth: auth as string,
|
||||||
auth,
|
})
|
||||||
};
|
: { state: "empty" },
|
||||||
promises.push(client.listRegistrationApplications(form));
|
};
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
return promises;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async refetch() {
|
async refetch() {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { HttpService, UserService } from "../../services";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { RequestState } from "../../services/HttpService";
|
import { RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
|
RouteDataResponse,
|
||||||
amAdmin,
|
amAdmin,
|
||||||
editCommentReport,
|
editCommentReport,
|
||||||
editPostReport,
|
editPostReport,
|
||||||
|
@ -56,6 +57,12 @@ enum MessageEnum {
|
||||||
PrivateMessageReport,
|
PrivateMessageReport,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReportsData = RouteDataResponse<{
|
||||||
|
commentReportsRes: ListCommentReportsResponse;
|
||||||
|
postReportsRes: ListPostReportsResponse;
|
||||||
|
messageReportsRes: ListPrivateMessageReportsResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
type ItemType = {
|
type ItemType = {
|
||||||
id: number;
|
id: number;
|
||||||
type_: MessageEnum;
|
type_: MessageEnum;
|
||||||
|
@ -75,7 +82,7 @@ interface ReportsState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Reports extends Component<any, ReportsState> {
|
export class Reports extends Component<any, ReportsState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<ReportsData>(this.context);
|
||||||
state: ReportsState = {
|
state: ReportsState = {
|
||||||
commentReportsRes: { state: "empty" },
|
commentReportsRes: { state: "empty" },
|
||||||
postReportsRes: { state: "empty" },
|
postReportsRes: { state: "empty" },
|
||||||
|
@ -99,8 +106,9 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [commentReportsRes, postReportsRes, messageReportsRes] =
|
const { commentReportsRes, postReportsRes, messageReportsRes } =
|
||||||
this.isoData.routeData;
|
this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
commentReportsRes,
|
commentReportsRes,
|
||||||
|
@ -111,7 +119,7 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
if (amAdmin()) {
|
if (amAdmin()) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
messageReportsRes,
|
messageReportsRes: messageReportsRes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -481,55 +489,48 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
await i.refetch();
|
await i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
auth,
|
auth,
|
||||||
client,
|
client,
|
||||||
}: InitialFetchRequest): Promise<any>[] {
|
}: InitialFetchRequest): Promise<ReportsData> {
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
|
||||||
|
|
||||||
const unresolved_only = true;
|
const unresolved_only = true;
|
||||||
const page = 1;
|
const page = 1;
|
||||||
const limit = fetchLimit;
|
const limit = fetchLimit;
|
||||||
|
|
||||||
if (auth) {
|
const commentReportsForm: ListCommentReports = {
|
||||||
const commentReportsForm: ListCommentReports = {
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth: auth as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const postReportsForm: ListPostReports = {
|
||||||
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth: auth as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const data: ReportsData = {
|
||||||
|
commentReportsRes: await client.listCommentReports(commentReportsForm),
|
||||||
|
postReportsRes: await client.listPostReports(postReportsForm),
|
||||||
|
messageReportsRes: { state: "empty" },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (amAdmin()) {
|
||||||
|
const privateMessageReportsForm: ListPrivateMessageReports = {
|
||||||
unresolved_only,
|
unresolved_only,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
auth,
|
auth: auth as string,
|
||||||
};
|
};
|
||||||
promises.push(client.listCommentReports(commentReportsForm));
|
|
||||||
|
|
||||||
const postReportsForm: ListPostReports = {
|
data.messageReportsRes = await client.listPrivateMessageReports(
|
||||||
unresolved_only,
|
privateMessageReportsForm
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
auth,
|
|
||||||
};
|
|
||||||
promises.push(client.listPostReports(postReportsForm));
|
|
||||||
|
|
||||||
if (amAdmin()) {
|
|
||||||
const privateMessageReportsForm: ListPrivateMessageReports = {
|
|
||||||
unresolved_only,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
auth,
|
|
||||||
};
|
|
||||||
promises.push(
|
|
||||||
client.listPrivateMessageReports(privateMessageReportsForm)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
promises.push(
|
|
||||||
Promise.resolve({ state: "empty" }),
|
|
||||||
Promise.resolve({ state: "empty" }),
|
|
||||||
Promise.resolve({ state: "empty" })
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promises;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async refetch() {
|
async refetch() {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
import {
|
import {
|
||||||
CreatePost as CreatePostI,
|
CreatePost as CreatePostI,
|
||||||
GetCommunity,
|
GetCommunity,
|
||||||
|
GetCommunityResponse,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
ListCommunitiesResponse,
|
ListCommunitiesResponse,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
|
@ -17,6 +18,7 @@ import {
|
||||||
import {
|
import {
|
||||||
Choice,
|
Choice,
|
||||||
QueryParams,
|
QueryParams,
|
||||||
|
RouteDataResponse,
|
||||||
enableDownvotes,
|
enableDownvotes,
|
||||||
enableNsfw,
|
enableNsfw,
|
||||||
getIdFromString,
|
getIdFromString,
|
||||||
|
@ -32,6 +34,11 @@ export interface CreatePostProps {
|
||||||
communityId?: number;
|
communityId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreatePostData = RouteDataResponse<{
|
||||||
|
communityResponse: GetCommunityResponse;
|
||||||
|
initialCommunitiesRes: ListCommunitiesResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
function getCreatePostQueryParams() {
|
function getCreatePostQueryParams() {
|
||||||
return getQueryParams<CreatePostProps>({
|
return getQueryParams<CreatePostProps>({
|
||||||
communityId: getIdFromString,
|
communityId: getIdFromString,
|
||||||
|
@ -54,7 +61,7 @@ export class CreatePost extends Component<
|
||||||
RouteComponentProps<Record<string, never>>,
|
RouteComponentProps<Record<string, never>>,
|
||||||
CreatePostState
|
CreatePostState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<CreatePostData>(this.context);
|
||||||
state: CreatePostState = {
|
state: CreatePostState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
@ -71,7 +78,15 @@ export class CreatePost extends Component<
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [communityRes, listCommunitiesRes] = this.isoData.routeData;
|
const { communityResponse: communityRes, initialCommunitiesRes } =
|
||||||
|
this.isoData.routeData;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
loading: false,
|
||||||
|
initialCommunitiesRes,
|
||||||
|
isIsomorphic: true,
|
||||||
|
};
|
||||||
|
|
||||||
if (communityRes?.state === "success") {
|
if (communityRes?.state === "success") {
|
||||||
const communityChoice: Choice = {
|
const communityChoice: Choice = {
|
||||||
|
@ -84,13 +99,6 @@ export class CreatePost extends Component<
|
||||||
selectedCommunityChoice: communityChoice,
|
selectedCommunityChoice: communityChoice,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
|
||||||
...this.state,
|
|
||||||
loading: false,
|
|
||||||
initialCommunitiesRes: listCommunitiesRes,
|
|
||||||
isIsomorphic: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,14 +242,17 @@ export class CreatePost extends Component<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
client,
|
client,
|
||||||
query: { communityId },
|
query: { communityId },
|
||||||
auth,
|
auth,
|
||||||
}: InitialFetchRequest<QueryParams<CreatePostProps>>): Promise<
|
}: InitialFetchRequest<
|
||||||
RequestState<any>
|
QueryParams<CreatePostProps>
|
||||||
>[] {
|
>): Promise<CreatePostData> {
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
const data: CreatePostData = {
|
||||||
|
initialCommunitiesRes: await fetchCommunitiesForOptions(client),
|
||||||
|
communityResponse: { state: "empty" },
|
||||||
|
};
|
||||||
|
|
||||||
if (communityId) {
|
if (communityId) {
|
||||||
const form: GetCommunity = {
|
const form: GetCommunity = {
|
||||||
|
@ -249,13 +260,9 @@ export class CreatePost extends Component<
|
||||||
id: getIdFromString(communityId),
|
id: getIdFromString(communityId),
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(client.getCommunity(form));
|
data.communityResponse = await client.getCommunity(form);
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
promises.push(fetchCommunitiesForOptions(client));
|
return data;
|
||||||
|
|
||||||
return promises;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ import {
|
||||||
isImage,
|
isImage,
|
||||||
myAuth,
|
myAuth,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
|
RouteDataResponse,
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
|
@ -93,6 +94,11 @@ import { PostListing } from "./post-listing";
|
||||||
|
|
||||||
const commentsShownInterval = 15;
|
const commentsShownInterval = 15;
|
||||||
|
|
||||||
|
type PostData = RouteDataResponse<{
|
||||||
|
postRes: GetPostResponse;
|
||||||
|
commentsRes: GetCommentsResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface PostState {
|
interface PostState {
|
||||||
postId?: number;
|
postId?: number;
|
||||||
commentId?: number;
|
commentId?: number;
|
||||||
|
@ -110,7 +116,7 @@ interface PostState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Post extends Component<any, PostState> {
|
export class Post extends Component<any, PostState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<PostData>(this.context);
|
||||||
private commentScrollDebounced: () => void;
|
private commentScrollDebounced: () => void;
|
||||||
state: PostState = {
|
state: PostState = {
|
||||||
postRes: { state: "empty" },
|
postRes: { state: "empty" },
|
||||||
|
@ -169,7 +175,7 @@ export class Post extends Component<any, PostState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [postRes, commentsRes] = this.isoData.routeData;
|
const { commentsRes, postRes } = this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
|
@ -220,13 +226,12 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
auth,
|
|
||||||
client,
|
client,
|
||||||
path,
|
path,
|
||||||
}: InitialFetchRequest): Promise<any>[] {
|
auth,
|
||||||
|
}: InitialFetchRequest): Promise<PostData> {
|
||||||
const pathSplit = path.split("/");
|
const pathSplit = path.split("/");
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
|
||||||
|
|
||||||
const pathType = pathSplit.at(1);
|
const pathType = pathSplit.at(1);
|
||||||
const id = pathSplit.at(2) ? Number(pathSplit.at(2)) : undefined;
|
const id = pathSplit.at(2) ? Number(pathSplit.at(2)) : undefined;
|
||||||
|
@ -252,10 +257,10 @@ export class Post extends Component<any, PostState> {
|
||||||
commentsForm.parent_id = id;
|
commentsForm.parent_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
promises.push(client.getPost(postForm));
|
return {
|
||||||
promises.push(client.getComments(commentsForm));
|
postRes: await client.getPost(postForm),
|
||||||
|
commentsRes: await client.getComments(commentsForm),
|
||||||
return promises;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
|
RouteDataResponse,
|
||||||
getRecipientIdFromProps,
|
getRecipientIdFromProps,
|
||||||
myAuth,
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
|
@ -19,6 +20,10 @@ 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";
|
||||||
|
|
||||||
|
type CreatePrivateMessageData = RouteDataResponse<{
|
||||||
|
recipientDetailsResponse: GetPersonDetailsResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
interface CreatePrivateMessageState {
|
interface CreatePrivateMessageState {
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
recipientRes: RequestState<GetPersonDetailsResponse>;
|
recipientRes: RequestState<GetPersonDetailsResponse>;
|
||||||
|
@ -30,7 +35,7 @@ export class CreatePrivateMessage extends Component<
|
||||||
any,
|
any,
|
||||||
CreatePrivateMessageState
|
CreatePrivateMessageState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<CreatePrivateMessageData>(this.context);
|
||||||
state: CreatePrivateMessageState = {
|
state: CreatePrivateMessageState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
recipientRes: { state: "empty" },
|
recipientRes: { state: "empty" },
|
||||||
|
@ -47,7 +52,7 @@ export class CreatePrivateMessage extends Component<
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
recipientRes: this.isoData.routeData[0],
|
recipientRes: this.isoData.routeData.recipientDetailsResponse,
|
||||||
isIsomorphic: true,
|
isIsomorphic: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -59,6 +64,25 @@ export class CreatePrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async fetchInitialData({
|
||||||
|
client,
|
||||||
|
path,
|
||||||
|
auth,
|
||||||
|
}: InitialFetchRequest): Promise<CreatePrivateMessageData> {
|
||||||
|
const person_id = Number(path.split("/").pop());
|
||||||
|
|
||||||
|
const form: GetPersonDetails = {
|
||||||
|
person_id,
|
||||||
|
sort: "New",
|
||||||
|
saved_only: false,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
recipientDetailsResponse: await client.getPersonDetails(form),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async fetchPersonDetails() {
|
async fetchPersonDetails() {
|
||||||
this.setState({
|
this.setState({
|
||||||
recipientRes: { state: "loading" },
|
recipientRes: { state: "loading" },
|
||||||
|
@ -74,19 +98,6 @@ export class CreatePrivateMessage extends Component<
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData(
|
|
||||||
req: InitialFetchRequest
|
|
||||||
): Promise<RequestState<any>>[] {
|
|
||||||
const person_id = Number(req.path.split("/").pop());
|
|
||||||
const form: GetPersonDetails = {
|
|
||||||
person_id,
|
|
||||||
sort: "New",
|
|
||||||
saved_only: false,
|
|
||||||
auth: req.auth,
|
|
||||||
};
|
|
||||||
return [req.client.getPersonDetails(form)];
|
|
||||||
}
|
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
if (this.state.recipientRes.state == "success") {
|
if (this.state.recipientRes.state == "success") {
|
||||||
const name_ = this.state.recipientRes.data.person_view.person.name;
|
const name_ = this.state.recipientRes.data.person_view.person.name;
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { HttpService, RequestState } from "../services/HttpService";
|
||||||
import {
|
import {
|
||||||
Choice,
|
Choice,
|
||||||
QueryParams,
|
QueryParams,
|
||||||
|
RouteDataResponse,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
communityToChoice,
|
communityToChoice,
|
||||||
|
@ -70,6 +71,14 @@ interface SearchProps {
|
||||||
page: number;
|
page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchData = RouteDataResponse<{
|
||||||
|
communityResponse: GetCommunityResponse;
|
||||||
|
listCommunitiesResponse: ListCommunitiesResponse;
|
||||||
|
creatorDetailsResponse: GetPersonDetailsResponse;
|
||||||
|
searchResponse: SearchResponse;
|
||||||
|
resolveObjectResponse: ResolveObjectResponse;
|
||||||
|
}>;
|
||||||
|
|
||||||
type FilterType = "creator" | "community";
|
type FilterType = "creator" | "community";
|
||||||
|
|
||||||
interface SearchState {
|
interface SearchState {
|
||||||
|
@ -228,7 +237,8 @@ function getListing(
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Search extends Component<any, SearchState> {
|
export class Search extends Component<any, SearchState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData<SearchData>(this.context);
|
||||||
|
|
||||||
state: SearchState = {
|
state: SearchState = {
|
||||||
resolveObjectRes: { state: "empty" },
|
resolveObjectRes: { state: "empty" },
|
||||||
creatorDetailsRes: { state: "empty" },
|
creatorDetailsRes: { state: "empty" },
|
||||||
|
@ -262,42 +272,63 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (FirstLoadService.isFirstLoad) {
|
if (FirstLoadService.isFirstLoad) {
|
||||||
const [
|
const {
|
||||||
communityRes,
|
communityResponse: communityRes,
|
||||||
communitiesRes,
|
creatorDetailsResponse: creatorDetailsRes,
|
||||||
creatorDetailsRes,
|
listCommunitiesResponse: communitiesRes,
|
||||||
searchRes,
|
resolveObjectResponse: resolveObjectRes,
|
||||||
resolveObjectRes,
|
searchResponse: searchRes,
|
||||||
] = this.isoData.routeData;
|
} = this.isoData.routeData;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
communitiesRes,
|
|
||||||
communityRes,
|
|
||||||
creatorDetailsRes,
|
|
||||||
creatorSearchOptions:
|
|
||||||
creatorDetailsRes.state == "success"
|
|
||||||
? [personToChoice(creatorDetailsRes.data.person_view)]
|
|
||||||
: [],
|
|
||||||
isIsomorphic: true,
|
isIsomorphic: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (communityRes.state === "success") {
|
if (creatorDetailsRes?.state === "success") {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
communitySearchOptions: [
|
creatorSearchOptions:
|
||||||
communityToChoice(communityRes.data.community_view),
|
creatorDetailsRes?.state === "success"
|
||||||
],
|
? [personToChoice(creatorDetailsRes.data.person_view)]
|
||||||
|
: [],
|
||||||
|
creatorDetailsRes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (q) {
|
if (communitiesRes?.state === "success") {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
searchRes,
|
communitiesRes,
|
||||||
resolveObjectRes,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (communityRes?.state === "success") {
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
communityRes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q !== "") {
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (searchRes?.state === "success") {
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
searchRes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolveObjectRes?.state === "success") {
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
resolveObjectRes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,23 +359,25 @@ export class Search extends Component<any, SearchState> {
|
||||||
saveScrollPosition(this.context);
|
saveScrollPosition(this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData({
|
static async fetchInitialData({
|
||||||
client,
|
client,
|
||||||
auth,
|
auth,
|
||||||
query: { communityId, creatorId, q, type, sort, listingType, page },
|
query: { communityId, creatorId, q, type, sort, listingType, page },
|
||||||
}: InitialFetchRequest<QueryParams<SearchProps>>): Promise<
|
}: InitialFetchRequest<QueryParams<SearchProps>>): Promise<SearchData> {
|
||||||
RequestState<any>
|
|
||||||
>[] {
|
|
||||||
const promises: Promise<RequestState<any>>[] = [];
|
|
||||||
|
|
||||||
const community_id = getIdFromString(communityId);
|
const community_id = getIdFromString(communityId);
|
||||||
|
let communityResponse: RequestState<GetCommunityResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
|
let listCommunitiesResponse: RequestState<ListCommunitiesResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
if (community_id) {
|
if (community_id) {
|
||||||
const getCommunityForm: GetCommunity = {
|
const getCommunityForm: GetCommunity = {
|
||||||
id: community_id,
|
id: community_id,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(client.getCommunity(getCommunityForm));
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
communityResponse = await client.getCommunity(getCommunityForm);
|
||||||
} else {
|
} else {
|
||||||
const listCommunitiesForm: ListCommunities = {
|
const listCommunitiesForm: ListCommunities = {
|
||||||
type_: defaultListingType,
|
type_: defaultListingType,
|
||||||
|
@ -352,23 +385,32 @@ export class Search extends Component<any, SearchState> {
|
||||||
limit: fetchLimit,
|
limit: fetchLimit,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
promises.push(client.listCommunities(listCommunitiesForm));
|
listCommunitiesResponse = await client.listCommunities(
|
||||||
|
listCommunitiesForm
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const creator_id = getIdFromString(creatorId);
|
const creator_id = getIdFromString(creatorId);
|
||||||
|
let creatorDetailsResponse: RequestState<GetPersonDetailsResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
if (creator_id) {
|
if (creator_id) {
|
||||||
const getCreatorForm: GetPersonDetails = {
|
const getCreatorForm: GetPersonDetails = {
|
||||||
person_id: creator_id,
|
person_id: creator_id,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(client.getPersonDetails(getCreatorForm));
|
|
||||||
} else {
|
creatorDetailsResponse = await client.getPersonDetails(getCreatorForm);
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = getSearchQueryFromQuery(q);
|
const query = getSearchQueryFromQuery(q);
|
||||||
|
|
||||||
|
let searchResponse: RequestState<SearchResponse> = { state: "empty" };
|
||||||
|
let resolveObjectResponse: RequestState<ResolveObjectResponse> = {
|
||||||
|
state: "empty",
|
||||||
|
};
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
const form: SearchForm = {
|
const form: SearchForm = {
|
||||||
q: query,
|
q: query,
|
||||||
|
@ -383,21 +425,24 @@ export class Search extends Component<any, SearchState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (query !== "") {
|
if (query !== "") {
|
||||||
promises.push(client.search(form));
|
searchResponse = await client.search(form);
|
||||||
if (auth) {
|
if (auth) {
|
||||||
const resolveObjectForm: ResolveObject = {
|
const resolveObjectForm: ResolveObject = {
|
||||||
q: query,
|
q: query,
|
||||||
auth,
|
auth,
|
||||||
};
|
};
|
||||||
promises.push(client.resolveObject(resolveObjectForm));
|
resolveObjectResponse = await client.resolveObject(resolveObjectForm);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
promises.push(Promise.resolve({ state: "empty" }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return promises;
|
return {
|
||||||
|
communityResponse,
|
||||||
|
creatorDetailsResponse,
|
||||||
|
listCommunitiesResponse,
|
||||||
|
resolveObjectResponse,
|
||||||
|
searchResponse,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
@ -463,7 +508,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
minLength={1}
|
minLength={1}
|
||||||
/>
|
/>
|
||||||
<button type="submit" className="btn btn-secondary mr-2 mb-2">
|
<button type="submit" className="btn btn-secondary mr-2 mb-2">
|
||||||
{this.state.searchRes.state == "loading" ? (
|
{this.state.searchRes.state === "loading" ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<span>{i18n.t("search")}</span>
|
<span>{i18n.t("search")}</span>
|
||||||
|
|
|
@ -6,15 +6,17 @@ import { ErrorPageData } from "./utils";
|
||||||
/**
|
/**
|
||||||
* This contains serialized data, it needs to be deserialized before use.
|
* This contains serialized data, it needs to be deserialized before use.
|
||||||
*/
|
*/
|
||||||
export interface IsoData {
|
export interface IsoData<T extends RouteData = any> {
|
||||||
path: string;
|
path: string;
|
||||||
routeData: RequestState<any>[];
|
routeData: T;
|
||||||
site_res: GetSiteResponse;
|
site_res: GetSiteResponse;
|
||||||
errorPageData?: ErrorPageData;
|
errorPageData?: ErrorPageData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IsoDataOptionalSite = Partial<IsoData> &
|
export type IsoDataOptionalSite<T extends RouteData = any> = Partial<
|
||||||
Pick<IsoData, Exclude<keyof IsoData, "site_res">>;
|
IsoData<T>
|
||||||
|
> &
|
||||||
|
Pick<IsoData<T>, Exclude<keyof IsoData<T>, "site_res">>;
|
||||||
|
|
||||||
export interface ILemmyConfig {
|
export interface ILemmyConfig {
|
||||||
wsHost?: string;
|
wsHost?: string;
|
||||||
|
@ -80,3 +82,5 @@ export interface CommentNodeI {
|
||||||
children: Array<CommentNodeI>;
|
children: Array<CommentNodeI>;
|
||||||
depth: number;
|
depth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RouteData = Record<string, RequestState<any>>;
|
||||||
|
|
|
@ -21,15 +21,13 @@ import { CreatePost } from "./components/post/create-post";
|
||||||
import { Post } from "./components/post/post";
|
import { Post } from "./components/post/post";
|
||||||
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
|
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
|
||||||
import { Search } from "./components/search";
|
import { Search } from "./components/search";
|
||||||
import { InitialFetchRequest } from "./interfaces";
|
import { InitialFetchRequest, RouteData } from "./interfaces";
|
||||||
import { RequestState } from "./services/HttpService";
|
|
||||||
|
|
||||||
interface IRoutePropsWithFetch extends IRouteProps {
|
interface IRoutePropsWithFetch<T extends RouteData> extends IRouteProps {
|
||||||
// TODO Make sure this one is good.
|
fetchInitialData?(req: InitialFetchRequest): Promise<T>;
|
||||||
fetchInitialData?(req: InitialFetchRequest): Promise<RequestState<any>>[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const routes: IRoutePropsWithFetch[] = [
|
export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
|
||||||
{
|
{
|
||||||
path: `/`,
|
path: `/`,
|
||||||
component: Home,
|
component: Home,
|
||||||
|
|
|
@ -11,7 +11,7 @@ type LoadingRequestState = {
|
||||||
state: "loading";
|
state: "loading";
|
||||||
};
|
};
|
||||||
|
|
||||||
type FailedRequestState = {
|
export type FailedRequestState = {
|
||||||
state: "failed";
|
state: "failed";
|
||||||
msg: string;
|
msg: string;
|
||||||
};
|
};
|
||||||
|
@ -58,7 +58,7 @@ class WrappedLemmyHttpClient {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: res,
|
data: res,
|
||||||
state: "success",
|
state: !(res === undefined || res === null) ? "success" : "empty",
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`API error: ${error}`);
|
console.error(`API error: ${error}`);
|
||||||
|
|
|
@ -43,8 +43,15 @@ import tippy from "tippy.js";
|
||||||
import Toastify from "toastify-js";
|
import Toastify from "toastify-js";
|
||||||
import { getHttpBase } from "./env";
|
import { getHttpBase } from "./env";
|
||||||
import { i18n } from "./i18next";
|
import { i18n } from "./i18next";
|
||||||
import { CommentNodeI, DataType, IsoData, VoteType } from "./interfaces";
|
import {
|
||||||
|
CommentNodeI,
|
||||||
|
DataType,
|
||||||
|
IsoData,
|
||||||
|
RouteData,
|
||||||
|
VoteType,
|
||||||
|
} from "./interfaces";
|
||||||
import { HttpService, UserService } from "./services";
|
import { HttpService, UserService } from "./services";
|
||||||
|
import { RequestState } from "./services/HttpService";
|
||||||
|
|
||||||
let Tribute: any;
|
let Tribute: any;
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -1150,7 +1157,7 @@ export function isBrowser() {
|
||||||
return typeof window !== "undefined";
|
return typeof window !== "undefined";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setIsoData(context: any): IsoData {
|
export function setIsoData<T extends RouteData>(context: any): IsoData<T> {
|
||||||
// If its the browser, you need to deserialize the data from the window
|
// If its the browser, you need to deserialize the data from the window
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
return window.isoData;
|
return window.isoData;
|
||||||
|
@ -1480,6 +1487,10 @@ export function newVote(voteType: VoteType, myVote?: number): number {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RouteDataResponse<T extends Record<string, any>> = {
|
||||||
|
[K in keyof T]: RequestState<T[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
function sleep(millis: number): Promise<void> {
|
function sleep(millis: number): Promise<void> {
|
||||||
return new Promise(resolve => setTimeout(resolve, millis));
|
return new Promise(resolve => setTimeout(resolve, millis));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue