Merge branch 'main' into default-pfp

This commit is contained in:
SleeplessOne1917 2023-06-17 02:44:08 +00:00 committed by GitHub
commit 1676afbf33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 526 additions and 374 deletions

View file

@ -19,9 +19,16 @@
"themes:watch": "sass --watch src/assets/css/themes/:src/assets/css/themes"
},
"lint-staged": {
"*.{ts,tsx,js}": ["prettier --write", "eslint --fix"],
"*.{css, scss}": ["prettier --write"],
"package.json": ["sortpack"]
"*.{ts,tsx,js}": [
"prettier --write",
"eslint --fix"
],
"*.{css, scss}": [
"prettier --write"
],
"package.json": [
"sortpack"
]
},
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.21.0",

View file

@ -17,9 +17,10 @@ import {
ILemmyConfig,
InitialFetchRequest,
IsoDataOptionalSite,
RouteData,
} from "../shared/interfaces";
import { routes } from "../shared/routes";
import { RequestState, wrapClient } from "../shared/services/HttpService";
import { FailedRequestState, wrapClient } from "../shared/services/HttpService";
import {
ErrorPageData,
favIconPngUrl,
@ -136,7 +137,7 @@ server.get("/*", async (req, res) => {
// 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
let site: GetSiteResponse | undefined = undefined;
const routeData: RequestState<any>[] = [];
let routeData: RouteData = {};
let errorPageData: ErrorPageData | undefined = undefined;
let try_site = await client.getSite(getSiteForm);
if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
@ -160,7 +161,7 @@ server.get("/*", async (req, res) => {
return res.redirect("/setup");
}
if (site) {
if (site && activeRoute?.fetchInitialData) {
const initialFetchReq: InitialFetchRequest = {
client,
auth,
@ -169,26 +170,23 @@ server.get("/*", async (req, res) => {
site,
};
if (activeRoute?.fetchInitialData) {
routeData.push(
...(await Promise.all([
...activeRoute.fetchInitialData(initialFetchReq),
]))
);
}
routeData = await activeRoute.fetchInitialData(initialFetchReq);
}
} else if (try_site.state === "failed") {
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
if (routeData[0] && routeData[0].state === "failed") {
const error = routeData[0].msg;
console.error(error);
if (error === "instance_is_private") {
if (error) {
console.error(error.msg);
if (error.msg === "instance_is_private") {
return res.redirect(`/signup`);
} else {
errorPageData = getErrorPageData(new Error(error), site);
errorPageData = getErrorPageData(new Error(error.msg), site);
}
}

View file

@ -22,7 +22,7 @@ export class PictrsImage extends Component<PictrsImageProps, any> {
render() {
return (
<picture>
<picture className="d-inline-block overflow-hidden">
<source srcSet={this.src("webp")} type="image/webp" />
<source srcSet={this.props.src} />
<source srcSet={this.src("jpg")} type="image/jpeg" />

View file

@ -12,6 +12,7 @@ import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
QueryParams,
RouteDataResponse,
editCommunity,
getPageFromString,
getQueryParams,
@ -30,6 +31,10 @@ import { CommunityLink } from "./community-link";
const communityLimit = 50;
type CommunitiesData = RouteDataResponse<{
listCommunitiesResponse: ListCommunitiesResponse;
}>;
interface CommunitiesState {
listCommunitiesResponse: RequestState<ListCommunitiesResponse>;
siteRes: GetSiteResponse;
@ -47,7 +52,7 @@ function getListingTypeFromQuery(listingType?: string): ListingType {
}
export class Communities extends Component<any, CommunitiesState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<CommunitiesData>(this.context);
state: CommunitiesState = {
listCommunitiesResponse: { state: "empty" },
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
if (FirstLoadService.isFirstLoad) {
const { listCommunitiesResponse } = this.isoData.routeData;
this.state = {
...this.state,
listCommunitiesResponse: this.isoData.routeData[0],
listCommunitiesResponse,
isIsomorphic: true,
};
}
@ -274,13 +281,13 @@ export class Communities extends Component<any, CommunitiesState> {
i.context.router.history.push(`/search?q=${searchParamEncoded}`);
}
static fetchInitialData({
static async fetchInitialData({
query: { listingType, page },
client,
auth,
}: InitialFetchRequest<QueryParams<CommunitiesProps>>): Promise<
RequestState<any>
>[] {
}: InitialFetchRequest<
QueryParams<CommunitiesProps>
>): Promise<CommunitiesData> {
const listCommunitiesForm: ListCommunities = {
type_: getListingTypeFromQuery(listingType),
sort: "TopMonth",
@ -289,7 +296,11 @@ export class Communities extends Component<any, CommunitiesState> {
auth: auth,
};
return [client.listCommunities(listCommunitiesForm)];
return {
listCommunitiesResponse: await client.listCommunities(
listCommunitiesForm
),
};
}
getCommunitiesQueryParams() {

View file

@ -63,6 +63,7 @@ import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
QueryParams,
RouteDataResponse,
commentsToFlatNodes,
communityRSSUrl,
editComment,
@ -100,6 +101,12 @@ import { SiteSidebar } from "../home/site-sidebar";
import { PostListings } from "../post/post-listings";
import { CommunityLink } from "./community-link";
type CommunityData = RouteDataResponse<{
communityRes: GetCommunityResponse;
postsRes: GetPostsResponse;
commentsRes: GetCommentsResponse;
}>;
interface State {
communityRes: RequestState<GetCommunityResponse>;
postsRes: RequestState<GetPostsResponse>;
@ -140,7 +147,7 @@ export class Community extends Component<
RouteComponentProps<{ name: string }>,
State
> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<CommunityData>(this.context);
state: State = {
communityRes: { state: "empty" },
postsRes: { state: "empty" },
@ -194,13 +201,14 @@ export class Community extends Component<
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
const [communityRes, postsRes, commentsRes] = this.isoData.routeData;
const { communityRes, commentsRes, postsRes } = this.isoData.routeData;
this.state = {
...this.state,
isIsomorphic: true,
commentsRes,
communityRes,
postsRes,
commentsRes,
isIsomorphic: true,
};
}
}
@ -227,23 +235,21 @@ export class Community extends Component<
saveScrollPosition(this.context);
}
static fetchInitialData({
static async fetchInitialData({
client,
path,
query: { dataType: urlDataType, page: urlPage, sort: urlSort },
auth,
}: InitialFetchRequest<QueryParams<CommunityProps>>): Promise<
RequestState<any>
>[] {
Promise<CommunityData>
> {
const pathSplit = path.split("/");
const promises: Promise<RequestState<any>>[] = [];
const communityName = pathSplit[2];
const communityForm: GetCommunity = {
name: communityName,
auth,
};
promises.push(client.getCommunity(communityForm));
const dataType = getDataTypeFromQuery(urlDataType);
@ -251,6 +257,11 @@ export class Community extends Component<
const page = getPageFromString(urlPage);
let postsResponse: RequestState<GetPostsResponse> = { state: "empty" };
let commentsResponse: RequestState<GetCommentsResponse> = {
state: "empty",
};
if (dataType === DataType.Post) {
const getPostsForm: GetPosts = {
community_name: communityName,
@ -261,8 +272,8 @@ export class Community extends Component<
saved_only: false,
auth,
};
promises.push(client.getPosts(getPostsForm));
promises.push(Promise.resolve({ state: "empty" }));
postsResponse = await client.getPosts(getPostsForm);
} else {
const getCommentsForm: GetComments = {
community_name: communityName,
@ -273,11 +284,15 @@ export class Community extends Component<
saved_only: false,
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 {

View file

@ -14,6 +14,7 @@ import { InitialFetchRequest } from "../../interfaces";
import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
RouteDataResponse,
capitalizeFirstLetter,
fetchThemeList,
myAuthRequired,
@ -32,6 +33,11 @@ import RateLimitForm from "./rate-limit-form";
import { SiteForm } from "./site-form";
import { TaglineForm } from "./tagline-form";
type AdminSettingsData = RouteDataResponse<{
bannedRes: BannedPersonsResponse;
instancesRes: GetFederatedInstancesResponse;
}>;
interface AdminSettingsState {
siteRes: GetSiteResponse;
banned: PersonView[];
@ -46,7 +52,7 @@ interface AdminSettingsState {
}
export class AdminSettings extends Component<any, AdminSettingsState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<AdminSettingsData>(this.context);
state: AdminSettingsState = {
siteRes: this.isoData.site_res,
banned: [],
@ -70,7 +76,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
const [bannedRes, instancesRes] = this.isoData.routeData;
const { bannedRes, instancesRes } = this.isoData.routeData;
this.state = {
...this.state,
bannedRes,
@ -80,47 +87,18 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
}
}
async fetchData() {
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({
static async fetchInitialData({
auth,
client,
}: InitialFetchRequest): Promise<any>[] {
const promises: Promise<RequestState<any>>[] = [];
if (auth) {
promises.push(client.getBannedPersons({ auth }));
promises.push(client.getFederatedInstances({ auth }));
} else {
promises.push(
Promise.resolve({ state: "empty" }),
Promise.resolve({ state: "empty" })
);
}
return promises;
}: InitialFetchRequest): Promise<AdminSettingsData> {
return {
bannedRes: await client.getBannedPersons({
auth: auth as string,
}),
instancesRes: await client.getFederatedInstances({
auth: auth as string,
}),
};
}
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() {
return (
<>

View file

@ -77,6 +77,7 @@ import {
QueryParams,
relTags,
restoreScrollPosition,
RouteDataResponse,
saveScrollPosition,
setIsoData,
setupTippy,
@ -117,6 +118,45 @@ interface HomeProps {
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 {
return type ? DataType[type] : DataType.Post;
}
@ -176,7 +216,7 @@ const LinkButton = ({
);
export class Home extends Component<any, HomeState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<HomeData>(this.context);
state: HomeState = {
postsRes: { 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
if (FirstLoadService.isFirstLoad) {
const [postsRes, commentsRes, trendingCommunitiesRes] =
const { trendingCommunitiesRes, commentsRes, postsRes } =
this.isoData.routeData;
this.state = {
...this.state,
postsRes,
commentsRes,
trendingCommunitiesRes,
commentsRes,
postsRes,
tagline: getRandomFromList(this.state?.siteRes?.taglines ?? [])
?.content,
isIsomorphic: true,
@ -244,7 +284,12 @@ export class Home extends Component<any, HomeState> {
}
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()]);
}
@ -255,13 +300,11 @@ export class Home extends Component<any, HomeState> {
saveScrollPosition(this.context);
}
static fetchInitialData({
static async fetchInitialData({
client,
auth,
query: { dataType: urlDataType, listingType, page: urlPage, sort: urlSort },
}: InitialFetchRequest<QueryParams<HomeProps>>): Promise<
RequestState<any>
>[] {
}: InitialFetchRequest<QueryParams<HomeProps>>): Promise<HomeData> {
const dataType = getDataTypeFromQuery(urlDataType);
// 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 promises: Promise<RequestState<any>>[] = [];
let postsRes: RequestState<GetPostsResponse> = { state: "empty" };
let commentsRes: RequestState<GetCommentsResponse> = {
state: "empty",
};
if (dataType === DataType.Post) {
const getPostsForm: GetPosts = {
@ -282,8 +328,7 @@ export class Home extends Component<any, HomeState> {
auth,
};
promises.push(client.getPosts(getPostsForm));
promises.push(Promise.resolve({ state: "empty" }));
postsRes = await client.getPosts(getPostsForm);
} else {
const getCommentsForm: GetComments = {
page,
@ -293,8 +338,8 @@ export class Home extends Component<any, HomeState> {
saved_only: false,
auth,
};
promises.push(Promise.resolve({ state: "empty" }));
promises.push(client.getComments(getCommentsForm));
commentsRes = await client.getComments(getCommentsForm);
}
const trendingCommunitiesForm: ListCommunities = {
@ -303,9 +348,14 @@ export class Home extends Component<any, HomeState> {
limit: trendingFetchLimit,
auth,
};
promises.push(client.listCommunities(trendingCommunitiesForm));
return promises;
return {
trendingCommunitiesRes: await client.listCommunities(
trendingCommunitiesForm
),
commentsRes,
postsRes,
};
}
get documentTitle(): string {
@ -340,7 +390,7 @@ export class Home extends Component<any, HomeState> {
></div>
)}
<div className="d-block d-md-none">{this.mobileView}</div>
{this.posts()}
{this.posts}
</main>
<aside className="d-none d-md-block col-md-4">
{this.mySidebar}
@ -552,7 +602,7 @@ export class Home extends Component<any, HomeState> {
await this.fetchData();
}
posts() {
get posts() {
const { page } = getHomeQueryParams();
return (
@ -571,7 +621,7 @@ export class Home extends Component<any, HomeState> {
const siteRes = this.state.siteRes;
if (dataType === DataType.Post) {
switch (this.state.postsRes?.state) {
switch (this.state.postsRes.state) {
case "loading":
return (
<h5>
@ -677,44 +727,11 @@ export class Home extends Component<any, HomeState> {
<span className="mr-2">
<SortSelect sort={sort} onChange={this.handleSortChange} />
</span>
{this.getRss(listingType)}
{getRss(listingType)}
</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() {
this.setState({ trendingCommunitiesRes: { state: "loading" } });
this.setState({

View file

@ -8,10 +8,14 @@ import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import { relTags, setIsoData } from "../../utils";
import { RouteDataResponse, relTags, setIsoData } from "../../utils";
import { HtmlTags } from "../common/html-tags";
import { Spinner } from "../common/icon";
type InstancesData = RouteDataResponse<{
federatedInstancesResponse: GetFederatedInstancesResponse;
}>;
interface InstancesState {
instancesRes: RequestState<GetFederatedInstancesResponse>;
siteRes: GetSiteResponse;
@ -19,7 +23,7 @@ interface InstancesState {
}
export class Instances extends Component<any, InstancesState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<InstancesData>(this.context);
state: InstancesState = {
instancesRes: { state: "empty" },
siteRes: this.isoData.site_res,
@ -33,7 +37,7 @@ export class Instances extends Component<any, InstancesState> {
if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
instancesRes: this.isoData.routeData[0],
instancesRes: this.isoData.routeData.federatedInstancesResponse,
isIsomorphic: true,
};
}
@ -55,10 +59,12 @@ export class Instances extends Component<any, InstancesState> {
});
}
static fetchInitialData(
req: InitialFetchRequest
): Promise<RequestState<any>>[] {
return [req.client.getFederatedInstances({})];
static async fetchInitialData({
client,
}: InitialFetchRequest): Promise<InstancesData> {
return {
federatedInstancesResponse: await client.getFederatedInstances({}),
};
}
get documentTitle(): string {

View file

@ -13,6 +13,7 @@ import {
GetModlog,
GetModlogResponse,
GetPersonDetails,
GetPersonDetailsResponse,
ModAddCommunityView,
ModAddView,
ModBanFromCommunityView,
@ -34,6 +35,7 @@ import { HttpService, RequestState } from "../services/HttpService";
import {
Choice,
QueryParams,
RouteDataResponse,
amAdmin,
amMod,
debounce,
@ -74,6 +76,13 @@ type View =
| AdminPurgePostView
| AdminPurgeCommentView;
type ModlogData = RouteDataResponse<{
res: GetModlogResponse;
communityRes: GetCommunityResponse;
modUserResponse: GetPersonDetailsResponse;
userResponse: GetPersonDetailsResponse;
}>;
interface ModlogType {
id: number;
type_: ModlogActionType;
@ -631,7 +640,7 @@ export class Modlog extends Component<
RouteComponentProps<{ communityId?: string }>,
ModlogState
> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<ModlogData>(this.context);
state: ModlogState = {
res: { state: "empty" },
@ -653,25 +662,26 @@ export class Modlog extends Component<
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
const [res, communityRes, filteredModRes, filteredUserRes] =
const { res, communityRes, modUserResponse, userResponse } =
this.isoData.routeData;
this.state = {
...this.state,
res,
communityRes,
};
if (filteredModRes.state === "success") {
if (modUserResponse.state === "success") {
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,
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,
path,
query: { modId: urlModId, page, userId: urlUserId, actionType },
auth,
site,
}: InitialFetchRequest<QueryParams<ModlogProps>>): Promise<
RequestState<any>
>[] {
}: InitialFetchRequest<QueryParams<ModlogProps>>): Promise<ModlogData> {
const pathSplit = path.split("/");
const promises: Promise<RequestState<any>>[] = [];
const communityId = getIdFromString(pathSplit[2]);
const modId = !site.site_view.local_site.hide_modlog_mod_names
? getIdFromString(urlModId)
@ -985,40 +992,50 @@ export class Modlog extends Component<
auth,
};
promises.push(client.getModlog(modlogForm));
let communityResponse: RequestState<GetCommunityResponse> = {
state: "empty",
};
if (communityId) {
const communityForm: GetCommunity = {
id: communityId,
auth,
};
promises.push(client.getCommunity(communityForm));
} else {
promises.push(Promise.resolve({ state: "empty" }));
communityResponse = await client.getCommunity(communityForm);
}
let modUserResponse: RequestState<GetPersonDetailsResponse> = {
state: "empty",
};
if (modId) {
const getPersonForm: GetPersonDetails = {
person_id: modId,
auth,
};
promises.push(client.getPersonDetails(getPersonForm));
} else {
promises.push(Promise.resolve({ state: "empty" }));
modUserResponse = await client.getPersonDetails(getPersonForm);
}
let userResponse: RequestState<GetPersonDetailsResponse> = {
state: "empty",
};
if (userId) {
const getPersonForm: GetPersonDetails = {
person_id: userId,
auth,
};
promises.push(client.getPersonDetails(getPersonForm));
} else {
promises.push(Promise.resolve({ state: "empty" }));
userResponse = await client.getPersonDetails(getPersonForm);
}
return promises;
return {
res: await client.getModlog(modlogForm),
communityRes: communityResponse,
modUserResponse,
userResponse,
};
}
}

View file

@ -24,10 +24,7 @@ import {
DistinguishComment,
EditComment,
EditPrivateMessage,
GetPersonMentions,
GetPersonMentionsResponse,
GetPrivateMessages,
GetReplies,
GetRepliesResponse,
GetSiteResponse,
MarkCommentReplyAsRead,
@ -53,6 +50,7 @@ import { UserService } from "../../services";
import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
RouteDataResponse,
commentsToFlatNodes,
editCommentReply,
editMention,
@ -92,6 +90,13 @@ enum ReplyEnum {
Mention,
Message,
}
type InboxData = RouteDataResponse<{
repliesRes: GetRepliesResponse;
mentionsRes: GetPersonMentionsResponse;
messagesRes: PrivateMessagesResponse;
}>;
type ReplyType = {
id: number;
type_: ReplyEnum;
@ -114,7 +119,7 @@ interface InboxState {
}
export class Inbox extends Component<any, InboxState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<InboxData>(this.context);
state: InboxState = {
unreadOrAll: UnreadOrAll.Unread,
messageType: MessageType.All,
@ -162,7 +167,7 @@ export class Inbox extends Component<any, InboxState> {
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
const [repliesRes, mentionsRes, messagesRes] = this.isoData.routeData;
const { mentionsRes, messagesRes, repliesRes } = this.isoData.routeData;
this.state = {
...this.state,
@ -686,50 +691,40 @@ export class Inbox extends Component<any, InboxState> {
await i.refetch();
}
static fetchInitialData({
static async fetchInitialData({
client,
auth,
}: InitialFetchRequest): Promise<any>[] {
const promises: Promise<RequestState<any>>[] = [];
}: InitialFetchRequest): Promise<InboxData> {
const sort: CommentSortType = "New";
if (auth) {
// It can be /u/me, or /username/1
const repliesForm: GetReplies = {
sort,
unread_only: true,
page: 1,
limit: fetchLimit,
auth,
};
promises.push(client.getReplies(repliesForm));
const personMentionsForm: GetPersonMentions = {
sort,
unread_only: true,
page: 1,
limit: fetchLimit,
auth,
};
promises.push(client.getPersonMentions(personMentionsForm));
const privateMessagesForm: GetPrivateMessages = {
unread_only: true,
page: 1,
limit: fetchLimit,
auth,
};
promises.push(client.getPrivateMessages(privateMessagesForm));
} else {
promises.push(
Promise.resolve({ state: "empty" }),
Promise.resolve({ state: "empty" }),
Promise.resolve({ state: "empty" })
);
}
return promises;
return {
mentionsRes: auth
? await client.getPersonMentions({
sort,
unread_only: true,
page: 1,
limit: fetchLimit,
auth,
})
: { state: "empty" },
messagesRes: auth
? await client.getPrivateMessages({
unread_only: true,
page: 1,
limit: fetchLimit,
auth,
})
: { state: "empty" },
repliesRes: auth
? await client.getReplies({
sort,
unread_only: true,
page: 1,
limit: fetchLimit,
auth,
})
: { state: "empty" },
};
}
async refetch() {

View file

@ -54,6 +54,7 @@ import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
QueryParams,
RouteDataResponse,
canMod,
capitalizeFirstLetter,
editComment,
@ -90,6 +91,10 @@ import { CommunityLink } from "../community/community-link";
import { PersonDetails } from "./person-details";
import { PersonListing } from "./person-listing";
type ProfileData = RouteDataResponse<{
personResponse: GetPersonDetailsResponse;
}>;
interface ProfileState {
personRes: RequestState<GetPersonDetailsResponse>;
personBlocked: boolean;
@ -156,7 +161,7 @@ export class Profile extends Component<
RouteComponentProps<{ username: string }>,
ProfileState
> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<ProfileData>(this.context);
state: ProfileState = {
personRes: { state: "empty" },
personBlocked: false,
@ -208,7 +213,7 @@ export class Profile extends Component<
if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
personRes: this.isoData.routeData[0],
personRes: this.isoData.routeData.personResponse,
isIsomorphic: true,
};
}
@ -267,14 +272,12 @@ export class Profile extends Component<
}
}
static fetchInitialData({
static async fetchInitialData({
client,
path,
query: { page, sort, view: urlView },
auth,
}: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<
RequestState<any>
>[] {
}: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<ProfileData> {
const pathSplit = path.split("/");
const username = pathSplit[2];
@ -289,7 +292,9 @@ export class Profile extends Component<
auth,
};
return [client.getPersonDetails(form)];
return {
personResponse: await client.getPersonDetails(form),
};
}
get documentTitle(): string {

View file

@ -2,7 +2,6 @@ import { Component, linkEvent } from "inferno";
import {
ApproveRegistrationApplication,
GetSiteResponse,
ListRegistrationApplications,
ListRegistrationApplicationsResponse,
RegistrationApplicationView,
} from "lemmy-js-client";
@ -12,6 +11,7 @@ import { UserService } from "../../services";
import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
RouteDataResponse,
editRegistrationApplication,
fetchLimit,
myAuthRequired,
@ -28,6 +28,10 @@ enum UnreadOrAll {
All,
}
type RegistrationApplicationsData = RouteDataResponse<{
listRegistrationApplicationsResponse: ListRegistrationApplicationsResponse;
}>;
interface RegistrationApplicationsState {
appsRes: RequestState<ListRegistrationApplicationsResponse>;
siteRes: GetSiteResponse;
@ -40,7 +44,7 @@ export class RegistrationApplications extends Component<
any,
RegistrationApplicationsState
> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<RegistrationApplicationsData>(this.context);
state: RegistrationApplicationsState = {
appsRes: { state: "empty" },
siteRes: this.isoData.site_res,
@ -59,7 +63,7 @@ export class RegistrationApplications extends Component<
if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
appsRes: this.isoData.routeData[0],
appsRes: this.isoData.routeData.listRegistrationApplicationsResponse,
isIsomorphic: true,
};
}
@ -184,25 +188,20 @@ export class RegistrationApplications extends Component<
this.refetch();
}
static fetchInitialData({
static async fetchInitialData({
auth,
client,
}: InitialFetchRequest): Promise<any>[] {
const promises: Promise<RequestState<any>>[] = [];
if (auth) {
const form: ListRegistrationApplications = {
unread_only: true,
page: 1,
limit: fetchLimit,
auth,
};
promises.push(client.listRegistrationApplications(form));
} else {
promises.push(Promise.resolve({ state: "empty" }));
}
return promises;
}: InitialFetchRequest): Promise<RegistrationApplicationsData> {
return {
listRegistrationApplicationsResponse: auth
? await client.listRegistrationApplications({
unread_only: true,
page: 1,
limit: fetchLimit,
auth: auth as string,
})
: { state: "empty" },
};
}
async refetch() {

View file

@ -23,6 +23,7 @@ import { HttpService, UserService } from "../../services";
import { FirstLoadService } from "../../services/FirstLoadService";
import { RequestState } from "../../services/HttpService";
import {
RouteDataResponse,
amAdmin,
editCommentReport,
editPostReport,
@ -56,6 +57,12 @@ enum MessageEnum {
PrivateMessageReport,
}
type ReportsData = RouteDataResponse<{
commentReportsRes: ListCommentReportsResponse;
postReportsRes: ListPostReportsResponse;
messageReportsRes: ListPrivateMessageReportsResponse;
}>;
type ItemType = {
id: number;
type_: MessageEnum;
@ -75,7 +82,7 @@ interface ReportsState {
}
export class Reports extends Component<any, ReportsState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<ReportsData>(this.context);
state: ReportsState = {
commentReportsRes: { 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
if (FirstLoadService.isFirstLoad) {
const [commentReportsRes, postReportsRes, messageReportsRes] =
const { commentReportsRes, postReportsRes, messageReportsRes } =
this.isoData.routeData;
this.state = {
...this.state,
commentReportsRes,
@ -111,7 +119,7 @@ export class Reports extends Component<any, ReportsState> {
if (amAdmin()) {
this.state = {
...this.state,
messageReportsRes,
messageReportsRes: messageReportsRes,
};
}
}
@ -481,55 +489,48 @@ export class Reports extends Component<any, ReportsState> {
await i.refetch();
}
static fetchInitialData({
static async fetchInitialData({
auth,
client,
}: InitialFetchRequest): Promise<any>[] {
const promises: Promise<RequestState<any>>[] = [];
}: InitialFetchRequest): Promise<ReportsData> {
const unresolved_only = true;
const page = 1;
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,
page,
limit,
auth,
auth: auth as string,
};
promises.push(client.listCommentReports(commentReportsForm));
const postReportsForm: ListPostReports = {
unresolved_only,
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" })
data.messageReportsRes = await client.listPrivateMessageReports(
privateMessageReportsForm
);
}
return promises;
return data;
}
async refetch() {

View file

@ -3,6 +3,7 @@ import { RouteComponentProps } from "inferno-router/dist/Route";
import {
CreatePost as CreatePostI,
GetCommunity,
GetCommunityResponse,
GetSiteResponse,
ListCommunitiesResponse,
} from "lemmy-js-client";
@ -17,6 +18,7 @@ import {
import {
Choice,
QueryParams,
RouteDataResponse,
enableDownvotes,
enableNsfw,
getIdFromString,
@ -32,6 +34,11 @@ export interface CreatePostProps {
communityId?: number;
}
type CreatePostData = RouteDataResponse<{
communityResponse: GetCommunityResponse;
initialCommunitiesRes: ListCommunitiesResponse;
}>;
function getCreatePostQueryParams() {
return getQueryParams<CreatePostProps>({
communityId: getIdFromString,
@ -54,7 +61,7 @@ export class CreatePost extends Component<
RouteComponentProps<Record<string, never>>,
CreatePostState
> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<CreatePostData>(this.context);
state: CreatePostState = {
siteRes: this.isoData.site_res,
loading: true,
@ -71,7 +78,15 @@ export class CreatePost extends Component<
// Only fetch the data if coming from another route
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") {
const communityChoice: Choice = {
@ -84,13 +99,6 @@ export class CreatePost extends Component<
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,
query: { communityId },
auth,
}: InitialFetchRequest<QueryParams<CreatePostProps>>): Promise<
RequestState<any>
>[] {
const promises: Promise<RequestState<any>>[] = [];
}: InitialFetchRequest<
QueryParams<CreatePostProps>
>): Promise<CreatePostData> {
const data: CreatePostData = {
initialCommunitiesRes: await fetchCommunitiesForOptions(client),
communityResponse: { state: "empty" },
};
if (communityId) {
const form: GetCommunity = {
@ -249,13 +260,9 @@ export class CreatePost extends Component<
id: getIdFromString(communityId),
};
promises.push(client.getCommunity(form));
} else {
promises.push(Promise.resolve({ state: "empty" }));
data.communityResponse = await client.getCommunity(form);
}
promises.push(fetchCommunitiesForOptions(client));
return promises;
return data;
}
}

View file

@ -77,6 +77,7 @@ import {
isImage,
myAuth,
restoreScrollPosition,
RouteDataResponse,
saveScrollPosition,
setIsoData,
setupTippy,
@ -93,6 +94,11 @@ import { PostListing } from "./post-listing";
const commentsShownInterval = 15;
type PostData = RouteDataResponse<{
postRes: GetPostResponse;
commentsRes: GetCommentsResponse;
}>;
interface PostState {
postId?: number;
commentId?: number;
@ -110,7 +116,7 @@ interface PostState {
}
export class Post extends Component<any, PostState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<PostData>(this.context);
private commentScrollDebounced: () => void;
state: PostState = {
postRes: { state: "empty" },
@ -169,7 +175,7 @@ export class Post extends Component<any, PostState> {
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
const [postRes, commentsRes] = this.isoData.routeData;
const { commentsRes, postRes } = this.isoData.routeData;
this.state = {
...this.state,
@ -220,13 +226,12 @@ export class Post extends Component<any, PostState> {
}
}
static fetchInitialData({
auth,
static async fetchInitialData({
client,
path,
}: InitialFetchRequest): Promise<any>[] {
auth,
}: InitialFetchRequest): Promise<PostData> {
const pathSplit = path.split("/");
const promises: Promise<RequestState<any>>[] = [];
const pathType = pathSplit.at(1);
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;
}
promises.push(client.getPost(postForm));
promises.push(client.getComments(commentsForm));
return promises;
return {
postRes: await client.getPost(postForm),
commentsRes: await client.getComments(commentsForm),
};
}
componentWillUnmount() {

View file

@ -10,6 +10,7 @@ import { InitialFetchRequest } from "../../interfaces";
import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
RouteDataResponse,
getRecipientIdFromProps,
myAuth,
setIsoData,
@ -19,6 +20,10 @@ import { HtmlTags } from "../common/html-tags";
import { Spinner } from "../common/icon";
import { PrivateMessageForm } from "./private-message-form";
type CreatePrivateMessageData = RouteDataResponse<{
recipientDetailsResponse: GetPersonDetailsResponse;
}>;
interface CreatePrivateMessageState {
siteRes: GetSiteResponse;
recipientRes: RequestState<GetPersonDetailsResponse>;
@ -30,7 +35,7 @@ export class CreatePrivateMessage extends Component<
any,
CreatePrivateMessageState
> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<CreatePrivateMessageData>(this.context);
state: CreatePrivateMessageState = {
siteRes: this.isoData.site_res,
recipientRes: { state: "empty" },
@ -47,7 +52,7 @@ export class CreatePrivateMessage extends Component<
if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
recipientRes: this.isoData.routeData[0],
recipientRes: this.isoData.routeData.recipientDetailsResponse,
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() {
this.setState({
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 {
if (this.state.recipientRes.state == "success") {
const name_ = this.state.recipientRes.data.person_view.person.name;

View file

@ -27,6 +27,7 @@ import { HttpService, RequestState } from "../services/HttpService";
import {
Choice,
QueryParams,
RouteDataResponse,
capitalizeFirstLetter,
commentsToFlatNodes,
communityToChoice,
@ -70,6 +71,14 @@ interface SearchProps {
page: number;
}
type SearchData = RouteDataResponse<{
communityResponse: GetCommunityResponse;
listCommunitiesResponse: ListCommunitiesResponse;
creatorDetailsResponse: GetPersonDetailsResponse;
searchResponse: SearchResponse;
resolveObjectResponse: ResolveObjectResponse;
}>;
type FilterType = "creator" | "community";
interface SearchState {
@ -228,7 +237,8 @@ function getListing(
}
export class Search extends Component<any, SearchState> {
private isoData = setIsoData(this.context);
private isoData = setIsoData<SearchData>(this.context);
state: SearchState = {
resolveObjectRes: { 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
if (FirstLoadService.isFirstLoad) {
const [
communityRes,
communitiesRes,
creatorDetailsRes,
searchRes,
resolveObjectRes,
] = this.isoData.routeData;
const {
communityResponse: communityRes,
creatorDetailsResponse: creatorDetailsRes,
listCommunitiesResponse: communitiesRes,
resolveObjectResponse: resolveObjectRes,
searchResponse: searchRes,
} = this.isoData.routeData;
this.state = {
...this.state,
communitiesRes,
communityRes,
creatorDetailsRes,
creatorSearchOptions:
creatorDetailsRes.state == "success"
? [personToChoice(creatorDetailsRes.data.person_view)]
: [],
isIsomorphic: true,
};
if (communityRes.state === "success") {
if (creatorDetailsRes?.state === "success") {
this.state = {
...this.state,
communitySearchOptions: [
communityToChoice(communityRes.data.community_view),
],
creatorSearchOptions:
creatorDetailsRes?.state === "success"
? [personToChoice(creatorDetailsRes.data.person_view)]
: [],
creatorDetailsRes,
};
}
if (q) {
if (communitiesRes?.state === "success") {
this.state = {
...this.state,
searchRes,
resolveObjectRes,
communitiesRes,
};
}
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);
}
static fetchInitialData({
static async fetchInitialData({
client,
auth,
query: { communityId, creatorId, q, type, sort, listingType, page },
}: InitialFetchRequest<QueryParams<SearchProps>>): Promise<
RequestState<any>
>[] {
const promises: Promise<RequestState<any>>[] = [];
}: InitialFetchRequest<QueryParams<SearchProps>>): Promise<SearchData> {
const community_id = getIdFromString(communityId);
let communityResponse: RequestState<GetCommunityResponse> = {
state: "empty",
};
let listCommunitiesResponse: RequestState<ListCommunitiesResponse> = {
state: "empty",
};
if (community_id) {
const getCommunityForm: GetCommunity = {
id: community_id,
auth,
};
promises.push(client.getCommunity(getCommunityForm));
promises.push(Promise.resolve({ state: "empty" }));
communityResponse = await client.getCommunity(getCommunityForm);
} else {
const listCommunitiesForm: ListCommunities = {
type_: defaultListingType,
@ -352,23 +385,32 @@ export class Search extends Component<any, SearchState> {
limit: fetchLimit,
auth,
};
promises.push(Promise.resolve({ state: "empty" }));
promises.push(client.listCommunities(listCommunitiesForm));
listCommunitiesResponse = await client.listCommunities(
listCommunitiesForm
);
}
const creator_id = getIdFromString(creatorId);
let creatorDetailsResponse: RequestState<GetPersonDetailsResponse> = {
state: "empty",
};
if (creator_id) {
const getCreatorForm: GetPersonDetails = {
person_id: creator_id,
auth,
};
promises.push(client.getPersonDetails(getCreatorForm));
} else {
promises.push(Promise.resolve({ state: "empty" }));
creatorDetailsResponse = await client.getPersonDetails(getCreatorForm);
}
const query = getSearchQueryFromQuery(q);
let searchResponse: RequestState<SearchResponse> = { state: "empty" };
let resolveObjectResponse: RequestState<ResolveObjectResponse> = {
state: "empty",
};
if (query) {
const form: SearchForm = {
q: query,
@ -383,21 +425,24 @@ export class Search extends Component<any, SearchState> {
};
if (query !== "") {
promises.push(client.search(form));
searchResponse = await client.search(form);
if (auth) {
const resolveObjectForm: ResolveObject = {
q: query,
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 {
@ -463,7 +508,7 @@ export class Search extends Component<any, SearchState> {
minLength={1}
/>
<button type="submit" className="btn btn-secondary mr-2 mb-2">
{this.state.searchRes.state == "loading" ? (
{this.state.searchRes.state === "loading" ? (
<Spinner />
) : (
<span>{i18n.t("search")}</span>

View file

@ -6,15 +6,17 @@ import { ErrorPageData } from "./utils";
/**
* This contains serialized data, it needs to be deserialized before use.
*/
export interface IsoData {
export interface IsoData<T extends RouteData = any> {
path: string;
routeData: RequestState<any>[];
routeData: T;
site_res: GetSiteResponse;
errorPageData?: ErrorPageData;
}
export type IsoDataOptionalSite = Partial<IsoData> &
Pick<IsoData, Exclude<keyof IsoData, "site_res">>;
export type IsoDataOptionalSite<T extends RouteData = any> = Partial<
IsoData<T>
> &
Pick<IsoData<T>, Exclude<keyof IsoData<T>, "site_res">>;
export interface ILemmyConfig {
wsHost?: string;
@ -80,3 +82,5 @@ export interface CommentNodeI {
children: Array<CommentNodeI>;
depth: number;
}
export type RouteData = Record<string, RequestState<any>>;

View file

@ -21,15 +21,13 @@ import { CreatePost } from "./components/post/create-post";
import { Post } from "./components/post/post";
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
import { Search } from "./components/search";
import { InitialFetchRequest } from "./interfaces";
import { RequestState } from "./services/HttpService";
import { InitialFetchRequest, RouteData } from "./interfaces";
interface IRoutePropsWithFetch extends IRouteProps {
// TODO Make sure this one is good.
fetchInitialData?(req: InitialFetchRequest): Promise<RequestState<any>>[];
interface IRoutePropsWithFetch<T extends RouteData> extends IRouteProps {
fetchInitialData?(req: InitialFetchRequest): Promise<T>;
}
export const routes: IRoutePropsWithFetch[] = [
export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
{
path: `/`,
component: Home,

View file

@ -11,7 +11,7 @@ type LoadingRequestState = {
state: "loading";
};
type FailedRequestState = {
export type FailedRequestState = {
state: "failed";
msg: string;
};
@ -58,7 +58,7 @@ class WrappedLemmyHttpClient {
return {
data: res,
state: "success",
state: !(res === undefined || res === null) ? "success" : "empty",
};
} catch (error) {
console.error(`API error: ${error}`);

View file

@ -43,8 +43,15 @@ import tippy from "tippy.js";
import Toastify from "toastify-js";
import { getHttpBase } from "./env";
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 { RequestState } from "./services/HttpService";
let Tribute: any;
if (isBrowser()) {
@ -1150,7 +1157,7 @@ export function isBrowser() {
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 (isBrowser()) {
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> {
return new Promise(resolve => setTimeout(resolve, millis));
}