Bookmarks and layout

This commit is contained in:
Max 2024-11-25 17:36:29 +03:00
parent 66178664f3
commit 9aa5cbc12a
13 changed files with 265 additions and 15 deletions

View File

@ -1,19 +1,40 @@
import { Container } from "@radix-ui/themes";
import { Container, Separator, Text } from "@radix-ui/themes";
import { useQuery } from "@tanstack/react-query";
import { Interweave } from "interweave";
import { useParams } from "react-router-dom";
import { axiosLocalhost } from "../../api/axios/axios";
import SkeletonLoader from "./SkeletonLoader/SkeletonLoader";
type TArticleViewer = {
htmlToParse?: string;
}
};
export default function ArticleViewer(props: TArticleViewer) {
const queryPapms = useParams()
let queryParams = useParams();
const { data, isPending } = useQuery({
queryKey: [`post_${queryParams["postId"]}`],
queryFn: async () => {
const response = await axiosLocalhost.get(
`posts/${queryParams["postId"]}`
);
return response.data;
},
});
if (isPending)
return (
<SkeletonLoader />
);
return (
<>
<div className="ql-snow">
<Container className="mt-4 ql-editor">
<Interweave content={props?.htmlToParse || ""} />
<Container size={"2"} className="mt-4">
<Text className="mb-2" as="div" size={"9"}>{data.title}</Text>
<Separator size={"4"} className="mb-2" />
<Interweave content={data.content} />
</Container>
</div>
</>

View File

@ -0,0 +1,27 @@
import { Container, Skeleton, Text } from "@radix-ui/themes";
import {
headerLong,
headerShort,
pText,
} from "../../../constants/textForSkeleton";
export default function SkeletonLoader() {
return (
<Container size={"2"} className="mt-4">
<Skeleton>
<Text size={"6"}>{headerLong}</Text>
<br />
<Text size={"6"}>{headerShort}</Text>
<br />
<br />
<Text>{pText}</Text>
<br />
<br />
<Text wrap={"pretty"}>{pText}</Text>
<br />
<br />
<Text>{pText}</Text>
</Skeleton>
</Container>
);
}

View File

@ -1,5 +1,5 @@
import { Box, Container, Flex } from "@radix-ui/themes";
import { useSetAtom } from "jotai";
import { useAtom, useSetAtom } from "jotai";
import {
postCreationAtom,
postCreationTitleAtom
@ -8,7 +8,7 @@ import Editor from "../../Components/Editor/Editor";
import SubmitPostButton from "./SubmitPostButton/SubmitPostButton";
export default function PostCreatorPage() {
const setTitleValue = useSetAtom(postCreationTitleAtom);
const [titleValue, setTitleValue] = useAtom(postCreationTitleAtom);
const setContentValue = useSetAtom(postCreationAtom);
return (
@ -20,11 +20,12 @@ export default function PostCreatorPage() {
<input
placeholder={"Post title"}
className="mb-2 border-0 border-b-[1px]
outline-none w-full border-b-gray-400
text-[60px] pl-4 pr-4 font-times"
outline-none w-full border-b-gray-400
text-[60px] pl-4 pr-4 font-times"
onChange={(e) => {
setTitleValue(e.target.value);
}}
value={titleValue}
/>
</Container>

View File

@ -1,6 +1,6 @@
import { Button } from "@radix-ui/themes";
import { useMutation } from "@tanstack/react-query";
import { useAtomValue } from "jotai";
import { useAtom } from "jotai";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
@ -16,15 +16,20 @@ type TSubmitPostButton = {
export default function SubmitPostButton(props: TSubmitPostButton) {
const { t } = useTranslation();
const contentValue = useAtomValue(postCreationAtom);
const titleValue = useAtomValue(postCreationTitleAtom);
const navigate = useNavigate();
const [isDisabled, setIsDisabled] = useState(false);
const [contentValue, setContentValue] = useAtom(postCreationAtom);
const [titleValue, setTitleValue] = useAtom(postCreationTitleAtom);
const navigate = useNavigate();
const postMutation = useMutation({
mutationFn: async () => {
if (!titleValue) throw new Error("no title provided");
if (!contentValue || contentValue === "<p><br></p>")
throw new Error("no content provided");
axiosLocalhost.post("/posts", {
title: titleValue,
content: contentValue,
@ -37,6 +42,8 @@ export default function SubmitPostButton(props: TSubmitPostButton) {
setIsDisabled(false);
},
onSuccess: () => {
setContentValue("");
setTitleValue("");
navigate("/");
},
});

View File

@ -0,0 +1,12 @@
export const pText = `The goal of typography is to relate font size, line
height, and line width in a proportional way that
maximizes beauty and makes reading easier and more
pleasant. The question is: What proportion(s) will give
us the best results? The golden ratio is often observed
in nature where beauty and utility intersect; perhaps we
can use this divine proportion to enhance these
attributes in our typography.`;
export const headerLong = `THUS SHU SHU HDFQIUWKHFQWHF KJQWHqwfiqfquwdhqwjdk`;
export const headerShort = `THUS SHU SHU HDFQIUWKHFQWHF`;

View File

@ -4,6 +4,7 @@ import {
Route,
useRouteError,
} from "react-router-dom";
import ArticleViewer from "../Components/ArticleViewer/ArticleViewer";
import AuthPageWrapper from "../Pages/AuthPageWrapper/AuthPageWrapper";
import LoginPage from "../Pages/LoginRegisterPage/LoginPage/LoginPage";
import RegisterPage from "../Pages/LoginRegisterPage/RegisterPage/RegisterPage";
@ -36,7 +37,9 @@ export const routes = createRoutesFromElements(
<PostCreatorPage />
</AuthPageWrapper>
}
></Route>
/>
<Route path="/posts/:postId" element={<ArticleViewer />} />
</Route>
<Route

View File

@ -0,0 +1,36 @@
package bookmarkspolicies
import (
bookmarksrules "enshi/ABAC/BookmarkPolicies/bookmarkRules"
"enshi/ABAC/rules"
"github.com/gin-gonic/gin"
)
const (
DELETE_BOOKMARK = "delete_bookmark"
CREATE_BOOKMARK = "create_bookmark"
READ_BOOKMARK = "read_bookmark"
)
func BlogPolicies(c *gin.Context) (bool, []error) {
target, exists := c.Get("target")
if !exists {
return false, nil
}
// Permit if one permit
switch target {
case DELETE_BOOKMARK:
return rules.CheckRule(c, bookmarksrules.BookmarkDeleteRule)
case CREATE_BOOKMARK:
return rules.CheckRule(c, bookmarksrules.BookmarkCreateRule)
case READ_BOOKMARK:
return rules.CheckRule(c, bookmarksrules.BookmarkReadRule)
}
return false, nil
}

View File

@ -0,0 +1,22 @@
package bookmarksrules
import (
globalrules "enshi/ABAC/GlobalRules"
"enshi/ABAC/rules"
"github.com/gin-gonic/gin"
)
func BookmarkCreateRule(c *gin.Context) (bool, []error) {
rulesToCheck := []rules.RuleFunction{
globalrules.AuthorizedRule,
}
isAllowed, errors := rules.CheckRules(
c,
rulesToCheck,
rules.ALL_RULES_MUST_BE_COMPLETED,
)
return isAllowed, errors
}

View File

@ -0,0 +1,22 @@
package bookmarksrules
import (
globalrules "enshi/ABAC/GlobalRules"
"enshi/ABAC/rules"
"github.com/gin-gonic/gin"
)
func BookmarkDeleteRule(c *gin.Context) (bool, []error) {
rulesToCheck := []rules.RuleFunction{
globalrules.AuthorizedRule,
}
isAllowed, errors := rules.CheckRules(
c,
rulesToCheck,
rules.ALL_RULES_MUST_BE_COMPLETED,
)
return isAllowed, errors
}

View File

@ -0,0 +1,22 @@
package bookmarksrules
import (
globalrules "enshi/ABAC/GlobalRules"
"enshi/ABAC/rules"
"github.com/gin-gonic/gin"
)
func BookmarkReadRule(c *gin.Context) (bool, []error) {
rulesToCheck := []rules.RuleFunction{
globalrules.AuthorizedRule,
}
isAllowed, errors := rules.CheckRules(
c,
rulesToCheck,
rules.ALL_RULES_MUST_BE_COMPLETED,
)
return isAllowed, errors
}

View File

@ -0,0 +1,33 @@
package middleware
import (
bookmarkspolicies "enshi/ABAC/BookmarkPolicies"
"enshi/ABAC/rules"
"github.com/gin-gonic/gin"
)
func BookmarksMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
switch c.Request.Method {
case "DELETE":
c.Set("target", bookmarkspolicies.DELETE_BOOKMARK)
case "POST":
c.Set("target", bookmarkspolicies.CREATE_BOOKMARK)
case "GET":
c.Set("target", bookmarkspolicies.READ_BOOKMARK)
}
isAllowed, errors := bookmarkspolicies.BlogPolicies(c)
if rules.ShouldAbortRequest(c, isAllowed, errors) {
c.Abort()
return
}
c.Next()
}
}

View File

@ -0,0 +1,35 @@
package bookmarksroutes
import (
"context"
rest_api_stuff "enshi/REST_API_stuff"
db_repo "enshi/db/go_queries"
"enshi/db_connection"
"enshi/middleware/getters"
"github.com/gin-gonic/gin"
)
func CreatePost(c *gin.Context) {
var bookmarkParams db_repo.CreateBookmarkParams
if err := c.BindJSON(&bookmarkParams); err != nil {
rest_api_stuff.BadRequestAnswer(c, err)
return
}
userId, err := getters.GetUserIdFromContext(c)
if err != nil {
rest_api_stuff.BadRequestAnswer(c, err)
return
}
bookmarkParams.UserID = userId
query := db_repo.New(db_connection.Dbx)
if _, err := query.CreateBookmark(context.Background(), bookmarkParams); err != nil {
rest_api_stuff.InternalErrorAnswer(c, err)
return
}
rest_api_stuff.OkAnswer(c, "Bookmark has been created!")
}

View File

@ -5,6 +5,7 @@ import (
"enshi/middleware/getters"
"enshi/routes/authRoutes"
"enshi/routes/blogRoutes"
bookmarksroutes "enshi/routes/bookmarksRoutes"
"enshi/routes/postsRoutes"
"enshi/routes/userProfileRoutes"
"net/http"
@ -114,6 +115,14 @@ func SetupRotes(g *gin.Engine) error {
userProfileRoutes.UpdateUserProfile,
)
bookmarksGroup := g.Group("/")
bookmarksGroup.Use(middleware.BookmarksMiddleware())
bookmarksGroup.POST(
"bookmarks/:post-id",
bookmarksroutes.CreateBookmark,
)
// Admin group routes
adminGroup := g.Group("/admin/")
adminGroup.Use(middleware.AdminMiddleware())