mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-15 14:22:14 +00:00
Update comments component - reload, better reusability
This commit is contained in:
1 parent
6721bc724d
commit
c2f1dafe8b
4 files changed
+98
-47
No files matched your search
@@ -0,0 +1,60 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import {
|
||||
Comment,
|
||||
ListComments,
|
||||
ListCommentsIsError,
|
||||
ListCommentsIsOk,
|
||||
} from "@storyteller/components/src/api/comments/ListComments";
|
||||
import { CreateCommentForm } from "./CreateCommentForm";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { CommentList } from "./CommentList";
|
||||
|
||||
interface Props {
|
||||
entityType: string;
|
||||
entityToken: string;
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a reusable component that can be put on several different pages.
|
||||
*
|
||||
* It requires the entity type ("tts_model", "tts_result", "w2l_template", "w2l_result", etc.)
|
||||
* and the entity token, and it will be able to fetch a user's previous vote and change it.
|
||||
*
|
||||
* This button component manages all of its own state and API calls.
|
||||
*/
|
||||
function CommentComponent(props: Props) {
|
||||
const [comments, setComments] = useState<Array<Comment>>([]);
|
||||
|
||||
const loadComments = useCallback(async () => {
|
||||
const response = await ListComments(props.entityType, props.entityToken);
|
||||
|
||||
if (ListCommentsIsOk(response)) {
|
||||
setComments(response.comments);
|
||||
} else if (ListCommentsIsError(response)) {
|
||||
// TODO
|
||||
}
|
||||
}, [props.entityType, props.entityToken]);
|
||||
|
||||
useEffect(() => {
|
||||
loadComments();
|
||||
}, [loadComments]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CreateCommentForm
|
||||
entityType={props.entityType}
|
||||
entityToken={props.entityToken}
|
||||
loadComments={loadComments}
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
/>
|
||||
<CommentList
|
||||
entityType={props.entityType}
|
||||
entityToken={props.entityToken}
|
||||
comments={comments}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { CommentComponent };
|
||||
@@ -1,38 +1,27 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import React from "react";
|
||||
import { formatDistance } from "date-fns";
|
||||
import {
|
||||
Comment,
|
||||
ListComments,
|
||||
ListCommentsIsError,
|
||||
ListCommentsIsOk,
|
||||
} from "@storyteller/components/src/api/comments/ListComments";
|
||||
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
interface Props {
|
||||
entity_type: string;
|
||||
entity_token: string;
|
||||
entityType: string;
|
||||
entityToken: string;
|
||||
comments: Comment[];
|
||||
}
|
||||
|
||||
/**
|
||||
* This is part of a reusable component for putting comments on several
|
||||
* different page types.
|
||||
*
|
||||
* See the documentation on the parent <CommentComponent />
|
||||
*/
|
||||
function CommentList(props: Props) {
|
||||
const [comments, setComments] = useState<Array<Comment>>([]);
|
||||
|
||||
const getComments = useCallback(async () => {
|
||||
const response = await ListComments(props.entity_type, props.entity_token);
|
||||
|
||||
if (ListCommentsIsOk(response)) {
|
||||
setComments(response.comments);
|
||||
} else if (ListCommentsIsError(response)) {
|
||||
// TODO
|
||||
}
|
||||
}, [props.entity_token, props.entity_type]);
|
||||
|
||||
useEffect(() => {
|
||||
getComments();
|
||||
}, [getComments]);
|
||||
|
||||
// NB: It's more convenient to show recent data first {.reverse()}
|
||||
var reversedComments = comments.slice();
|
||||
var reversedComments = props.comments.slice();
|
||||
|
||||
const now = new Date();
|
||||
|
||||
@@ -55,7 +44,12 @@ function CommentList(props: Props) {
|
||||
<span className="px-2">·</span>
|
||||
<span className="opacity-75">{relativeCreateTime}</span>
|
||||
</div>
|
||||
{/* It's okay to set "dangerous" html here as the server safely created it. */}
|
||||
{/*
|
||||
It's okay to set "dangerous" html here as the server safely created
|
||||
it from markdown and shields against user injection attempts. Don't
|
||||
do this with other server data, though, unless you know that field
|
||||
is safe from the backend engineers.
|
||||
*/}
|
||||
<div
|
||||
className="mt-1 text-center text-lg-start"
|
||||
dangerouslySetInnerHTML={{
|
||||
|
||||
+17
-15
@@ -7,20 +7,21 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
|
||||
interface Props {
|
||||
entity_type: string;
|
||||
entity_token: string;
|
||||
entityType: string;
|
||||
entityToken: string;
|
||||
loadComments: () => void;
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a reusable component that can be put on several different pages.
|
||||
* This is part of a reusable component for putting comments on several
|
||||
* different page types.
|
||||
*
|
||||
* It requires the entity type ("tts_model", "tts_result", "w2l_template", "w2l_result", etc.)
|
||||
* and the entity token, and it will be able to fetch a user's previous vote and change it.
|
||||
*
|
||||
* This button component manages all of its own state and API calls.
|
||||
* See the documentation on the parent <CommentComponent />
|
||||
*/
|
||||
function CreateCommentComponent(props: Props) {
|
||||
function CreateCommentForm(props: Props) {
|
||||
const {entityType, entityToken, loadComments} = props;
|
||||
|
||||
const [commentMarkdown, setCommentMarkdown] = useState<string>("");
|
||||
const [uuidIdempotencyToken, setUuidIdempotencyToken] = useState<string>(
|
||||
uuidv4()
|
||||
@@ -29,19 +30,20 @@ function CreateCommentComponent(props: Props) {
|
||||
|
||||
const postComment = useCallback(async () => {
|
||||
const request = {
|
||||
// Idempotency token prevents the user from clicking submit twice.
|
||||
uuid_idempotency_token: uuidIdempotencyToken,
|
||||
entity_type: props.entity_type,
|
||||
entity_token: props.entity_token,
|
||||
entity_type: entityType,
|
||||
entity_token: entityToken,
|
||||
comment_markdown: commentMarkdown,
|
||||
};
|
||||
const rating = await CreateComment(request);
|
||||
if (CreateCommentIsOk(rating)) {
|
||||
//let ratingValue = rating.maybe_rating_value || undefined;
|
||||
//setUserRatingValue(ratingValue);
|
||||
loadComments(); // Trigger reload.
|
||||
}
|
||||
}, [
|
||||
props.entity_type,
|
||||
props.entity_token,
|
||||
entityType,
|
||||
entityToken,
|
||||
loadComments,
|
||||
uuidIdempotencyToken,
|
||||
commentMarkdown,
|
||||
]);
|
||||
@@ -117,4 +119,4 @@ function CreateCommentComponent(props: Props) {
|
||||
return <div>{createCommentComponent}</div>;
|
||||
}
|
||||
|
||||
export { CreateCommentComponent };
|
||||
export { CreateCommentForm };
|
||||
@@ -39,8 +39,7 @@ import "tippy.js/dist/tippy.css";
|
||||
import { motion } from "framer-motion";
|
||||
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
|
||||
import { faCalendarAlt } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { CreateCommentComponent } from "../../../_common/comments/CreateCommentComponent";
|
||||
import { CommentList } from "../../../_common/comments/CommentList";
|
||||
import { CommentComponent } from "../../../_common/comments/CommentComponent";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -537,15 +536,11 @@ function ProfilePage(this: any, props: Props) {
|
||||
aria-labelledby="comments-tab"
|
||||
>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<CreateCommentComponent
|
||||
entity_type="user"
|
||||
entity_token={userData?.user_token}
|
||||
<CommentComponent
|
||||
entityType="user"
|
||||
entityToken={userData?.user_token}
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
/>
|
||||
<CommentList
|
||||
entity_type="user"
|
||||
entity_token={userData?.user_token}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user