IsoDataOptionalSite,
} from "../shared/interfaces";
import { routes } from "../shared/routes";
-import { RequestState, wrapClient } from "../shared/services/HttpService";
++import {
++ FailedRequestState,
++ RequestState,
++ wrapClient,
++} from "../shared/services/HttpService";
import {
ErrorPageData,
favIconPngUrl,
// 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;
- let routeData: Record<string, any> = {};
- let errorPageData: ErrorPageData | undefined;
- try {
- let try_site: any = await client.getSite(getSiteForm);
- if (try_site.error == "not_logged_in") {
- console.error(
- "Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
- );
- getSiteForm.auth = undefined;
- auth = undefined;
- try_site = await client.getSite(getSiteForm);
- }
- const routeData: RequestState<any>[] = [];
++ let routeData: Record<string, RequestState<any>> = {};
+ let errorPageData: ErrorPageData | undefined = undefined;
+ let try_site = await client.getSite(getSiteForm);
+ if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
+ console.error(
+ "Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
+ );
+ getSiteForm.auth = undefined;
+ auth = undefined;
+ try_site = await client.getSite(getSiteForm);
+ }
- if (!auth && isAuthPath(path)) {
- res.redirect("/login");
- return;
- }
+ if (!auth && isAuthPath(path)) {
+ return res.redirect("/login");
+ }
- site = try_site;
+ if (try_site.state === "success") {
+ site = try_site.data;
initializeSite(site);
+ if (path != "/setup" && !site.site_view.local_site.site_setup) {
+ return res.redirect("/setup");
+ }
+
if (site) {
const initialFetchReq: InitialFetchRequest = {
client,
};
if (activeRoute?.fetchInitialData) {
- routeData.push(
- ...(await Promise.all([
- ...activeRoute.fetchInitialData(initialFetchReq),
- ]))
+ const routeDataKeysAndVals = await Promise.all(
+ Object.entries(activeRoute.fetchInitialData(initialFetchReq)).map(
+ async ([key, val]) => [key, await val]
+ )
);
+
+ routeData = routeDataKeysAndVals.reduce((acc, [key, val]) => {
+ acc[key] = val;
+
+ return acc;
+ }, {});
}
}
- } catch (error) {
- errorPageData = getErrorPageData(error, site);
+ } else if (try_site.state === "failed") {
+ errorPageData = getErrorPageData(new Error(try_site.msg), site);
}
- const error = Object.values(routeData).find(val => val?.error)?.error;
++ 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);
- if (error === "instance_is_private") {
++ console.error(error.msg);
++ if (error.msg === "instance_is_private") {
return res.redirect(`/signup`);
} else {
- errorPageData = getErrorPageData(error, site);
- errorPageData = getErrorPageData(new Error(error), site);
++ errorPageData = getErrorPageData(new Error(error.msg), site);
}
}
ListCommunities,
ListCommunitiesResponse,
ListingType,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
- import { InitialFetchRequest } from "shared/interfaces";
import { i18n } from "../../i18next";
- import { WebSocketService } from "../../services";
+ import { InitialFetchRequest } from "../../interfaces";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { HttpService, RequestState } from "../../services/HttpService";
import {
QueryParams,
- WithPromiseKeys,
++ RouteDataResponse,
+ editCommunity,
getPageFromString,
getQueryParams,
getQueryString,
const communityLimit = 50;
- interface CommunitiesData {
++type CommunitiesData = RouteDataResponse<{
+ listCommunitiesResponse: ListCommunitiesResponse;
- }
++}>;
+
interface CommunitiesState {
- listCommunitiesResponse?: ListCommunitiesResponse;
- loading: boolean;
+ listCommunitiesResponse: RequestState<ListCommunitiesResponse>;
siteRes: GetSiteResponse;
searchText: string;
+ isIsomorphic: boolean;
}
interface CommunitiesProps {
return listingType ? (listingType as ListingType) : "Local";
}
- function toggleSubscribe(community_id: number, follow: boolean) {
- const auth = myAuth();
- if (auth) {
- const form: FollowCommunity = {
- community_id,
- follow,
- auth,
- };
-
- WebSocketService.Instance.send(wsClient.followCommunity(form));
- }
- }
-
- function refetch() {
- const { listingType, page } = getCommunitiesQueryParams();
-
- const listCommunitiesForm: ListCommunities = {
- type_: listingType,
- sort: "TopMonth",
- limit: communityLimit,
- page,
- auth: myAuth(false),
- };
-
- WebSocketService.Instance.send(wsClient.listCommunities(listCommunitiesForm));
- }
-
export class Communities extends Component<any, CommunitiesState> {
- private subscription?: Subscription;
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<CommunitiesData>(this.context);
state: CommunitiesState = {
- loading: true,
+ listCommunitiesResponse: { state: "empty" },
siteRes: this.isoData.site_res,
searchText: "",
+ isIsomorphic: false,
};
constructor(props: any, context: any) {
this.handlePageChange = this.handlePageChange.bind(this);
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
-
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
+ const { listCommunitiesResponse } = this.isoData.routeData;
+
this.state = {
...this.state,
- listCommunitiesResponse: this.isoData.routeData[0],
+ listCommunitiesResponse,
- loading: false,
+ isIsomorphic: true,
};
- } else {
- refetch();
}
}
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>
- >): WithPromiseKeys<CommunitiesData> {
++ >): Promise<CommunitiesData> {
const listCommunitiesForm: ListCommunities = {
type_: getListingTypeFromQuery(listingType),
sort: "TopMonth",
auth: auth,
};
- return [client.listCommunities(listCommunitiesForm)];
+ return {
- listCommunitiesResponse: client.listCommunities(listCommunitiesForm),
++ listCommunitiesResponse: await client.listCommunities(
++ listCommunitiesForm
++ ),
+ };
}
- parseMessage(msg: any) {
- const op = wsUserOp(msg);
- console.log(msg);
- if (msg.error) {
- toast(i18n.t(msg.error), "danger");
- } else if (op === UserOperation.ListCommunities) {
- const data = wsJsonToRes<ListCommunitiesResponse>(msg);
- this.setState({ listCommunitiesResponse: data, loading: false });
- window.scrollTo(0, 0);
- } else if (op === UserOperation.FollowCommunity) {
- const {
- community_view: {
- community,
- subscribed,
- counts: { subscribers },
- },
- } = wsJsonToRes<CommunityResponse>(msg);
- const res = this.state.listCommunitiesResponse;
- const found = res?.communities.find(
- ({ community: { id } }) => id == community.id
- );
-
- if (found) {
- found.subscribed = subscribed;
- found.counts.subscribers = subscribers;
- this.setState(this.state);
+ getCommunitiesQueryParams() {
+ return getQueryParams<CommunitiesProps>({
+ listingType: getListingTypeFromQuery,
+ page: getPageFromString,
+ });
+ }
+
+ async handleFollow(data: {
+ i: Communities;
+ communityId: number;
+ follow: boolean;
+ }) {
+ const res = await HttpService.client.followCommunity({
+ community_id: data.communityId,
+ follow: data.follow,
+ auth: myAuthRequired(),
+ });
+ data.i.findAndUpdateCommunity(res);
+ }
+
+ async refetch() {
+ this.setState({ listCommunitiesResponse: { state: "loading" } });
+
+ const { listingType, page } = this.getCommunitiesQueryParams();
+
+ this.setState({
+ listCommunitiesResponse: await HttpService.client.listCommunities({
+ type_: listingType,
+ sort: "TopMonth",
+ limit: communityLimit,
+ page,
+ auth: myAuth(),
+ }),
+ });
+
+ window.scrollTo(0, 0);
+ }
+
+ findAndUpdateCommunity(res: RequestState<CommunityResponse>) {
+ this.setState(s => {
+ if (
+ s.listCommunitiesResponse.state == "success" &&
+ res.state == "success"
+ ) {
+ s.listCommunitiesResponse.data.communities = editCommunity(
+ res.data.community_view,
+ s.listCommunitiesResponse.data.communities
+ );
}
- }
+ return s;
+ });
}
}
DataType,
InitialFetchRequest,
} from "../../interfaces";
- import { UserService, WebSocketService } from "../../services";
+ import { UserService } from "../../services";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { HttpService, RequestState } from "../../services/HttpService";
import {
QueryParams,
- WithPromiseKeys,
++ RouteDataResponse,
commentsToFlatNodes,
communityRSSUrl,
- createCommentLikeRes,
- createPostLikeFindRes,
- editCommentRes,
- editPostFindRes,
+ editComment,
+ editPost,
+ editWith,
enableDownvotes,
enableNsfw,
fetchLimit,
import { PostListings } from "../post/post-listings";
import { CommunityLink } from "./community-link";
- interface CommunityData {
++type CommunityData = RouteDataResponse<{
+ communityResponse: GetCommunityResponse;
+ postsResponse?: GetPostsResponse;
+ commentsResponse?: GetCommentsResponse;
- }
++}>;
+
interface State {
- communityRes?: GetCommunityResponse;
- communityLoading: boolean;
- listingsLoading: boolean;
- posts: PostView[];
- comments: CommentView[];
+ communityRes: RequestState<GetCommunityResponse>;
+ postsRes: RequestState<GetPostsResponse>;
+ commentsRes: RequestState<GetCommentsResponse>;
+ siteRes: GetSiteResponse;
showSidebarMobile: boolean;
+ finished: Map<CommentId, boolean | undefined>;
+ isIsomorphic: boolean;
}
interface CommunityProps {
RouteComponentProps<{ name: string }>,
State
> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<CommunityData>(this.context);
- private subscription?: Subscription;
state: State = {
- communityLoading: true,
- listingsLoading: true,
- posts: [],
- comments: [],
+ communityRes: { state: "empty" },
+ postsRes: { state: "empty" },
+ commentsRes: { state: "empty" },
+ siteRes: this.isoData.site_res,
showSidebarMobile: false,
+ finished: new Map(),
+ isIsomorphic: false,
};
constructor(props: RouteComponentProps<{ name: string }>, context: any) {
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
this.handlePageChange = this.handlePageChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ // All of the action binds
+ this.handleDeleteCommunity = this.handleDeleteCommunity.bind(this);
+ this.handleEditCommunity = this.handleEditCommunity.bind(this);
+ this.handleFollow = this.handleFollow.bind(this);
+ this.handleRemoveCommunity = this.handleRemoveCommunity.bind(this);
+ this.handleCreateComment = this.handleCreateComment.bind(this);
+ this.handleEditComment = this.handleEditComment.bind(this);
+ this.handleSaveComment = this.handleSaveComment.bind(this);
+ this.handleBlockCommunity = this.handleBlockCommunity.bind(this);
+ this.handleBlockPerson = this.handleBlockPerson.bind(this);
+ this.handleDeleteComment = this.handleDeleteComment.bind(this);
+ this.handleRemoveComment = this.handleRemoveComment.bind(this);
+ this.handleCommentVote = this.handleCommentVote.bind(this);
+ this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
+ this.handleAddAdmin = this.handleAddAdmin.bind(this);
+ this.handlePurgePerson = this.handlePurgePerson.bind(this);
+ this.handlePurgeComment = this.handlePurgeComment.bind(this);
+ this.handleCommentReport = this.handleCommentReport.bind(this);
+ this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
+ this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
+ this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
+ this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
+ this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
+ this.handleBanPerson = this.handleBanPerson.bind(this);
+ this.handlePostVote = this.handlePostVote.bind(this);
+ this.handlePostEdit = this.handlePostEdit.bind(this);
+ this.handlePostReport = this.handlePostReport.bind(this);
+ this.handleLockPost = this.handleLockPost.bind(this);
+ this.handleDeletePost = this.handleDeletePost.bind(this);
+ this.handleRemovePost = this.handleRemovePost.bind(this);
+ this.handleSavePost = this.handleSavePost.bind(this);
+ this.handlePurgePost = this.handlePurgePost.bind(this);
+ this.handleFeaturePost = this.handleFeaturePost.bind(this);
// Only fetch the data if coming from another route
- if (this.isoData.path == this.context.router.route.match.url) {
- const { communityResponse, commentsResponse, postsResponse } =
- this.isoData.routeData;
+ if (FirstLoadService.isFirstLoad) {
- const [communityRes, postsRes, commentsRes] = this.isoData.routeData;
++ const {
++ communityResponse: communityRes,
++ commentsResponse: commentsRes,
++ postsResponse: postsRes,
++ } = this.isoData.routeData;
+
this.state = {
...this.state,
- communityRes: communityResponse,
- communityRes,
- postsRes,
- commentsRes,
+ isIsomorphic: true,
};
- if (postsResponse) {
- this.state = { ...this.state, posts: postsResponse.posts };
+
- if (commentsResponse) {
- this.state = { ...this.state, comments: commentsResponse.comments };
++ if (communityRes.state === "success") {
++ this.state = {
++ ...this.state,
++ communityRes,
++ };
+ }
+
- this.state = {
- ...this.state,
- communityLoading: false,
- listingsLoading: false,
- };
- } else {
- this.fetchCommunity();
- this.fetchData();
++ if (postsRes?.state === "success") {
++ this.state = {
++ ...this.state,
++ postsRes,
++ };
+ }
+
++ if (commentsRes?.state === "success") {
++ this.state = {
++ ...this.state,
++ commentsRes,
++ };
++ }
}
}
componentWillUnmount() {
saveScrollPosition(this.context);
- this.subscription?.unsubscribe();
}
-- static fetchInitialData({
++ static async fetchInitialData({
client,
path,
query: { dataType: urlDataType, page: urlPage, sort: urlSort },
auth,
- }: InitialFetchRequest<
- QueryParams<CommunityProps>
- >): WithPromiseKeys<CommunityData> {
+ }: InitialFetchRequest<QueryParams<CommunityProps>>): Promise<
- RequestState<any>
- >[] {
++ Promise<CommunityData>
++ > {
const pathSplit = path.split("/");
- const promises: Promise<RequestState<any>>[] = [];
const communityName = pathSplit[2];
const communityForm: GetCommunity = {
const page = getPageFromString(urlPage);
- let postsResponse: Promise<GetPostsResponse> | undefined = undefined;
- let commentsResponse: Promise<GetCommentsResponse> | undefined = undefined;
++ let postsResponse: RequestState<GetPostsResponse> | undefined = undefined;
++ let commentsResponse: RequestState<GetCommentsResponse> | undefined =
++ undefined;
+
if (dataType === DataType.Post) {
const getPostsForm: GetPosts = {
community_name: communityName,
saved_only: false,
auth,
};
- promises.push(client.getPosts(getPostsForm));
- promises.push(Promise.resolve({ state: "empty" }));
+
- postsResponse = client.getPosts(getPostsForm);
++ postsResponse = await client.getPosts(getPostsForm);
} else {
const getCommentsForm: GetComments = {
community_name: communityName,
saved_only: false,
auth,
};
- promises.push(Promise.resolve({ state: "empty" }));
- promises.push(client.getComments(getCommentsForm));
+
- commentsResponse = client.getComments(getCommentsForm);
++ commentsResponse = await client.getComments(getCommentsForm);
}
- return promises;
+ return {
- communityResponse: client.getCommunity(communityForm),
++ communityResponse: await client.getCommunity(communityForm),
+ commentsResponse,
+ postsResponse,
+ };
}
get documentTitle(): string {
GetFederatedInstancesResponse,
GetSiteResponse,
PersonView,
- SiteResponse,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
- import { WebSocketService } from "../../services";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { HttpService, RequestState } from "../../services/HttpService";
import {
- WithPromiseKeys,
++ RouteDataResponse,
capitalizeFirstLetter,
- isBrowser,
- myAuth,
- randomStr,
+ fetchThemeList,
+ myAuthRequired,
+ removeFromEmojiDataModel,
setIsoData,
showLocal,
toast,
import { SiteForm } from "./site-form";
import { TaglineForm } from "./tagline-form";
- interface AdminSettingsData {
++type AdminSettingsData = RouteDataResponse<{
+ bannedPersonsResponse: BannedPersonsResponse;
+ federatedInstancesResponse: GetFederatedInstancesResponse;
- }
++}>;
+
interface AdminSettingsState {
siteRes: GetSiteResponse;
- instancesRes?: GetFederatedInstancesResponse;
banned: PersonView[];
- loading: boolean;
- leaveAdminTeamLoading: boolean;
+ currentTab: string;
+ instancesRes: RequestState<GetFederatedInstancesResponse>;
+ bannedRes: RequestState<BannedPersonsResponse>;
+ leaveAdminTeamRes: RequestState<GetSiteResponse>;
+ themeList: string[];
+ isIsomorphic: boolean;
}
export class AdminSettings extends Component<any, AdminSettingsState> {
- private siteConfigTextAreaId = `site-config-${randomStr()}`;
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<AdminSettingsData>(this.context);
- private subscription?: Subscription;
state: AdminSettingsState = {
siteRes: this.isoData.site_res,
banned: [],
constructor(props: any, context: any) {
super(props, context);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ this.handleEditSite = this.handleEditSite.bind(this);
+ this.handleEditEmoji = this.handleEditEmoji.bind(this);
+ this.handleDeleteEmoji = this.handleDeleteEmoji.bind(this);
+ this.handleCreateEmoji = this.handleCreateEmoji.bind(this);
// Only fetch the data if coming from another route
- if (this.isoData.path == this.context.router.route.match.url) {
- const { bannedPersonsResponse, federatedInstancesResponse } =
- this.isoData.routeData;
+ if (FirstLoadService.isFirstLoad) {
- const [bannedRes, instancesRes] = this.isoData.routeData;
++ const {
++ bannedPersonsResponse: bannedRes,
++ federatedInstancesResponse: instancesRes,
++ } = this.isoData.routeData;
+
this.state = {
...this.state,
- banned: bannedPersonsResponse.banned,
- instancesRes: federatedInstancesResponse,
- loading: false,
+ bannedRes,
+ instancesRes,
+ isIsomorphic: true,
};
- } else {
- let cAuth = myAuth();
- if (cAuth) {
- WebSocketService.Instance.send(
- wsClient.getBannedPersons({
- auth: cAuth,
- })
- );
- WebSocketService.Instance.send(
- wsClient.getFederatedInstances({ auth: cAuth })
- );
- }
}
}
- 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,
- });
- }
-
-- static fetchInitialData({
++ static async fetchInitialData({
auth,
client,
- }: InitialFetchRequest): WithPromiseKeys<AdminSettingsData> {
- }: 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 {
- bannedPersonsResponse: client.getBannedPersons({ auth: auth as string }),
- federatedInstancesResponse: client.getFederatedInstances({
++ bannedPersonsResponse: await client.getBannedPersons({
+ auth: auth as string,
- }) as Promise<GetFederatedInstancesResponse>,
++ }),
++ federatedInstancesResponse: await client.getFederatedInstances({
++ auth: auth as string,
++ }),
+ };
}
- componentDidMount() {
- if (isBrowser()) {
- var textarea: any = document.getElementById(this.siteConfigTextAreaId);
- autosize(textarea);
- }
- }
-
- componentWillUnmount() {
- if (isBrowser()) {
- this.subscription?.unsubscribe();
+ async componentDidMount() {
+ if (!this.state.isIsomorphic) {
+ await this.fetchData();
}
}
);
}
++ 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 (
<>
QueryParams,
relTags,
restoreScrollPosition,
- saveCommentRes,
++ RouteDataResponse,
saveScrollPosition,
setIsoData,
setupTippy,
page: number;
}
- interface HomeData {
++type HomeData = RouteDataResponse<{
+ postsResponse?: GetPostsResponse;
+ commentsResponse?: GetCommentsResponse;
+ trendingResponse: ListCommunitiesResponse;
- }
++}>;
+
function getDataTypeFromQuery(type?: string): DataType {
return type ? DataType[type] : DataType.Post;
}
</Link>
);
- function getRss(listingType: ListingType) {
- const { sort } = getHomeQueryParams();
- const auth = myAuth(false);
-
- 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} />
- </>
- )
- );
- }
-
export class Home extends Component<any, HomeState> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<HomeData>(this.context);
- private subscription?: Subscription;
state: HomeState = {
- trendingCommunities: [],
+ postsRes: { state: "empty" },
+ commentsRes: { state: "empty" },
+ trendingCommunitiesRes: { state: "empty" },
siteRes: this.isoData.site_res,
showSubscribedMobile: false,
showTrendingMobile: false,
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
this.handlePageChange = this.handlePageChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ this.handleCreateComment = this.handleCreateComment.bind(this);
+ this.handleEditComment = this.handleEditComment.bind(this);
+ this.handleSaveComment = this.handleSaveComment.bind(this);
+ this.handleBlockPerson = this.handleBlockPerson.bind(this);
+ this.handleDeleteComment = this.handleDeleteComment.bind(this);
+ this.handleRemoveComment = this.handleRemoveComment.bind(this);
+ this.handleCommentVote = this.handleCommentVote.bind(this);
+ this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
+ this.handleAddAdmin = this.handleAddAdmin.bind(this);
+ this.handlePurgePerson = this.handlePurgePerson.bind(this);
+ this.handlePurgeComment = this.handlePurgeComment.bind(this);
+ this.handleCommentReport = this.handleCommentReport.bind(this);
+ this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
+ this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
+ this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
+ this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
+ this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
+ this.handleBanPerson = this.handleBanPerson.bind(this);
+ this.handlePostEdit = this.handlePostEdit.bind(this);
+ this.handlePostVote = this.handlePostVote.bind(this);
+ this.handlePostReport = this.handlePostReport.bind(this);
+ this.handleLockPost = this.handleLockPost.bind(this);
+ this.handleDeletePost = this.handleDeletePost.bind(this);
+ this.handleRemovePost = this.handleRemovePost.bind(this);
+ this.handleSavePost = this.handleSavePost.bind(this);
+ this.handlePurgePost = this.handlePurgePost.bind(this);
+ this.handleFeaturePost = this.handleFeaturePost.bind(this);
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
- const { trendingResponse, commentsResponse, postsResponse } =
- this.isoData.routeData;
+ if (FirstLoadService.isFirstLoad) {
- const [postsRes, commentsRes, trendingCommunitiesRes] =
- this.isoData.routeData;
++ const {
++ trendingResponse: trendingCommunitiesRes,
++ commentsResponse: commentsRes,
++ postsResponse: postsRes,
++ } = this.isoData.routeData;
- if (postsResponse) {
- this.state = { ...this.state, posts: postsResponse.posts };
- }
+ this.state = {
+ ...this.state,
- postsRes,
- commentsRes,
+ trendingCommunitiesRes,
+ tagline: getRandomFromList(this.state?.siteRes?.taglines ?? [])
+ ?.content,
+ isIsomorphic: true,
+ };
+
- if (commentsResponse) {
- this.state = { ...this.state, comments: commentsResponse.comments };
++ if (commentsRes?.state === "success") {
++ this.state = {
++ ...this.state,
++ commentsRes,
++ };
+ }
+
- if (isBrowser()) {
- WebSocketService.Instance.send(
- wsClient.communityJoin({ community_id: 0 })
- );
++ if (postsRes?.state === "success") {
++ this.state = {
++ ...this.state,
++ postsRes,
++ };
+ }
- const taglines = this.state?.siteRes?.taglines ?? [];
- this.state = {
- ...this.state,
- trendingCommunities: trendingResponse?.communities ?? [],
- loading: false,
- tagline: getRandomFromList(taglines)?.content,
- };
- } else {
- fetchTrendingCommunities();
- fetchData();
}
}
componentWillUnmount() {
saveScrollPosition(this.context);
- this.subscription?.unsubscribe();
}
-- static fetchInitialData({
++ static async fetchInitialData({
client,
auth,
query: { dataType: urlDataType, listingType, page: urlPage, sort: urlSort },
- }: InitialFetchRequest<QueryParams<HomeProps>>): WithPromiseKeys<HomeData> {
- }: InitialFetchRequest<QueryParams<HomeProps>>): Promise<
- RequestState<any>
- >[] {
++ }: InitialFetchRequest<QueryParams<HomeProps>>): Promise<HomeData> {
const dataType = getDataTypeFromQuery(urlDataType);
// TODO figure out auth default_listingType, default_sort_type
const page = urlPage ? Number(urlPage) : 1;
- const promises: Promise<any>[] = [];
-
- let postsResponse: Promise<GetPostsResponse> | undefined = undefined;
- let commentsResponse: Promise<GetCommentsResponse> | undefined = undefined;
- const promises: Promise<RequestState<any>>[] = [];
++ let postsResponse: RequestState<GetPostsResponse> | undefined = undefined;
++ let commentsResponse: RequestState<GetCommentsResponse> | undefined =
++ undefined;
if (dataType === DataType.Post) {
const getPostsForm: GetPosts = {
auth,
};
- postsResponse = client.getPosts(getPostsForm);
- promises.push(client.getPosts(getPostsForm));
- promises.push(Promise.resolve({ state: "empty" }));
++ postsResponse = await client.getPosts(getPostsForm);
} else {
const getCommentsForm: GetComments = {
page,
saved_only: false,
auth,
};
- promises.push(Promise.resolve({ state: "empty" }));
- promises.push(client.getComments(getCommentsForm));
+
- commentsResponse = client.getComments(getCommentsForm);
++ commentsResponse = await client.getComments(getCommentsForm);
}
const trendingCommunitiesForm: ListCommunities = {
limit: trendingFetchLimit,
auth,
};
-- promises.push(client.listCommunities(trendingCommunitiesForm));
- return promises;
+ return {
- trendingResponse: client.listCommunities(trendingCommunitiesForm),
++ trendingResponse: await client.listCommunities(trendingCommunitiesForm),
+ commentsResponse,
+ postsResponse,
+ };
}
get documentTitle(): string {
GetFederatedInstancesResponse,
GetSiteResponse,
Instance,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
- import { WebSocketService } from "../../services";
- import {
- WithPromiseKeys,
- isBrowser,
- relTags,
- setIsoData,
- toast,
- wsClient,
- wsSubscribe,
- } from "../../utils";
+ 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";
- interface InstancesData {
++type InstancesData = RouteDataResponse<{
+ federatedInstancesResponse: GetFederatedInstancesResponse;
- }
++}>;
+
interface InstancesState {
+ instancesRes: RequestState<GetFederatedInstancesResponse>;
siteRes: GetSiteResponse;
- instancesRes?: GetFederatedInstancesResponse;
- loading: boolean;
+ isIsomorphic: boolean;
}
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,
- loading: true,
+ isIsomorphic: false,
};
- private subscription?: Subscription;
constructor(props: any, context: any) {
super(props, context);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
-
// Only fetch the data if coming from another route
- if (this.isoData.path == this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
- instancesRes: this.isoData.routeData[0],
+ instancesRes: this.isoData.routeData.federatedInstancesResponse,
- loading: false,
+ isIsomorphic: true,
};
- } else {
- WebSocketService.Instance.send(wsClient.getFederatedInstances({}));
}
}
- static fetchInitialData({
- client,
- }: InitialFetchRequest): WithPromiseKeys<InstancesData> {
+ async componentDidMount() {
+ if (!this.state.isIsomorphic) {
+ await this.fetchInstances();
+ }
+ }
+
+ async fetchInstances() {
+ this.setState({
+ instancesRes: { state: "loading" },
+ });
+
+ this.setState({
+ instancesRes: await HttpService.client.getFederatedInstances({}),
+ });
+ }
+
- static fetchInitialData(
++ static async fetchInitialData(
+ req: InitialFetchRequest
- ): Promise<RequestState<any>>[] {
- return [req.client.getFederatedInstances({})];
++ ): Promise<InstancesData> {
+ return {
- federatedInstancesResponse: client.getFederatedInstances(
- {}
- ) as Promise<GetFederatedInstancesResponse>,
++ federatedInstancesResponse: await req.client.getFederatedInstances({}),
+ };
}
get documentTitle(): string {
import {
Choice,
QueryParams,
- WithPromiseKeys,
++ RouteDataResponse,
amAdmin,
amMod,
debounce,
| AdminPurgePostView
| AdminPurgeCommentView;
- interface ModlogData {
++type ModlogData = RouteDataResponse<{
+ modlogResponse: GetModlogResponse;
+ communityResponse?: GetCommunityResponse;
+ modUserResponse?: GetPersonDetailsResponse;
+ userResponse?: GetPersonDetailsResponse;
- }
++}>;
+
interface ModlogType {
id: number;
type_: ModlogActionType;
RouteComponentProps<{ communityId?: string }>,
ModlogState
> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<ModlogData>(this.context);
- private subscription?: Subscription;
state: ModlogState = {
- loadingModlog: true,
+ res: { state: "empty" },
+ communityRes: { state: "empty" },
loadingModSearch: false,
loadingUserSearch: false,
userSearchOptions: [],
this.handleUserChange = this.handleUserChange.bind(this);
this.handleModChange = this.handleModChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
-
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
- const [res, communityRes, filteredModRes, filteredUserRes] =
- this.isoData.routeData;
+ const {
- modlogResponse,
- communityResponse,
++ modlogResponse: res,
++ communityResponse: communityRes,
+ modUserResponse,
+ userResponse,
+ } = this.isoData.routeData;
+
this.state = {
...this.state,
- res: modlogResponse,
+ res,
- communityRes,
};
- // Getting the moderators
- this.state = {
- ...this.state,
- communityMods: communityResponse?.moderators,
- };
-
- if (modUserResponse) {
- if (filteredModRes.state === "success") {
++ if (communityRes?.state === "success") {
this.state = {
...this.state,
- modSearchOptions: [personToChoice(modUserResponse.person_view)],
- modSearchOptions: [personToChoice(filteredModRes.data.person_view)],
++ communityRes,
};
}
- if (userResponse) {
- if (filteredUserRes.state === "success") {
++ if (modUserResponse?.state === "success") {
this.state = {
...this.state,
- userSearchOptions: [personToChoice(userResponse.person_view)],
- userSearchOptions: [personToChoice(filteredUserRes.data.person_view)],
++ modSearchOptions: [personToChoice(modUserResponse.data.person_view)],
+ };
+ }
+
- this.state = { ...this.state, loadingModlog: false };
- } else {
- this.refetch();
- }
- }
-
- componentWillUnmount() {
- if (isBrowser()) {
- this.subscription?.unsubscribe();
++ if (userResponse?.state === "success") {
++ this.state = {
++ ...this.state,
++ userSearchOptions: [personToChoice(userResponse.data.person_view)],
+ };
+ }
}
}
}
}
-- static fetchInitialData({
++ static async fetchInitialData({
client,
path,
query: { modId: urlModId, page, userId: urlUserId, actionType },
auth,
site,
- }: InitialFetchRequest<
- QueryParams<ModlogProps>
- >): WithPromiseKeys<ModlogData> {
- }: 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)
auth,
};
- let communityResponse: Promise<GetCommunityResponse> | undefined =
- promises.push(client.getModlog(modlogForm));
++ let communityResponse: RequestState<GetCommunityResponse> | undefined =
+ undefined;
if (communityId) {
const communityForm: GetCommunity = {
id: communityId,
auth,
};
- promises.push(client.getCommunity(communityForm));
- } else {
- promises.push(Promise.resolve({ state: "empty" }));
+
- communityResponse = client.getCommunity(communityForm);
++ communityResponse = await client.getCommunity(communityForm);
}
- let modUserResponse: Promise<GetPersonDetailsResponse> | undefined =
++ let modUserResponse: RequestState<GetPersonDetailsResponse> | undefined =
+ undefined;
+
if (modId) {
const getPersonForm: GetPersonDetails = {
person_id: modId,
auth,
};
- modUserResponse = client.getPersonDetails(getPersonForm);
- promises.push(client.getPersonDetails(getPersonForm));
- } else {
- promises.push(Promise.resolve({ state: "empty" }));
++ modUserResponse = await client.getPersonDetails(getPersonForm);
}
- let userResponse: Promise<GetPersonDetailsResponse> | undefined = undefined;
++ let userResponse: RequestState<GetPersonDetailsResponse> | undefined =
++ undefined;
+
if (userId) {
const getPersonForm: GetPersonDetails = {
person_id: userId,
auth,
};
- userResponse = client.getPersonDetails(getPersonForm);
- promises.push(client.getPersonDetails(getPersonForm));
- } else {
- promises.push(Promise.resolve({ state: "empty" }));
++ userResponse = await client.getPersonDetails(getPersonForm);
}
- return promises;
+ return {
- modlogResponse: client.getModlog(modlogForm),
++ modlogResponse: await client.getModlog(modlogForm),
+ communityResponse,
+ modUserResponse,
+ userResponse,
+ };
}
-
- parseMessage(msg: any) {
- const op = wsUserOp(msg);
- console.log(msg);
-
- if (msg.error) {
- toast(i18n.t(msg.error), "danger");
- } else {
- switch (op) {
- case UserOperation.GetModlog: {
- const res = wsJsonToRes<GetModlogResponse>(msg);
- window.scrollTo(0, 0);
- this.setState({ res, loadingModlog: false });
-
- break;
- }
-
- case UserOperation.GetCommunity: {
- const {
- moderators,
- community_view: {
- community: { name },
- },
- } = wsJsonToRes<GetCommunityResponse>(msg);
- this.setState({
- communityMods: moderators,
- communityName: name,
- });
-
- break;
- }
- }
- }
- }
}
CommentResponse,
CommentSortType,
CommentView,
- GetPersonMentions,
+ CreateComment,
+ CreateCommentLike,
+ CreateCommentReport,
+ CreatePrivateMessage,
+ CreatePrivateMessageReport,
+ DeleteComment,
+ DeletePrivateMessage,
+ DistinguishComment,
+ EditComment,
+ EditPrivateMessage,
- GetPersonMentions,
GetPersonMentionsResponse,
-- GetPrivateMessages,
-- GetReplies,
GetRepliesResponse,
GetSiteResponse,
+ MarkCommentReplyAsRead,
+ MarkPersonMentionAsRead,
+ MarkPrivateMessageAsRead,
PersonMentionResponse,
PersonMentionView,
- PostReportResponse,
PrivateMessageReportResponse,
PrivateMessageResponse,
PrivateMessageView,
PrivateMessagesResponse,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
+ PurgeComment,
+ PurgeItemResponse,
+ PurgePerson,
+ PurgePost,
+ RemoveComment,
+ SaveComment,
+ TransferCommunity,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
import { i18n } from "../../i18next";
import { CommentViewType, InitialFetchRequest } from "../../interfaces";
- import { UserService, WebSocketService } from "../../services";
+ import { UserService } from "../../services";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { HttpService, RequestState } from "../../services/HttpService";
import {
- WithPromiseKeys,
++ RouteDataResponse,
commentsToFlatNodes,
- createCommentLikeRes,
- editCommentRes,
+ editCommentReply,
+ editMention,
+ editPrivateMessage,
+ editWith,
enableDownvotes,
fetchLimit,
- isBrowser,
+ getCommentParentId,
myAuth,
+ myAuthRequired,
relTags,
- saveCommentRes,
setIsoData,
- setupTippy,
toast,
updatePersonBlock,
- wsClient,
- wsSubscribe,
} from "../../utils";
import { CommentNodes } from "../comment/comment-nodes";
import { CommentSortSelect } from "../common/comment-sort-select";
Mention,
Message,
}
- interface InboxData {
+
- }
++type InboxData = RouteDataResponse<{
+ repliesResponse: GetRepliesResponse;
+ personMentionsResponse: GetPersonMentionsResponse;
+ privateMessagesResponse: PrivateMessagesResponse;
++}>;
+
type ReplyType = {
id: number;
type_: ReplyEnum;
}
export class Inbox extends Component<any, InboxState> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<InboxData>(this.context);
- private subscription?: Subscription;
state: InboxState = {
unreadOrAll: UnreadOrAll.Unread,
messageType: MessageType.All,
this.handleSortChange = this.handleSortChange.bind(this);
this.handlePageChange = this.handlePageChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ this.handleCreateComment = this.handleCreateComment.bind(this);
+ this.handleEditComment = this.handleEditComment.bind(this);
+ this.handleSaveComment = this.handleSaveComment.bind(this);
+ this.handleBlockPerson = this.handleBlockPerson.bind(this);
+ this.handleDeleteComment = this.handleDeleteComment.bind(this);
+ this.handleRemoveComment = this.handleRemoveComment.bind(this);
+ this.handleCommentVote = this.handleCommentVote.bind(this);
+ this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
+ this.handleAddAdmin = this.handleAddAdmin.bind(this);
+ this.handlePurgePerson = this.handlePurgePerson.bind(this);
+ this.handlePurgeComment = this.handlePurgeComment.bind(this);
+ this.handleCommentReport = this.handleCommentReport.bind(this);
+ this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
+ this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
+ this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
+ this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
+ this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
+ this.handleBanPerson = this.handleBanPerson.bind(this);
+
+ this.handleDeleteMessage = this.handleDeleteMessage.bind(this);
+ this.handleMarkMessageAsRead = this.handleMarkMessageAsRead.bind(this);
+ this.handleMessageReport = this.handleMessageReport.bind(this);
+ this.handleCreateMessage = this.handleCreateMessage.bind(this);
+ this.handleEditMessage = this.handleEditMessage.bind(this);
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
- const [repliesRes, mentionsRes, messagesRes] = this.isoData.routeData;
+ const {
- personMentionsResponse,
- privateMessagesResponse,
- repliesResponse,
++ personMentionsResponse: mentionsRes,
++ privateMessagesResponse: messagesRes,
++ repliesResponse: repliesRes,
+ } = this.isoData.routeData;
this.state = {
...this.state,
}
messages() {
- return (
- <div>
- {this.state.messages.map(pmv => (
- <PrivateMessage
- key={pmv.private_message.id}
- private_message_view={pmv}
- />
- ))}
- </div>
- );
+ switch (this.state.messagesRes.state) {
+ case "loading":
+ return (
+ <h5>
+ <Spinner large />
+ </h5>
+ );
+ case "success": {
+ const messages = this.state.messagesRes.data.private_messages;
+ return (
+ <div>
+ {messages.map(pmv => (
+ <PrivateMessage
+ key={pmv.private_message.id}
+ private_message_view={pmv}
+ onDelete={this.handleDeleteMessage}
+ onMarkRead={this.handleMarkMessageAsRead}
+ onReport={this.handleMessageReport}
+ onCreate={this.handleCreateMessage}
+ onEdit={this.handleEditMessage}
+ />
+ ))}
+ </div>
+ );
+ }
+ }
}
- handlePageChange(page: number) {
+ async handlePageChange(page: number) {
this.setState({ page });
- this.refetch();
+ await this.refetch();
}
- handleUnreadOrAllChange(i: Inbox, event: any) {
+ async handleUnreadOrAllChange(i: Inbox, event: any) {
i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
- i.refetch();
+ await i.refetch();
}
- handleMessageTypeChange(i: Inbox, event: any) {
+ async handleMessageTypeChange(i: Inbox, event: any) {
i.setState({ messageType: Number(event.target.value), page: 1 });
- i.refetch();
+ await i.refetch();
}
-- static fetchInitialData({
- auth,
++ static async fetchInitialData({
client,
- }: InitialFetchRequest): WithPromiseKeys<InboxData> {
+ auth,
- }: InitialFetchRequest): Promise<any>[] {
- const promises: Promise<RequestState<any>>[] = [];
-
++ }: InitialFetchRequest): Promise<InboxData> {
const sort: CommentSortType = "New";
- // It can be /u/me, or /username/1
- const repliesForm: GetReplies = {
- sort,
- unread_only: true,
- page: 1,
- limit: fetchLimit,
- auth: auth as string,
- };
- 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: auth as string,
- };
- 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: auth as string,
- };
- 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 {
- privateMessagesResponse: client.getPrivateMessages(privateMessagesForm),
- personMentionsResponse: client.getPersonMentions(personMentionsForm),
- repliesResponse: client.getReplies(repliesForm),
++ personMentionsResponse: auth
++ ? await client.getPersonMentions({
++ sort,
++ unread_only: true,
++ page: 1,
++ limit: fetchLimit,
++ auth,
++ })
++ : { state: "empty" },
++ privateMessagesResponse: auth
++ ? await client.getPrivateMessages({
++ unread_only: true,
++ page: 1,
++ limit: fetchLimit,
++ auth,
++ })
++ : { state: "empty" },
++ repliesResponse: auth
++ ? await client.getReplies({
++ sort,
++ unread_only: true,
++ page: 1,
++ limit: fetchLimit,
++ auth,
++ })
++ : { state: "empty" },
+ };
}
- refetch() {
- const { sort, page, unreadOrAll } = this.state;
- const unread_only = unreadOrAll === UnreadOrAll.Unread;
+ async refetch() {
+ const sort = this.state.sort;
+ const unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
+ const page = this.state.page;
const limit = fetchLimit;
- const auth = myAuth();
+ const auth = myAuthRequired();
- if (auth) {
- const repliesForm: GetReplies = {
+ this.setState({ repliesRes: { state: "loading" } });
+ this.setState({
+ repliesRes: await HttpService.client.getReplies({
sort,
unread_only,
page,
GetPersonDetails,
GetPersonDetailsResponse,
GetSiteResponse,
+ LockPost,
+ MarkCommentReplyAsRead,
+ MarkPersonMentionAsRead,
+ PersonView,
PostResponse,
+ PurgeComment,
PurgeItemResponse,
+ PurgePerson,
+ PurgePost,
+ RemoveComment,
+ RemovePost,
+ SaveComment,
+ SavePost,
SortType,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
+ TransferCommunity,
} from "lemmy-js-client";
import moment from "moment";
- import { Subscription } from "rxjs";
import { i18n } from "../../i18next";
import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
- import { UserService, WebSocketService } from "../../services";
+ import { UserService } from "../../services";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { HttpService, RequestState } from "../../services/HttpService";
import {
QueryParams,
- WithPromiseKeys,
++ RouteDataResponse,
canMod,
capitalizeFirstLetter,
- createCommentLikeRes,
- createPostLikeFindRes,
- editCommentRes,
- editPostFindRes,
+ editComment,
+ editPost,
+ editWith,
enableDownvotes,
enableNsfw,
fetchLimit,
import { PersonDetails } from "./person-details";
import { PersonListing } from "./person-listing";
- interface ProfileData {
++type ProfileData = RouteDataResponse<{
+ personResponse: GetPersonDetailsResponse;
- }
++}>;
+
interface ProfileState {
- personRes?: GetPersonDetailsResponse;
- loading: boolean;
+ personRes: RequestState<GetPersonDetailsResponse>;
personBlocked: boolean;
banReason?: string;
banExpireDays?: number;
RouteComponentProps<{ username: string }>,
ProfileState
> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<ProfileData>(this.context);
- private subscription?: Subscription;
state: ProfileState = {
- loading: true,
+ personRes: { state: "empty" },
personBlocked: false,
siteRes: this.isoData.site_res,
showBanDialog: false,
this.handleSortChange = this.handleSortChange.bind(this);
this.handlePageChange = this.handlePageChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ this.handleBlockPerson = this.handleBlockPerson.bind(this);
+ this.handleUnblockPerson = this.handleUnblockPerson.bind(this);
+
+ this.handleCreateComment = this.handleCreateComment.bind(this);
+ this.handleEditComment = this.handleEditComment.bind(this);
+ this.handleSaveComment = this.handleSaveComment.bind(this);
+ this.handleBlockPersonAlt = this.handleBlockPersonAlt.bind(this);
+ this.handleDeleteComment = this.handleDeleteComment.bind(this);
+ this.handleRemoveComment = this.handleRemoveComment.bind(this);
+ this.handleCommentVote = this.handleCommentVote.bind(this);
+ this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
+ this.handleAddAdmin = this.handleAddAdmin.bind(this);
+ this.handlePurgePerson = this.handlePurgePerson.bind(this);
+ this.handlePurgeComment = this.handlePurgeComment.bind(this);
+ this.handleCommentReport = this.handleCommentReport.bind(this);
+ this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
+ this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
+ this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
+ this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
+ this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
+ this.handleBanPerson = this.handleBanPerson.bind(this);
+ this.handlePostVote = this.handlePostVote.bind(this);
+ this.handlePostEdit = this.handlePostEdit.bind(this);
+ this.handlePostReport = this.handlePostReport.bind(this);
+ this.handleLockPost = this.handleLockPost.bind(this);
+ this.handleDeletePost = this.handleDeletePost.bind(this);
+ this.handleRemovePost = this.handleRemovePost.bind(this);
+ this.handleSavePost = this.handleSavePost.bind(this);
+ this.handlePurgePost = this.handlePurgePost.bind(this);
+ this.handleFeaturePost = this.handleFeaturePost.bind(this);
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
- personRes: this.isoData.routeData[0],
+ personRes: this.isoData.routeData.personResponse,
- loading: false,
+ isIsomorphic: true,
};
- } else {
- this.fetchUserData();
}
}
}
}
-- static fetchInitialData({
++ static async fetchInitialData({
client,
path,
query: { page, sort, view: urlView },
auth,
- }: InitialFetchRequest<
- QueryParams<ProfileProps>
- >): WithPromiseKeys<ProfileData> {
- }: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<
- RequestState<any>
- >[] {
++ }: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<ProfileData> {
const pathSplit = path.split("/");
const username = pathSplit[2];
auth,
};
- return [client.getPersonDetails(form)];
+ return {
- personResponse: client.getPersonDetails(form),
++ personResponse: await client.getPersonDetails(form),
+ };
}
- componentDidMount() {
- this.setPersonBlock();
- setupTippy();
- }
-
- componentWillUnmount() {
- this.subscription?.unsubscribe();
- saveScrollPosition(this.context);
- }
-
get documentTitle(): string {
+ const siteName = this.state.siteRes.site_view.site.name;
const res = this.state.personRes;
- return res
- ? `@${res.person_view.person.name} - ${this.state.siteRes.site_view.site.name}`
- : "";
+ return res.state == "success"
+ ? `@${res.data.person_view.person.name} - ${siteName}`
+ : siteName;
}
- render() {
- const { personRes, loading, siteRes } = this.state;
- const { page, sort, view } = getProfileQueryParams();
-
- return (
- <div className="container-lg">
- {loading ? (
+ renderPersonRes() {
+ switch (this.state.personRes.state) {
+ case "loading":
+ return (
<h5>
<Spinner large />
</h5>
import { Component, linkEvent } from "inferno";
import {
+ ApproveRegistrationApplication,
GetSiteResponse,
-- ListRegistrationApplications,
ListRegistrationApplicationsResponse,
- RegistrationApplicationResponse,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
+ RegistrationApplicationView,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
- import { UserService, WebSocketService } from "../../services";
+ import { UserService } from "../../services";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { HttpService, RequestState } from "../../services/HttpService";
import {
- WithPromiseKeys,
++ RouteDataResponse,
+ editRegistrationApplication,
fetchLimit,
- isBrowser,
- myAuth,
+ myAuthRequired,
setIsoData,
setupTippy,
- toast,
- updateRegistrationApplicationRes,
- wsClient,
- wsSubscribe,
} from "../../utils";
import { HtmlTags } from "../common/html-tags";
import { Spinner } from "../common/icon";
All,
}
- interface RegistrationApplicationsData {
++type RegistrationApplicationsData = RouteDataResponse<{
+ listRegistrationApplicationsResponse: ListRegistrationApplicationsResponse;
- }
++}>;
+
interface RegistrationApplicationsState {
- listRegistrationApplicationsResponse?: ListRegistrationApplicationsResponse;
+ appsRes: RequestState<ListRegistrationApplicationsResponse>;
siteRes: GetSiteResponse;
unreadOrAll: UnreadOrAll;
page: number;
any,
RegistrationApplicationsState
> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<RegistrationApplicationsData>(this.context);
- private subscription?: Subscription;
state: RegistrationApplicationsState = {
+ appsRes: { state: "empty" },
siteRes: this.isoData.site_res,
unreadOrAll: UnreadOrAll.Unread,
page: 1,
super(props, context);
this.handlePageChange = this.handlePageChange.bind(this);
-
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ this.handleApproveApplication = this.handleApproveApplication.bind(this);
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
- listRegistrationApplicationsResponse:
- this.isoData.routeData.listRegistrationApplicationsResponse,
- loading: false,
- appsRes: this.isoData.routeData[0],
++ appsRes: this.isoData.routeData.listRegistrationApplicationsResponse,
+ isIsomorphic: true,
};
- } else {
- this.refetch();
}
}
this.refetch();
}
-- static fetchInitialData({
++ static async fetchInitialData({
auth,
client,
- }: InitialFetchRequest): WithPromiseKeys<RegistrationApplicationsData> {
- const form: ListRegistrationApplications = {
- unread_only: true,
- page: 1,
- limit: fetchLimit,
- auth: auth as string,
- };
- }: 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:
- client.listRegistrationApplications(form),
++ listRegistrationApplicationsResponse: auth
++ ? await client.listRegistrationApplications({
++ unread_only: true,
++ page: 1,
++ limit: fetchLimit,
++ auth: auth as string,
++ })
++ : { state: "empty" },
+ };
}
- refetch() {
- let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
- let auth = myAuth();
- if (auth) {
- let form: ListRegistrationApplications = {
+ async refetch() {
+ const unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
+ this.setState({
+ appsRes: { state: "loading" },
+ });
+ this.setState({
+ appsRes: await HttpService.client.listRegistrationApplications({
unread_only: unread_only,
page: this.state.page,
limit: fetchLimit,
PostReportView,
PrivateMessageReportResponse,
PrivateMessageReportView,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
+ ResolveCommentReport,
+ ResolvePostReport,
+ ResolvePrivateMessageReport,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
- import { UserService, WebSocketService } from "../../services";
+ import { HttpService, UserService } from "../../services";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { RequestState } from "../../services/HttpService";
import {
- WithPromiseKeys,
++ RouteDataResponse,
amAdmin,
+ editCommentReport,
+ editPostReport,
+ editPrivateMessageReport,
fetchLimit,
- isBrowser,
- myAuth,
+ myAuthRequired,
setIsoData,
- setupTippy,
- toast,
- updateCommentReportRes,
- updatePostReportRes,
- updatePrivateMessageReportRes,
- wsClient,
- wsSubscribe,
} from "../../utils";
import { CommentReport } from "../comment/comment-report";
import { HtmlTags } from "../common/html-tags";
PrivateMessageReport,
}
- interface ReportsData {
++type ReportsData = RouteDataResponse<{
+ commentReportsResponse: ListCommentReportsResponse;
+ postReportsResponse: ListPostReportsResponse;
+ privateMessageReportsResponse?: ListPrivateMessageReportsResponse;
- }
++}>;
+
type ItemType = {
id: number;
type_: MessageEnum;
}
export class Reports extends Component<any, ReportsState> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<ReportsData>(this.context);
- private subscription?: Subscription;
state: ReportsState = {
+ commentReportsRes: { state: "empty" },
+ postReportsRes: { state: "empty" },
+ messageReportsRes: { state: "empty" },
unreadOrAll: UnreadOrAll.Unread,
messageType: MessageType.All,
- combined: [],
page: 1,
siteRes: this.isoData.site_res,
- loading: true,
+ isIsomorphic: false,
};
constructor(props: any, context: any) {
super(props, context);
this.handlePageChange = this.handlePageChange.bind(this);
-
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ this.handleResolveCommentReport =
+ this.handleResolveCommentReport.bind(this);
+ this.handleResolvePostReport = this.handleResolvePostReport.bind(this);
+ this.handleResolvePrivateMessageReport =
+ this.handleResolvePrivateMessageReport.bind(this);
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
- const [commentReportsRes, postReportsRes, messageReportsRes] =
- this.isoData.routeData;
+ const {
- commentReportsResponse,
- postReportsResponse,
- privateMessageReportsResponse,
++ commentReportsResponse: commentReportsRes,
++ postReportsResponse: postReportsRes,
++ privateMessageReportsResponse: messageReportsRes,
+ } = this.isoData.routeData;
+
this.state = {
...this.state,
- listCommentReportsResponse: commentReportsResponse,
- listPostReportsResponse: postReportsResponse,
- listPrivateMessageReportsResponse: privateMessageReportsResponse,
+ commentReportsRes,
+ postReportsRes,
+ isIsomorphic: true,
};
- this.state = {
- ...this.state,
- combined: this.buildCombined(),
- loading: false,
- };
- } else {
- this.refetch();
+ if (amAdmin()) {
+ this.state = {
+ ...this.state,
- messageReportsRes,
++ messageReportsRes: messageReportsRes ?? { state: "empty" },
+ };
+ }
}
}
}
privateMessageReports() {
- let reports =
- this.state.listPrivateMessageReportsResponse?.private_message_reports;
- return (
- reports && (
- <div>
- {reports.map(pmr => (
- <>
- <hr />
- <PrivateMessageReport
- key={pmr.private_message_report.id}
- report={pmr}
- />
- </>
- ))}
- </div>
- )
- );
+ const res = this.state.messageReportsRes;
+ switch (res.state) {
+ case "loading":
+ return (
+ <h5>
+ <Spinner large />
+ </h5>
+ );
+ case "success": {
+ const reports = res.data.private_message_reports;
+ return (
+ <div>
+ {reports.map(pmr => (
+ <>
+ <hr />
+ <PrivateMessageReport
+ key={pmr.private_message_report.id}
+ report={pmr}
+ onResolveReport={this.handleResolvePrivateMessageReport}
+ />
+ </>
+ ))}
+ </div>
+ );
+ }
+ }
}
- handlePageChange(page: number) {
+ async handlePageChange(page: number) {
this.setState({ page });
- this.refetch();
+ await this.refetch();
}
- handleUnreadOrAllChange(i: Reports, event: any) {
+ async handleUnreadOrAllChange(i: Reports, event: any) {
i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
- i.refetch();
+ await i.refetch();
}
- handleMessageTypeChange(i: Reports, event: any) {
+ async handleMessageTypeChange(i: Reports, event: any) {
i.setState({ messageType: Number(event.target.value), page: 1 });
- i.refetch();
+ await i.refetch();
}
-- static fetchInitialData({
++ static async fetchInitialData({
auth,
client,
- }: InitialFetchRequest): WithPromiseKeys<ReportsData> {
- }: 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 = {
- unresolved_only,
- page,
- limit,
- auth,
- };
- promises.push(client.listCommentReports(commentReportsForm));
+ const commentReportsForm: ListCommentReports = {
+ unresolved_only,
+ page,
+ limit,
+ auth: auth as string,
+ };
- const postReportsForm: ListPostReports = {
+ const postReportsForm: ListPostReports = {
+ unresolved_only,
+ page,
+ limit,
+ auth: auth as string,
+ };
+
- const data: WithPromiseKeys<ReportsData> = {
- commentReportsResponse: client.listCommentReports(commentReportsForm),
- postReportsResponse: client.listPostReports(postReportsForm),
++ const data: ReportsData = {
++ commentReportsResponse: await client.listCommentReports(
++ commentReportsForm
++ ),
++ postReportsResponse: await client.listPostReports(postReportsForm),
+ };
+
+ if (amAdmin()) {
+ const privateMessageReportsForm: ListPrivateMessageReports = {
unresolved_only,
page,
limit,
- auth,
+ auth: auth as string,
};
- promises.push(client.listPostReports(postReportsForm));
- data.privateMessageReportsResponse = client.listPrivateMessageReports(
- privateMessageReportsForm
- 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.privateMessageReportsResponse =
++ await client.listPrivateMessageReports(privateMessageReportsForm);
}
- return promises;
+ return data;
}
- refetch() {
- const unresolved_only = this.state.unreadOrAll === UnreadOrAll.Unread;
+ async refetch() {
+ const unresolved_only = this.state.unreadOrAll == UnreadOrAll.Unread;
const page = this.state.page;
const limit = fetchLimit;
- const auth = myAuth();
+ const auth = myAuthRequired();
+
+ this.setState({
+ commentReportsRes: { state: "loading" },
+ postReportsRes: { state: "loading" },
+ messageReportsRes: { state: "loading" },
+ });
+
+ const form:
+ | ListCommentReports
+ | ListPostReports
+ | ListPrivateMessageReports = {
+ unresolved_only,
+ page,
+ limit,
+ auth,
+ };
- if (auth) {
- const commentReportsForm: ListCommentReports = {
- unresolved_only,
- page,
- limit,
- auth,
- };
+ this.setState({
+ commentReportsRes: await HttpService.client.listCommentReports(form),
+ postReportsRes: await HttpService.client.listPostReports(form),
+ });
- WebSocketService.Instance.send(
- wsClient.listCommentReports(commentReportsForm)
- );
+ if (amAdmin()) {
+ this.setState({
+ messageReportsRes: await HttpService.client.listPrivateMessageReports(
+ form
+ ),
+ });
+ }
+ }
- const postReportsForm: ListPostReports = {
- unresolved_only,
- page,
- limit,
- auth,
- };
+ async handleResolveCommentReport(form: ResolveCommentReport) {
+ const res = await HttpService.client.resolveCommentReport(form);
+ this.findAndUpdateCommentReport(res);
+ }
- WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
+ async handleResolvePostReport(form: ResolvePostReport) {
+ const res = await HttpService.client.resolvePostReport(form);
+ this.findAndUpdatePostReport(res);
+ }
- if (amAdmin()) {
- const privateMessageReportsForm: ListPrivateMessageReports = {
- unresolved_only,
- page,
- limit,
- auth,
- };
- WebSocketService.Instance.send(
- wsClient.listPrivateMessageReports(privateMessageReportsForm)
+ async handleResolvePrivateMessageReport(form: ResolvePrivateMessageReport) {
+ const res = await HttpService.client.resolvePrivateMessageReport(form);
+ this.findAndUpdatePrivateMessageReport(res);
+ }
+
+ findAndUpdateCommentReport(res: RequestState<CommentReportResponse>) {
+ this.setState(s => {
+ if (s.commentReportsRes.state == "success" && res.state == "success") {
+ s.commentReportsRes.data.comment_reports = editCommentReport(
+ res.data.comment_report_view,
+ s.commentReportsRes.data.comment_reports
);
}
- }
+ return s;
+ });
}
- parseMessage(msg: any) {
- let op = wsUserOp(msg);
- console.log(msg);
- if (msg.error) {
- toast(i18n.t(msg.error), "danger");
- return;
- } else if (msg.reconnect) {
- this.refetch();
- } else if (op == UserOperation.ListCommentReports) {
- let data = wsJsonToRes<ListCommentReportsResponse>(msg);
- this.setState({ listCommentReportsResponse: data });
- this.setState({ combined: this.buildCombined(), loading: false });
- // this.sendUnreadCount();
- window.scrollTo(0, 0);
- setupTippy();
- } else if (op == UserOperation.ListPostReports) {
- let data = wsJsonToRes<ListPostReportsResponse>(msg);
- this.setState({ listPostReportsResponse: data });
- this.setState({ combined: this.buildCombined(), loading: false });
- // this.sendUnreadCount();
- window.scrollTo(0, 0);
- setupTippy();
- } else if (op == UserOperation.ListPrivateMessageReports) {
- let data = wsJsonToRes<ListPrivateMessageReportsResponse>(msg);
- this.setState({ listPrivateMessageReportsResponse: data });
- this.setState({ combined: this.buildCombined(), loading: false });
- // this.sendUnreadCount();
- window.scrollTo(0, 0);
- setupTippy();
- } else if (op == UserOperation.ResolvePostReport) {
- let data = wsJsonToRes<PostReportResponse>(msg);
- updatePostReportRes(
- data.post_report_view,
- this.state.listPostReportsResponse?.post_reports
- );
- let urcs = UserService.Instance.unreadReportCountSub;
- if (data.post_report_view.post_report.resolved) {
- urcs.next(urcs.getValue() - 1);
- } else {
- urcs.next(urcs.getValue() + 1);
- }
- this.setState(this.state);
- } else if (op == UserOperation.ResolveCommentReport) {
- let data = wsJsonToRes<CommentReportResponse>(msg);
- updateCommentReportRes(
- data.comment_report_view,
- this.state.listCommentReportsResponse?.comment_reports
- );
- let urcs = UserService.Instance.unreadReportCountSub;
- if (data.comment_report_view.comment_report.resolved) {
- urcs.next(urcs.getValue() - 1);
- } else {
- urcs.next(urcs.getValue() + 1);
+ findAndUpdatePostReport(res: RequestState<PostReportResponse>) {
+ this.setState(s => {
+ if (s.postReportsRes.state == "success" && res.state == "success") {
+ s.postReportsRes.data.post_reports = editPostReport(
+ res.data.post_report_view,
+ s.postReportsRes.data.post_reports
+ );
}
- this.setState(this.state);
- } else if (op == UserOperation.ResolvePrivateMessageReport) {
- let data = wsJsonToRes<PrivateMessageReportResponse>(msg);
- updatePrivateMessageReportRes(
- data.private_message_report_view,
- this.state.listPrivateMessageReportsResponse?.private_message_reports
- );
- let urcs = UserService.Instance.unreadReportCountSub;
- if (data.private_message_report_view.private_message_report.resolved) {
- urcs.next(urcs.getValue() - 1);
- } else {
- urcs.next(urcs.getValue() + 1);
+ return s;
+ });
+ }
+
+ findAndUpdatePrivateMessageReport(
+ res: RequestState<PrivateMessageReportResponse>
+ ) {
+ this.setState(s => {
+ if (s.messageReportsRes.state == "success" && res.state == "success") {
+ s.messageReportsRes.data.private_message_reports =
+ editPrivateMessageReport(
+ res.data.private_message_report_view,
+ s.messageReportsRes.data.private_message_reports
+ );
}
- this.setState(this.state);
- }
+ return s;
+ });
}
}
import { Component } from "inferno";
import { RouteComponentProps } from "inferno-router/dist/Route";
import {
+ CreatePost as CreatePostI,
GetCommunity,
+ GetCommunityResponse,
GetSiteResponse,
- PostView,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
+ ListCommunitiesResponse,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
- import { InitialFetchRequest, PostFormParams } from "shared/interfaces";
import { i18n } from "../../i18next";
- import { WebSocketService } from "../../services";
+ import { InitialFetchRequest, PostFormParams } from "../../interfaces";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import {
+ HttpService,
+ RequestState,
+ WrappedLemmyHttp,
+ } from "../../services/HttpService";
import {
Choice,
QueryParams,
- WithPromiseKeys,
++ RouteDataResponse,
enableDownvotes,
enableNsfw,
getIdFromString,
communityId?: number;
}
- interface CreatePostData {
++type CreatePostData = RouteDataResponse<{
+ communityResponse?: GetCommunityResponse;
- }
++ initialCommunitiesRes: ListCommunitiesResponse;
++}>;
+
function getCreatePostQueryParams() {
return getQueryParams<CreatePostProps>({
communityId: getIdFromString,
RouteComponentProps<Record<string, never>>,
CreatePostState
> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<CreatePostData>(this.context);
- private subscription?: Subscription;
state: CreatePostState = {
siteRes: this.isoData.site_res,
loading: true,
this.handleSelectedCommunityChange =
this.handleSelectedCommunityChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
-
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
- const { communityResponse } = this.isoData.routeData;
+ if (FirstLoadService.isFirstLoad) {
- const [communityRes, listCommunitiesRes] = this.isoData.routeData;
++ const { communityResponse: communityRes, initialCommunitiesRes } =
++ this.isoData.routeData;
- if (communityResponse) {
+ if (communityRes?.state === "success") {
const communityChoice: Choice = {
- label: communityResponse.community_view.community.title,
- value: communityResponse.community_view.community.id.toString(),
+ label: communityRes.data.community_view.community.title,
+ value: communityRes.data.community_view.community.id.toString(),
};
this.state = {
this.state = {
...this.state,
loading: false,
- initialCommunitiesRes: listCommunitiesRes,
++ initialCommunitiesRes,
+ isIsomorphic: true,
};
- } else {
- this.fetchCommunity();
}
}
});
}
- handlePostCreate(post_view: PostView) {
- this.props.history.replace(`/post/${post_view.post.id}`);
+ async handlePostCreate(form: CreatePostI) {
+ const res = await HttpService.client.createPost(form);
+
+ if (res.state === "success") {
+ const postId = res.data.post_view.post.id;
+ this.props.history.replace(`/post/${postId}`);
+ }
}
-- static fetchInitialData({
++ static async fetchInitialData({
client,
query: { communityId },
auth,
- }: InitialFetchRequest<QueryParams<CreatePostProps>>): Promise<
- RequestState<any>
- >[] {
- const promises: Promise<RequestState<any>>[] = [];
+ }: InitialFetchRequest<
+ QueryParams<CreatePostProps>
- >): WithPromiseKeys<CreatePostData> {
- const data: WithPromiseKeys<CreatePostData> = {};
++ >): Promise<CreatePostData> {
++ const data: CreatePostData = {
++ initialCommunitiesRes: await fetchCommunitiesForOptions(client),
++ };
if (communityId) {
const form: GetCommunity = {
id: getIdFromString(communityId),
};
- data.communityResponse = client.getCommunity(form);
- 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;
}
-
- parseMessage(msg: any) {
- const op = wsUserOp(msg);
- console.log(msg);
- if (msg.error) {
- toast(i18n.t(msg.error), "danger");
- return;
- }
-
- if (op === UserOperation.GetCommunity) {
- const {
- community_view: {
- community: { title, id },
- },
- } = wsJsonToRes<GetCommunityResponse>(msg);
-
- this.setState({
- selectedCommunityChoice: { label: title, value: id.toString() },
- loading: false,
- });
- }
- }
}
isImage,
myAuth,
restoreScrollPosition,
- saveCommentRes,
++ RouteDataResponse,
saveScrollPosition,
setIsoData,
setupTippy,
const commentsShownInterval = 15;
- interface PostData {
- postResponse: GetPostResponse;
- commentsResponse: GetCommentsResponse;
- }
++type PostData = RouteDataResponse<{
++ postRes: GetPostResponse;
++ commentsRes: GetCommentsResponse;
++}>;
+
interface PostState {
postId?: number;
commentId?: number;
}
export class Post extends Component<any, PostState> {
- private subscription?: Subscription;
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<PostData>(this.context);
private commentScrollDebounced: () => void;
state: PostState = {
+ postRes: { state: "empty" },
+ commentsRes: { state: "empty" },
postId: getIdFromProps(this.props),
commentId: getCommentIdFromProps(this.props),
- commentTree: [],
commentSort: "Hot",
commentViewType: CommentViewType.Tree,
scrolled: false,
this.state = { ...this.state, commentSectionRef: createRef() };
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
- const { commentsResponse, postResponse } = this.isoData.routeData;
+ if (FirstLoadService.isFirstLoad) {
- const [postRes, commentsRes] = this.isoData.routeData;
++ const { commentsRes, postRes } = this.isoData.routeData;
this.state = {
...this.state,
}
}
- static fetchInitialData(req: InitialFetchRequest): WithPromiseKeys<PostData> {
- const pathSplit = req.path.split("/");
- 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;
commentsForm.parent_id = id;
}
- promises.push(client.getPost(postForm));
- promises.push(client.getComments(commentsForm));
-
- return promises;
+ return {
- postResponse: req.client.getPost(postForm),
- commentsResponse: req.client.getComments(commentsForm),
++ postRes: await client.getPost(postForm),
++ commentsRes: await client.getComments(commentsForm),
+ };
}
componentWillUnmount() {
GetPersonDetails,
GetPersonDetailsResponse,
GetSiteResponse,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
} from "lemmy-js-client";
- import { Subscription } from "rxjs";
import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
- import { WebSocketService } from "../../services";
+ import { FirstLoadService } from "../../services/FirstLoadService";
+ import { HttpService, RequestState } from "../../services/HttpService";
import {
- WithPromiseKeys,
++ RouteDataResponse,
getRecipientIdFromProps,
- isBrowser,
myAuth,
setIsoData,
toast,
import { Spinner } from "../common/icon";
import { PrivateMessageForm } from "./private-message-form";
- interface CreatePrivateMessageData {
++type CreatePrivateMessageData = RouteDataResponse<{
+ recipientDetailsResponse: GetPersonDetailsResponse;
- }
++}>;
+
interface CreatePrivateMessageState {
siteRes: GetSiteResponse;
- recipientDetailsRes?: GetPersonDetailsResponse;
- recipient_id: number;
- loading: boolean;
+ recipientRes: RequestState<GetPersonDetailsResponse>;
+ recipientId: number;
+ isIsomorphic: boolean;
}
export class CreatePrivateMessage extends Component<
any,
CreatePrivateMessageState
> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<CreatePrivateMessageData>(this.context);
- private subscription?: Subscription;
state: CreatePrivateMessageState = {
siteRes: this.isoData.site_res,
- recipient_id: getRecipientIdFromProps(this.props),
- loading: true,
+ recipientRes: { state: "empty" },
+ recipientId: getRecipientIdFromProps(this.props),
+ isIsomorphic: false,
};
constructor(props: any, context: any) {
this.handlePrivateMessageCreate =
this.handlePrivateMessageCreate.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
-
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
this.state = {
...this.state,
- recipientDetailsRes: this.isoData.routeData.recipientDetailsResponse,
- loading: false,
- recipientRes: this.isoData.routeData[0],
++ recipientRes: this.isoData.routeData.recipientDetailsResponse,
+ isIsomorphic: true,
};
- } else {
- this.fetchPersonDetails();
}
}
- fetchPersonDetails() {
- let form: GetPersonDetails = {
- person_id: this.state.recipient_id,
- sort: "New",
- saved_only: false,
- auth: myAuth(false),
- };
- WebSocketService.Instance.send(wsClient.getPersonDetails(form));
+ async componentDidMount() {
+ if (!this.state.isIsomorphic) {
+ await this.fetchPersonDetails();
+ }
}
- static fetchInitialData(
- req: InitialFetchRequest
- ): WithPromiseKeys<CreatePrivateMessageData> {
- const person_id = Number(req.path.split("/").pop());
++ 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: req.auth,
++ auth,
+ };
+
+ return {
- recipientDetailsResponse: req.client.getPersonDetails(form),
++ recipientDetailsResponse: await client.getPersonDetails(form),
+ };
+ }
+
+ async fetchPersonDetails() {
+ this.setState({
+ recipientRes: { state: "loading" },
+ });
+
+ this.setState({
+ recipientRes: await HttpService.client.getPersonDetails({
+ person_id: this.state.recipientId,
+ sort: "New",
+ saved_only: false,
+ auth: myAuth(),
+ }),
+ });
+ }
+
- 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 {
- let name_ = this.state.recipientDetailsRes?.person_view.person.name;
- return name_ ? `${i18n.t("create_private_message")} - ${name_}` : "";
+ if (this.state.recipientRes.state == "success") {
+ const name_ = this.state.recipientRes.data.person_view.person.name;
+ return `${i18n.t("create_private_message")} - ${name_}`;
+ } else {
+ return "";
+ }
}
- componentWillUnmount() {
- if (isBrowser()) {
- this.subscription?.unsubscribe();
+ renderRecipientRes() {
+ switch (this.state.recipientRes.state) {
+ case "loading":
+ return (
+ <h5>
+ <Spinner large />
+ </h5>
+ );
+ case "success": {
+ const res = this.state.recipientRes.data;
+ return (
+ <div className="row">
+ <div className="col-12 col-lg-6 offset-lg-3 mb-4">
+ <h5>{i18n.t("create_private_message")}</h5>
+ <PrivateMessageForm
+ onCreate={this.handlePrivateMessageCreate}
+ recipient={res.person_view.person}
+ />
+ </div>
+ </div>
+ );
+ }
}
}
import {
Choice,
QueryParams,
- WithPromiseKeys,
++ RouteDataResponse,
capitalizeFirstLetter,
commentsToFlatNodes,
communityToChoice,
page: number;
}
- interface SearchData {
++type SearchData = RouteDataResponse<{
+ communityResponse?: GetCommunityResponse;
+ listCommunitiesResponse?: ListCommunitiesResponse;
+ creatorDetailsResponse?: GetPersonDetailsResponse;
+ searchResponse?: SearchResponse;
+ resolveObjectResponse?: ResolveObjectResponse;
- }
++}>;
+
type FilterType = "creator" | "community";
interface SearchState {
}
export class Search extends Component<any, SearchState> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<SearchData>(this.context);
- private subscription?: Subscription;
++
state: SearchState = {
- searchLoading: false,
+ resolveObjectRes: { state: "empty" },
+ creatorDetailsRes: { state: "empty" },
+ communitiesRes: { state: "empty" },
+ communityRes: { state: "empty" },
siteRes: this.isoData.site_res,
- communities: [],
- searchCommunitiesLoading: false,
- searchCreatorLoading: false,
creatorSearchOptions: [],
communitySearchOptions: [],
+ searchRes: { state: "empty" },
+ searchCreatorLoading: false,
+ searchCommunitiesLoading: false,
+ isIsomorphic: false,
};
constructor(props: any, context: any) {
};
// Only fetch the data if coming from another route
- if (this.isoData.path === this.context.router.route.match.url) {
+ if (FirstLoadService.isFirstLoad) {
- const [
- communityRes,
- communitiesRes,
- creatorDetailsRes,
- searchRes,
- resolveObjectRes,
- ] = this.isoData.routeData;
+ const {
- communityResponse,
- creatorDetailsResponse,
- listCommunitiesResponse,
- resolveObjectResponse,
- searchResponse,
++ communityResponse: communityRes,
++ creatorDetailsResponse: creatorDetailsRes,
++ listCommunitiesResponse: communitiesRes,
++ resolveObjectResponse: resolveObjectRes,
++ searchResponse: searchRes,
+ } = this.isoData.routeData;
- // This can be single or multiple communities given
- if (listCommunitiesResponse) {
+ 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,
- communities: listCommunitiesResponse.communities,
++ creatorSearchOptions:
++ creatorDetailsRes?.state === "success"
++ ? [personToChoice(creatorDetailsRes.data.person_view)]
++ : [],
++ creatorDetailsRes,
+ };
+ }
- if (communityResponse) {
++
++ if (communitiesRes?.state === "success") {
this.state = {
...this.state,
- communities: [communityResponse.community_view],
-- communitySearchOptions: [
- communityToChoice(communityResponse.community_view),
- communityToChoice(communityRes.data.community_view),
-- ],
++ communitiesRes,
};
}
- this.state = {
- ...this.state,
- creatorDetails: creatorDetailsResponse,
- creatorSearchOptions: creatorDetailsResponse
- ? [personToChoice(creatorDetailsResponse.person_view)]
- : [],
- };
- if (q) {
++ if (communityRes?.state === "success") {
+ this.state = {
+ ...this.state,
- searchRes,
- resolveObjectRes,
++ communityRes,
+ };
+ }
+
+ if (q !== "") {
+ this.state = {
+ ...this.state,
- searchResponse,
- resolveObjectResponse,
- searchLoading: false,
+ };
- } else {
- this.search();
- }
- } else {
- const listCommunitiesForm: ListCommunities = {
- type_: defaultListingType,
- sort: defaultSortType,
- limit: fetchLimit,
- auth: myAuth(false),
- };
+
- WebSocketService.Instance.send(
- wsClient.listCommunities(listCommunitiesForm)
- );
++ if (searchRes?.state === "success") {
++ this.state = {
++ ...this.state,
++ searchRes,
++ };
++ }
+
- if (q) {
- this.search();
++ if (resolveObjectRes?.state === "success") {
++ this.state = {
++ ...this.state,
++ resolveObjectRes,
++ };
++ }
+ }
}
}
saveScrollPosition(this.context);
}
-- static fetchInitialData({
++ static async fetchInitialData({
client,
auth,
query: { communityId, creatorId, q, type, sort, listingType, page },
- }: InitialFetchRequest<
- QueryParams<SearchProps>
- >): WithPromiseKeys<SearchData> {
- }: InitialFetchRequest<QueryParams<SearchProps>>): Promise<
- RequestState<any>
- >[] {
- const promises: Promise<RequestState<any>>[] = [];
-
++ }: InitialFetchRequest<QueryParams<SearchProps>>): Promise<SearchData> {
const community_id = getIdFromString(communityId);
- let communityResponse: Promise<GetCommunityResponse> | undefined =
- undefined;
- let listCommunitiesResponse: Promise<ListCommunitiesResponse> | undefined =
++ let communityResponse: RequestState<GetCommunityResponse> | undefined =
+ undefined;
++ let listCommunitiesResponse:
++ | RequestState<ListCommunitiesResponse>
++ | undefined = undefined;
if (community_id) {
const getCommunityForm: GetCommunity = {
id: community_id,
auth,
};
- promises.push(client.getCommunity(getCommunityForm));
- promises.push(Promise.resolve({ state: "empty" }));
+
- communityResponse = client.getCommunity(getCommunityForm);
++ communityResponse = await client.getCommunity(getCommunityForm);
} else {
const listCommunitiesForm: ListCommunities = {
type_: defaultListingType,
limit: fetchLimit,
auth,
};
- promises.push(Promise.resolve({ state: "empty" }));
- promises.push(client.listCommunities(listCommunitiesForm));
+
- listCommunitiesResponse = client.listCommunities(listCommunitiesForm);
++ listCommunitiesResponse = await client.listCommunities(
++ listCommunitiesForm
++ );
}
const creator_id = getIdFromString(creatorId);
- let creatorDetailsResponse: Promise<GetPersonDetailsResponse> | undefined =
- undefined;
++ let creatorDetailsResponse:
++ | RequestState<GetPersonDetailsResponse>
++ | undefined = undefined;
if (creator_id) {
const getCreatorForm: GetPersonDetails = {
person_id: creator_id,
auth,
};
- promises.push(client.getPersonDetails(getCreatorForm));
- } else {
- promises.push(Promise.resolve({ state: "empty" }));
+
- creatorDetailsResponse = client.getPersonDetails(getCreatorForm);
++ creatorDetailsResponse = await client.getPersonDetails(getCreatorForm);
}
const query = getSearchQueryFromQuery(q);
- let searchResponse: Promise<SearchResponse> | undefined = undefined;
- let resolveObjectResponse:
- | Promise<ResolveObjectResponse | undefined>
- | undefined = undefined;
++ let searchResponse: RequestState<SearchResponse> | undefined = undefined;
++ let resolveObjectResponse: RequestState<ResolveObjectResponse> | undefined =
++ undefined;
+
if (query) {
const form: SearchForm = {
q: query,
};
if (query !== "") {
- searchResponse = client.search(form);
- promises.push(client.search(form));
++ searchResponse = await client.search(form);
if (auth) {
const resolveObjectForm: ResolveObject = {
q: query,
auth,
};
- resolveObjectResponse = client
- promises.push(client.resolveObject(resolveObjectForm));
++ resolveObjectResponse = await client
+ .resolveObject(resolveObjectForm)
+ .catch(() => undefined);
}
- } else {
- promises.push(Promise.resolve({ state: "empty" }));
- promises.push(Promise.resolve({ state: "empty" }));
}
}
minLength={1}
/>
<button type="submit" className="btn btn-secondary mr-2 mb-2">
- {this.state.searchLoading ? (
- {this.state.searchRes.state == "loading" ? (
++ {this.state.searchRes.state === "loading" ? (
<Spinner />
) : (
<span>{i18n.t("search")}</span>
/**
* This contains serialized data, it needs to be deserialized before use.
*/
- export interface IsoData<T extends object = any> {
-export interface IsoData {
++export interface IsoData<T extends Record<string, RequestState<any>> = any> {
path: string;
- routeData: RequestState<any>[];
+ routeData: T;
site_res: GetSiteResponse;
errorPageData?: ErrorPageData;
}
- export type IsoDataOptionalSite<T extends object = any> = Partial<IsoData<T>> &
-export type IsoDataOptionalSite = Partial<IsoData> &
- Pick<IsoData, Exclude<keyof IsoData, "site_res">>;
++export type IsoDataOptionalSite<
++ T extends Record<string, RequestState<any>> = any
++> = Partial<IsoData<T>> &
+ Pick<IsoData<T>, Exclude<keyof IsoData<T>, "site_res">>;
export interface ILemmyConfig {
wsHost?: string;
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
import { Search } from "./components/search";
import { InitialFetchRequest } from "./interfaces";
- import { WithPromiseKeys } from "./utils";
+ import { RequestState } from "./services/HttpService";
- interface IRoutePropsWithFetch<T extends object> extends IRouteProps {
-interface IRoutePropsWithFetch extends IRouteProps {
++interface IRoutePropsWithFetch<T extends Record<string, RequestState<any>>>
++ extends IRouteProps {
// TODO Make sure this one is good.
- fetchInitialData?(req: InitialFetchRequest): WithPromiseKeys<T>;
- fetchInitialData?(req: InitialFetchRequest): Promise<RequestState<any>>[];
++ fetchInitialData?(req: InitialFetchRequest): T;
}
-export const routes: IRoutePropsWithFetch[] = [
+export const routes: IRoutePropsWithFetch<Record<string, any>>[] = [
{
path: `/`,
component: Home,
--- /dev/null
-type FailedRequestState = {
+ import { LemmyHttp } from "lemmy-js-client";
+ import { getHttpBase } from "../../shared/env";
+ import { i18n } from "../../shared/i18next";
+ import { toast } from "../../shared/utils";
+
+ type EmptyRequestState = {
+ state: "empty";
+ };
+
+ type LoadingRequestState = {
+ state: "loading";
+ };
+
- state: "success",
++export type FailedRequestState = {
+ state: "failed";
+ msg: string;
+ };
+
+ type SuccessRequestState<T> = {
+ state: "success";
+ data: T;
+ };
+
+ /**
+ * Shows the state of an API request.
+ *
+ * Can be empty, loading, failed, or success
+ */
+ export type RequestState<T> =
+ | EmptyRequestState
+ | LoadingRequestState
+ | FailedRequestState
+ | SuccessRequestState<T>;
+
+ export type WrappedLemmyHttp = {
+ [K in keyof LemmyHttp]: LemmyHttp[K] extends (...args: any[]) => any
+ ? ReturnType<LemmyHttp[K]> extends Promise<infer U>
+ ? (...args: Parameters<LemmyHttp[K]>) => Promise<RequestState<U>>
+ : (
+ ...args: Parameters<LemmyHttp[K]>
+ ) => Promise<RequestState<LemmyHttp[K]>>
+ : LemmyHttp[K];
+ };
+
+ class WrappedLemmyHttpClient {
+ #client: LemmyHttp;
+
+ constructor(client: LemmyHttp) {
+ this.#client = client;
+
+ for (const key of Object.getOwnPropertyNames(
+ Object.getPrototypeOf(this.#client)
+ )) {
+ if (key !== "constructor") {
+ WrappedLemmyHttpClient.prototype[key] = async (...args) => {
+ try {
+ const res = await this.#client[key](...args);
+
+ return {
+ data: res,
++ state: !(res === undefined || res === null) ? "success" : "empty",
+ };
+ } catch (error) {
+ console.error(`API error: ${error}`);
+ toast(i18n.t(error), "danger");
+ return {
+ state: "failed",
+ msg: error,
+ };
+ }
+ };
+ }
+ }
+ }
+ }
+
+ export function wrapClient(client: LemmyHttp) {
+ return new WrappedLemmyHttpClient(client) as unknown as WrappedLemmyHttp; // unfortunately, this verbose cast is necessary
+ }
+
+ export class HttpService {
+ static #_instance: HttpService;
+ #client: WrappedLemmyHttp;
+
+ private constructor() {
+ this.#client = wrapClient(new LemmyHttp(getHttpBase()));
+ }
+
+ static get #Instance() {
+ return this.#_instance ?? (this.#_instance = new this());
+ }
+
+ public static get client() {
+ return this.#Instance.#client;
+ }
+ }
import Toastify from "toastify-js";
import { getHttpBase } from "./env";
import { i18n, languages } from "./i18next";
- import { CommentNodeI, DataType, IsoData } from "./interfaces";
- import { UserService, WebSocketService } from "./services";
+ import { CommentNodeI, DataType, IsoData, VoteType } from "./interfaces";
+ import { HttpService, UserService } from "./services";
++import { RequestState } from "./services/HttpService";
let Tribute: any;
if (isBrowser()) {
return typeof window !== "undefined";
}
- export function setIsoData<T extends object>(context: any): IsoData<T> {
-export function setIsoData(context: any): IsoData {
++export function setIsoData<T extends Record<string, RequestState<any>>>(
++ context: any
++): IsoData<T> {
// If its the browser, you need to deserialize the data from the window
if (isBrowser()) {
return window.isoData;
navigator.share(shareData);
}
}
+
+ export function newVote(voteType: VoteType, myVote?: number): number {
+ if (voteType == VoteType.Upvote) {
+ return myVote == 1 ? 0 : 1;
+ } else {
+ return myVote == -1 ? 0 : -1;
+ }
+ }
++
++export type RouteDataResponse<T extends Record<string, any>> = {
++ [K in keyof T]: RequestState<Exclude<T[K], undefined>>;
++};