Skip to content

Commit

Permalink
add single page
Browse files Browse the repository at this point in the history
  • Loading branch information
cansirin committed Aug 25, 2023
1 parent 44d977f commit ac1d707
Show file tree
Hide file tree
Showing 29 changed files with 4,454 additions and 7 deletions.
127 changes: 127 additions & 0 deletions apps/kampus/app/pano/CreatePostCommentForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"use client";

import { useParams } from "next/navigation";
import { graphql, useFragment, useMutation } from "react-relay";
import { z } from "zod";

import {
Button,
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
Textarea,
useForm,
} from "@kampus/ui";

import { CreatePostCommentForm_viewer$key } from "./__generated__/CreatePostCommentForm_viewer.graphql";

const mutation = graphql`
mutation CreatePostCommentFormMutation(
$content: String!
$postID: String!
$connections: [ID!]!
) {
createPanoComment(input: { content: $content, postID: $postID }) {
edge @prependEdge(connections: $connections) {
cursor
node {
id
content
createdAt
owner {
displayName
}
}
}
error {
... on UserError {
message
}
}
}
}
`;

const viewerFragment = graphql`
fragment CreatePostCommentForm_viewer on Viewer {
actor {
displayName
}
}
`;

const formSchema = z.object({
content: z.string().min(1, { message: "Yorum kısmı boş bırakılamaz." }),
});

type FormSchema = z.infer<typeof formSchema>;

interface Props {
viewer: CreatePostCommentForm_viewer$key;
connectionID?: string;
onCompleted?: () => void;
}

export function CreatePanoCommentForm(props: Props) {
const [commit, isInFlight] = useMutation(mutation);
const { id: postID } = useParams();
const { actor } = useFragment(viewerFragment, props.viewer);

const form = useForm(formSchema, {
defaultValues: {
content: "",
},
});

const onSubmit = (values: FormSchema) => {
commit({
variables: {
content: values.content,
postID: decodeURIComponent(postID!),
connections: [props.connectionID].filter(Boolean),
},
onError: (error) => {
console.error(error);
},
onCompleted: () => {
props.onCompleted?.();
},
});
};

return (
<Form {...form}>
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
<form className="flex flex-col gap-2" onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="content"
render={(props) => (
<FormItem>
<FormLabel>Senin yorumun</FormLabel>
<FormControl>
<Textarea
placeholder={
actor ? "Yorumunu buraya ekle..." : "Yorum yapmak için giriş yapmalısın."
}
className="max-h-64 resize-y"
disabled={!actor}
{...props.field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex">
<Button type="submit" disabled={isInFlight || !actor}>
Gönder
</Button>
</div>
</form>
</Form>
);
}
48 changes: 48 additions & 0 deletions apps/kampus/app/pano/SinglePostContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use client";

import { graphql, usePreloadedQuery } from "react-relay";

import { type SerializablePreloadedQuery } from "@kampus/relay";
import useSerializablePreloadedQuery from "@kampus/relay/use-serializable-preloaded-query";

import { SinglePostContainerQuery } from "./__generated__/SinglePostContainerQuery.graphql";
import { SinglePostFeed } from "./SinglePostFeed";

interface Props {
preloadedQuery: SerializablePreloadedQuery<SinglePostContainerQuery>;
}

const query = graphql`
query SinglePostContainerQuery($id: ID!) {
viewer {
...SinglePostFeed_viewer
}
pano {
post(id: $id) {
...SinglePostFeed_post
...SinglePostFeed_comments
}
}
}
`;

export const SinglePostContainer = (props: Props) => {
const queryRef = useSerializablePreloadedQuery(props.preloadedQuery);
const data = usePreloadedQuery(query, queryRef);

if (!data.pano.post) {
return null;
}

if (!data.viewer) {
return null;
}

console.log(data);

return (
<div>
<SinglePostFeed post={data.pano.post} viewer={data.viewer} comments={data.pano.post} />
</div>
);
};
85 changes: 85 additions & 0 deletions apps/kampus/app/pano/SinglePostFeed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { graphql, useFragment, usePaginationFragment } from "react-relay";

import { Separator } from "@kampus/ui";

import { SinglePostFeed_comments$key } from "./__generated__/SinglePostFeed_comments.graphql";
import { SinglePostFeed_post$key } from "./__generated__/SinglePostFeed_post.graphql";
import { SinglePostFeed_viewer$key } from "./__generated__/SinglePostFeed_viewer.graphql";
import { CreatePanoCommentForm } from "./CreatePostCommentForm";
import { CommentItem } from "./features/comment-list/CommentItem";
import { PostItem } from "./features/post-list/PostItem";

interface SinglePostFeedProps {
post: SinglePostFeed_post$key;
viewer: SinglePostFeed_viewer$key;
comments: SinglePostFeed_comments$key;
}

const fragment = graphql`
fragment SinglePostFeed_comments on PanoPost
@argumentDefinitions(
after: { type: "String" }
first: { type: "Int", defaultValue: 10 }
before: { type: "String" }
last: { type: "Int" }
)
@refetchable(queryName: "SinglePostFeedPaginationQuery") {
comments(first: $first, after: $after, last: $last, before: $before)
@connection(key: "SinglePostFeedFragment__comments") {
__id
edges {
node {
id
content
createdAt
owner {
displayName
}
...CommentItem_comment
}
}
}
}
`;

const postFragment = graphql`
fragment SinglePostFeed_post on PanoPost {
...PostItem_post
}
`;

const viewerFragment = graphql`
fragment SinglePostFeed_viewer on Viewer {
...PostItem_viewer
...CommentItem_viewer
...CreatePostCommentForm_viewer
}
`;

export const SinglePostFeed = (props: SinglePostFeedProps) => {
const post = useFragment(postFragment, props.post);
const viewer = useFragment(viewerFragment, props.viewer);
const { data } = usePaginationFragment(fragment, props.comments);

const comments = data.comments;

return (
<div className="flex flex-col gap-4">
<PostItem post={post} showContent viewerRef={viewer} />
<Separator />
<CreatePanoCommentForm viewer={viewer} connectionID={comments?.__id} />
<h2>Yorumlar</h2>
{comments?.edges?.map((edge) => {
if (!edge?.node) return null;
return (
<CommentItem
key={edge.node.id}
comment={edge.node}
viewerRef={viewer}
connectionID={comments.__id}
/>
);
})}
</div>
);
};
133 changes: 133 additions & 0 deletions apps/kampus/app/pano/UpdateCommentForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"use client";

import React from "react";
import { Check, X } from "lucide-react";
import { graphql, useFragment, useMutation } from "react-relay";
import { z } from "zod";

import {
Button,
Form,
FormControl,
FormField,
FormItem,
FormMessage,
Textarea,
useForm,
} from "@kampus/ui";

import { UpdateCommentForm_viewer$key } from "./__generated__/UpdateCommentForm_viewer.graphql";

const mutation = graphql`
mutation UpdateCommentFormMutation($content: String!, $id: ID!) {
updatePanoComment(input: { content: $content, id: $id }) {
edge {
cursor
node {
id
content
createdAt
owner {
displayName
}
}
}
error {
... on UserError {
message
}
}
}
}
`;

const viewerFragment = graphql`
fragment UpdateCommentForm_viewer on Viewer {
actor {
displayName
}
}
`;

const formSchema = z.object({
content: z.string().min(1, { message: "Yorum kısmı boş bırakılamaz." }),
});

type FormSchema = z.infer<typeof formSchema>;

interface Props {
viewer: UpdateCommentForm_viewer$key;
connectionID?: string;
onCompleted?: () => void;
defaultValue: string;
setEditing: React.Dispatch<React.SetStateAction<boolean>>;
commentID: string;
}

export function UpdateCommentForm(props: Props) {
const [commit, isInFlight] = useMutation(mutation);
const { actor } = useFragment(viewerFragment, props.viewer);

const form = useForm(formSchema, {
defaultValues: {
content: props.defaultValue,
},
});

const onSubmit = (values: FormSchema) => {
commit({
variables: {
content: values.content,
id: props.commentID,
connections: [props.connectionID].filter(Boolean),
},
onError: (error) => {
console.error(error);
},
onCompleted: () => {
props.onCompleted?.();
props.setEditing(false);
},
});
};

return (
<Form {...form}>
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
<form className="flex flex-col gap-2" onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="content"
render={(props) => (
<FormItem>
<FormControl>
<Textarea
placeholder={
actor ? "Yorumunu buraya ekle..." : "Yorum yapmak için giriş yapmalısın."
}
className="max-h-64 resize-y"
disabled={!actor}
{...props.field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex gap-2">
<Button
size="icon"
variant="destructive"
disabled={isInFlight}
onClick={() => props.setEditing(false)}
>
<X size={16} />
</Button>
<Button disabled={isInFlight} size="icon" type="submit">
<Check size={16} />
</Button>
</div>
</form>
</Form>
);
}
Loading

0 comments on commit ac1d707

Please sign in to comment.