]> Untitled Git - lemmy-ui.git/blobdiff - src/shared/components/post/create-post.tsx
Merge branch 'LemmyNet:main' into breakout-role-utils
[lemmy-ui.git] / src / shared / components / post / create-post.tsx
index 6d4122cef4deafd069277ee5bab5b0ffb9ddf529..63ee390f8ad0d85e36b5a040bef25b80c16460a5 100644 (file)
-import { Either, Left, None, Option, Right, Some } from "@sniptt/monads";
 import { Component } from "inferno";
+import { RouteComponentProps } from "inferno-router/dist/Route";
 import {
+  CreatePost as CreatePostI,
   GetCommunity,
-  GetCommunityResponse,
   GetSiteResponse,
-  ListCommunities,
   ListCommunitiesResponse,
-  ListingType,
-  PostView,
-  SortType,
-  toOption,
-  UserOperation,
-  wsJsonToRes,
-  wsUserOp,
 } from "lemmy-js-client";
-import { Subscription } from "rxjs";
-import { InitialFetchRequest, PostFormParams } from "shared/interfaces";
 import { i18n } from "../../i18next";
-import { UserService, WebSocketService } from "../../services";
+import { InitialFetchRequest, PostFormParams } from "../../interfaces";
+import { FirstLoadService } from "../../services/FirstLoadService";
 import {
-  auth,
+  HttpService,
+  RequestState,
+  WrappedLemmyHttp,
+} from "../../services/HttpService";
+import {
+  Choice,
   enableDownvotes,
   enableNsfw,
-  fetchLimit,
-  isBrowser,
+  getIdFromString,
+  myAuth,
   setIsoData,
-  toast,
-  wsClient,
-  wsSubscribe,
 } from "../../utils";
+import { getQueryParams } from "../../utils/helpers/get-query-params";
+import type { QueryParams } from "../../utils/types/query-params";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
 import { PostForm } from "./post-form";
 
+export interface CreatePostProps {
+  communityId?: number;
+}
+
+function getCreatePostQueryParams() {
+  return getQueryParams<CreatePostProps>({
+    communityId: getIdFromString,
+  });
+}
+
+function fetchCommunitiesForOptions(client: WrappedLemmyHttp) {
+  return client.listCommunities({ limit: 30, sort: "TopMonth", type_: "All" });
+}
+
 interface CreatePostState {
-  listCommunitiesResponse: Option<ListCommunitiesResponse>;
   siteRes: GetSiteResponse;
   loading: boolean;
+  selectedCommunityChoice?: Choice;
+  initialCommunitiesRes: RequestState<ListCommunitiesResponse>;
+  isIsomorphic: boolean;
 }
 
-export class CreatePost extends Component<any, CreatePostState> {
-  private isoData = setIsoData(this.context, ListCommunitiesResponse);
-  private subscription: Subscription;
-  private emptyState: CreatePostState = {
+export class CreatePost extends Component<
+  RouteComponentProps<Record<string, never>>,
+  CreatePostState
+> {
+  private isoData = setIsoData(this.context);
+  state: CreatePostState = {
     siteRes: this.isoData.site_res,
-    listCommunitiesResponse: None,
     loading: true,
+    initialCommunitiesRes: { state: "empty" },
+    isIsomorphic: false,
   };
 
-  constructor(props: any, context: any) {
+  constructor(props: RouteComponentProps<Record<string, never>>, context: any) {
     super(props, context);
-    this.state = this.emptyState;
 
     this.handlePostCreate = this.handlePostCreate.bind(this);
+    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 (FirstLoadService.isFirstLoad) {
+      const [communityRes, listCommunitiesRes] = this.isoData.routeData;
 
-    if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
-      toast(i18n.t("not_logged_in"), "danger");
-      this.context.router.history.push(`/login`);
-    }
+      if (communityRes?.state === "success") {
+        const communityChoice: Choice = {
+          label: communityRes.data.community_view.community.title,
+          value: communityRes.data.community_view.community.id.toString(),
+        };
+
+        this.state = {
+          ...this.state,
+          selectedCommunityChoice: communityChoice,
+        };
+      }
 
-    // Only fetch the data if coming from another route
-    if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        listCommunitiesResponse: Some(
-          this.isoData.routeData[0] as ListCommunitiesResponse
-        ),
         loading: false,
+        initialCommunitiesRes: listCommunitiesRes,
+        isIsomorphic: true,
       };
-    } else {
-      this.refetch();
     }
   }
 
-  refetch() {
-    this.params.nameOrId.match({
-      some: opt =>
-        opt.match({
-          left: name => {
-            let form = new GetCommunity({
-              name: Some(name),
-              id: None,
-              auth: auth(false).ok(),
-            });
-            WebSocketService.Instance.send(wsClient.getCommunity(form));
-          },
-          right: id => {
-            let form = new GetCommunity({
-              id: Some(id),
-              name: None,
-              auth: auth(false).ok(),
-            });
-            WebSocketService.Instance.send(wsClient.getCommunity(form));
+  async fetchCommunity() {
+    const { communityId } = getCreatePostQueryParams();
+    const auth = myAuth();
+
+    if (communityId) {
+      const res = await HttpService.client.getCommunity({
+        id: communityId,
+        auth,
+      });
+      if (res.state === "success") {
+        this.setState({
+          selectedCommunityChoice: {
+            label: res.data.community_view.community.name,
+            value: res.data.community_view.community.id.toString(),
           },
-        }),
-      none: () => {
-        let listCommunitiesForm = new ListCommunities({
-          type_: Some(ListingType.All),
-          sort: Some(SortType.TopAll),
-          limit: Some(fetchLimit),
-          page: None,
-          auth: auth(false).ok(),
+          loading: false,
         });
-        WebSocketService.Instance.send(
-          wsClient.listCommunities(listCommunitiesForm)
-        );
-      },
-    });
+      }
+    }
   }
 
-  componentWillUnmount() {
-    if (isBrowser()) {
-      this.subscription.unsubscribe();
+  async componentDidMount() {
+    // TODO test this
+    if (!this.state.isIsomorphic) {
+      const { communityId } = getCreatePostQueryParams();
+
+      const initialCommunitiesRes = await fetchCommunitiesForOptions(
+        HttpService.client
+      );
+
+      this.setState({
+        initialCommunitiesRes,
+      });
+
+      if (
+        communityId?.toString() !== this.state.selectedCommunityChoice?.value
+      ) {
+        await this.fetchCommunity();
+      } else if (!communityId) {
+        this.setState({
+          selectedCommunityChoice: undefined,
+          loading: false,
+        });
+      }
     }
   }
 
@@ -125,124 +148,114 @@ export class CreatePost extends Component<any, CreatePostState> {
   }
 
   render() {
+    const { selectedCommunityChoice } = this.state;
+
+    const locationState = this.props.history.location.state as
+      | PostFormParams
+      | undefined;
+
     return (
       <div className="container-lg">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         {this.state.loading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          this.state.listCommunitiesResponse.match({
-            some: res => (
-              <div className="row">
-                <div className="col-12 col-lg-6 offset-lg-3 mb-4">
-                  <h5>{i18n.t("create_post")}</h5>
-                  <PostForm
-                    post_view={None}
-                    communities={Some(res.communities)}
-                    onCreate={this.handlePostCreate}
-                    params={Some(this.params)}
-                    enableDownvotes={enableDownvotes(this.state.siteRes)}
-                    enableNsfw={enableNsfw(this.state.siteRes)}
-                    allLanguages={this.state.siteRes.all_languages}
-                  />
-                </div>
-              </div>
-            ),
-            none: <></>,
-          })
+          <div className="row">
+            <div
+              id="createPostForm"
+              className="col-12 col-lg-6 offset-lg-3 mb-4"
+            >
+              <h5>{i18n.t("create_post")}</h5>
+              <PostForm
+                onCreate={this.handlePostCreate}
+                params={locationState}
+                enableDownvotes={enableDownvotes(this.state.siteRes)}
+                enableNsfw={enableNsfw(this.state.siteRes)}
+                allLanguages={this.state.siteRes.all_languages}
+                siteLanguages={this.state.siteRes.discussion_languages}
+                selectedCommunityChoice={selectedCommunityChoice}
+                onSelectCommunity={this.handleSelectedCommunityChange}
+                initialCommunities={
+                  this.state.initialCommunitiesRes.state === "success"
+                    ? this.state.initialCommunitiesRes.data.communities
+                    : []
+                }
+              />
+            </div>
+          </div>
         )}
       </div>
     );
   }
 
-  get params(): PostFormParams {
-    let urlParams = new URLSearchParams(this.props.location.search);
-    let name = toOption(urlParams.get("community_name")).or(
-      this.prevCommunityName
-    );
-    let id = toOption(urlParams.get("community_id"))
-      .map(Number)
-      .or(this.prevCommunityId);
-    let nameOrId: Option<Either<string, number>>;
-    if (name.isSome()) {
-      nameOrId = Some(Left(name.unwrap()));
-    } else if (id.isSome()) {
-      nameOrId = Some(Right(id.unwrap()));
-    } else {
-      nameOrId = None;
-    }
+  async updateUrl({ communityId }: Partial<CreatePostProps>) {
+    const { communityId: urlCommunityId } = getCreatePostQueryParams();
 
-    let params: PostFormParams = {
-      name: toOption(urlParams.get("title")),
-      nameOrId,
-      body: toOption(urlParams.get("body")),
-      url: toOption(urlParams.get("url")),
-    };
+    const locationState = this.props.history.location.state as
+      | PostFormParams
+      | undefined;
 
-    return params;
-  }
+    const url = new URL(location.href);
 
-  get prevCommunityName(): Option<string> {
-    if (this.props.match.params.name) {
-      return toOption(this.props.match.params.name);
-    } else if (this.props.location.state) {
-      let lastLocation = this.props.location.state.prevPath;
-      if (lastLocation.includes("/c/")) {
-        return toOption(lastLocation.split("/c/")[1]);
-      }
-    }
-    return None;
-  }
+    const newId = (communityId ?? urlCommunityId)?.toString();
 
-  get prevCommunityId(): Option<number> {
-    if (this.props.match.params.id) {
-      return toOption(this.props.match.params.id);
+    if (newId !== undefined) {
+      url.searchParams.set("communityId", newId);
+    } else {
+      url.searchParams.delete("communityId");
     }
-    return None;
-  }
 
-  handlePostCreate(post_view: PostView) {
-    this.props.history.push(`/post/${post_view.post.id}`);
+    history.replaceState(locationState, "", url);
+
+    await this.fetchCommunity();
   }
 
-  static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
-    let listCommunitiesForm = new ListCommunities({
-      type_: Some(ListingType.All),
-      sort: Some(SortType.TopAll),
-      limit: Some(fetchLimit),
-      page: None,
-      auth: req.auth,
+  handleSelectedCommunityChange(choice: Choice) {
+    this.updateUrl({
+      communityId: getIdFromString(choice?.value),
     });
-    return [req.client.listCommunities(listCommunitiesForm)];
   }
 
-  parseMessage(msg: any) {
-    let op = wsUserOp(msg);
-    console.log(msg);
-    if (msg.error) {
-      toast(i18n.t(msg.error), "danger");
-      return;
-    } else if (op == UserOperation.ListCommunities) {
-      let data = wsJsonToRes<ListCommunitiesResponse>(
-        msg,
-        ListCommunitiesResponse
-      );
-      this.setState({ listCommunitiesResponse: Some(data), loading: false });
-    } else if (op == UserOperation.GetCommunity) {
-      let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
+  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}`);
+    } else {
       this.setState({
-        listCommunitiesResponse: Some({
-          communities: [data.community_view],
-        }),
         loading: false,
       });
     }
   }
+
+  static fetchInitialData({
+    client,
+    query: { communityId },
+    auth,
+  }: InitialFetchRequest<QueryParams<CreatePostProps>>): Promise<
+    RequestState<any>
+  >[] {
+    const promises: Promise<RequestState<any>>[] = [];
+
+    if (communityId) {
+      const form: GetCommunity = {
+        auth,
+        id: getIdFromString(communityId),
+      };
+
+      promises.push(client.getCommunity(form));
+    } else {
+      promises.push(Promise.resolve({ state: "empty" }));
+    }
+
+    promises.push(fetchCommunitiesForOptions(client));
+
+    return promises;
+  }
 }