]> 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 d2945b588f73253820ff9ff8375362fb3be6f70b..63ee390f8ad0d85e36b5a040bef25b80c16460a5 100644 (file)
 import { Component } from "inferno";
+import { RouteComponentProps } from "inferno-router/dist/Route";
 import {
-  CommunityView,
+  CreatePost as CreatePostI,
   GetCommunity,
-  GetCommunityResponse,
-  ListCommunities,
+  GetSiteResponse,
   ListCommunitiesResponse,
-  ListingType,
-  PostView,
-  SiteView,
-  SortType,
-  UserOperation,
 } 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 {
-  authField,
-  fetchLimit,
-  isBrowser,
+  HttpService,
+  RequestState,
+  WrappedLemmyHttp,
+} from "../../services/HttpService";
+import {
+  Choice,
+  enableDownvotes,
+  enableNsfw,
+  getIdFromString,
+  myAuth,
   setIsoData,
-  setOptionalAuth,
-  toast,
-  wsClient,
-  wsJsonToRes,
-  wsSubscribe,
-  wsUserOp,
 } 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 {
-  site_view: SiteView;
-  communities: CommunityView[];
+  siteRes: GetSiteResponse;
   loading: boolean;
+  selectedCommunityChoice?: Choice;
+  initialCommunitiesRes: RequestState<ListCommunitiesResponse>;
+  isIsomorphic: boolean;
 }
 
-export class CreatePost extends Component<any, CreatePostState> {
+export class CreatePost extends Component<
+  RouteComponentProps<Record<string, never>>,
+  CreatePostState
+> {
   private isoData = setIsoData(this.context);
-  private subscription: Subscription;
-  private emptyState: CreatePostState = {
-    site_view: this.isoData.site_res.site_view,
-    communities: [],
+  state: CreatePostState = {
+    siteRes: this.isoData.site_res,
     loading: true,
+    initialCommunitiesRes: { state: "empty" },
+    isIsomorphic: false,
   };
 
-  constructor(props: any, context: any) {
+  constructor(props: RouteComponentProps<Record<string, never>>, context: any) {
     super(props, context);
+
     this.handlePostCreate = this.handlePostCreate.bind(this);
-    this.state = this.emptyState;
+    this.handleSelectedCommunityChange =
+      this.handleSelectedCommunityChange.bind(this);
 
-    if (!UserService.Instance.localUserView && isBrowser()) {
-      toast(i18n.t("not_logged_in"), "danger");
-      this.context.router.history.push(`/login`);
-    }
+    // Only fetch the data if coming from another route
+    if (FirstLoadService.isFirstLoad) {
+      const [communityRes, listCommunitiesRes] = this.isoData.routeData;
 
-    this.parseMessage = this.parseMessage.bind(this);
-    this.subscription = wsSubscribe(this.parseMessage);
+      if (communityRes?.state === "success") {
+        const communityChoice: Choice = {
+          label: communityRes.data.community_view.community.title,
+          value: communityRes.data.community_view.community.id.toString(),
+        };
 
-    // Only fetch the data if coming from another route
-    if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.communities = this.isoData.routeData[0].communities;
-      this.state.loading = false;
-    } else {
-      this.refetch();
+        this.state = {
+          ...this.state,
+          selectedCommunityChoice: communityChoice,
+        };
+      }
+
+      this.state = {
+        ...this.state,
+        loading: false,
+        initialCommunitiesRes: listCommunitiesRes,
+        isIsomorphic: true,
+      };
     }
   }
 
-  refetch() {
-    if (this.params.community_id) {
-      let form: GetCommunity = {
-        id: this.params.community_id,
-      };
-      WebSocketService.Instance.send(wsClient.getCommunity(form));
-    } else if (this.params.community_name) {
-      let form: GetCommunity = {
-        name: this.params.community_name,
-      };
-      WebSocketService.Instance.send(wsClient.getCommunity(form));
-    } else {
-      let listCommunitiesForm: ListCommunities = {
-        type_: ListingType.All,
-        sort: SortType.TopAll,
-        limit: fetchLimit,
-        auth: authField(false),
-      };
-      WebSocketService.Instance.send(
-        wsClient.listCommunities(listCommunitiesForm)
-      );
+  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(),
+          },
+          loading: false,
+        });
+      }
     }
   }
 
-  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,
+        });
+      }
     }
   }
 
   get documentTitle(): string {
-    return `${i18n.t("create_post")} - ${this.state.site_view.site.name}`;
+    return `${i18n.t("create_post")} - ${
+      this.state.siteRes.site_view.site.name
+    }`;
   }
 
   render() {
+    const { selectedCommunityChoice } = this.state;
+
+    const locationState = this.props.history.location.state as
+      | PostFormParams
+      | undefined;
+
     return (
-      <div class="container">
+      <div className="container-lg">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
@@ -114,15 +165,26 @@ export class CreatePost extends Component<any, CreatePostState> {
             <Spinner large />
           </h5>
         ) : (
-          <div class="row">
-            <div class="col-12 col-lg-6 offset-lg-3 mb-4">
+          <div className="row">
+            <div
+              id="createPostForm"
+              className="col-12 col-lg-6 offset-lg-3 mb-4"
+            >
               <h5>{i18n.t("create_post")}</h5>
               <PostForm
-                communities={this.state.communities}
                 onCreate={this.handlePostCreate}
-                params={this.params}
-                enableDownvotes={this.state.site_view.site.enable_downvotes}
-                enableNsfw={this.state.site_view.site.enable_nsfw}
+                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>
@@ -131,70 +193,69 @@ export class CreatePost extends Component<any, CreatePostState> {
     );
   }
 
-  get params(): PostFormParams {
-    let urlParams = new URLSearchParams(this.props.location.search);
-    let params: PostFormParams = {
-      name: urlParams.get("title"),
-      community_name: urlParams.get("community_name") || this.prevCommunityName,
-      community_id: urlParams.get("community_id")
-        ? Number(urlParams.get("community_id")) || this.prevCommunityId
-        : null,
-      body: urlParams.get("body"),
-      url: urlParams.get("url"),
-    };
-
-    return params;
-  }
+  async updateUrl({ communityId }: Partial<CreatePostProps>) {
+    const { communityId: urlCommunityId } = getCreatePostQueryParams();
 
-  get prevCommunityName(): string {
-    if (this.props.match.params.name) {
-      return this.props.match.params.name;
-    } else if (this.props.location.state) {
-      let lastLocation = this.props.location.state.prevPath;
-      if (lastLocation.includes("/c/")) {
-        return lastLocation.split("/c/")[1];
-      }
-    }
-    return null;
-  }
+    const locationState = this.props.history.location.state as
+      | PostFormParams
+      | undefined;
 
-  get prevCommunityId(): number {
-    if (this.props.match.params.id) {
-      return this.props.match.params.id;
+    const url = new URL(location.href);
+
+    const newId = (communityId ?? urlCommunityId)?.toString();
+
+    if (newId !== undefined) {
+      url.searchParams.set("communityId", newId);
+    } else {
+      url.searchParams.delete("communityId");
     }
-    return null;
+
+    history.replaceState(locationState, "", url);
+
+    await this.fetchCommunity();
   }
 
-  handlePostCreate(post_view: PostView) {
-    this.props.history.push(`/post/${post_view.post.id}`);
+  handleSelectedCommunityChange(choice: Choice) {
+    this.updateUrl({
+      communityId: getIdFromString(choice?.value),
+    });
   }
 
-  static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
-    let listCommunitiesForm: ListCommunities = {
-      type_: ListingType.All,
-      sort: SortType.TopAll,
-      limit: fetchLimit,
-    };
-    setOptionalAuth(listCommunitiesForm, req.auth);
-    return [req.client.listCommunities(listCommunitiesForm)];
+  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({
+        loading: false,
+      });
+    }
   }
 
-  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).data;
-      this.state.communities = data.communities;
-      this.state.loading = false;
-      this.setState(this.state);
-    } else if (op == UserOperation.GetCommunity) {
-      let data = wsJsonToRes<GetCommunityResponse>(msg).data;
-      this.state.communities = [data.community_view];
-      this.state.loading = false;
-      this.setState(this.state);
+  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;
   }
 }