Skip to content

Commit

Permalink
Merge branch 'main' into fix/add-alt-tex-image
Browse files Browse the repository at this point in the history
  • Loading branch information
Sembauke authored Feb 13, 2024
2 parents 73bda18 + 511ccad commit 383e553
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 32 deletions.
3 changes: 2 additions & 1 deletion apps/backend/.lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"**/*.{js,jsx,ts,tsx}": "eslint"
"**/*.{js,jsx,ts,tsx}": "eslint",
"*": "prettier --ignore-unknown --write"
}
3 changes: 2 additions & 1 deletion apps/frontend/.lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"**/*.{js,jsx,ts,tsx}": "eslint"
"**/*.{js,jsx,ts,tsx}": "eslint",
"*": "prettier --ignore-unknown --write"
}
1 change: 1 addition & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"react-infinite-scroll-component": "^6.1.0",
"slugify": "^1.6.6",
"tiptap-markdown": "^0.8.2",
"use-debounce": "^10.0.0",
"uuid": "^9.0.1",
"yup": "^1.3.2"
},
Expand Down
97 changes: 85 additions & 12 deletions apps/frontend/src/components/post-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { useCallback, useEffect, useState } from "react";
import Tiptap from "@/components/tiptap";
import EditorDrawer from "@/components/editor-drawer";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronLeft, faEdit } from "@fortawesome/free-solid-svg-icons";
import {
faChevronLeft,
faCircleCheck,
faCircleXmark,
faEdit,
} from "@fortawesome/free-solid-svg-icons";
import { v4 as uuidv4 } from "uuid";
import slugify from "slugify";
import {
Expand All @@ -14,6 +19,9 @@ import {
Stack,
FormControl,
FormErrorMessage,
Divider,
chakra,
Spinner,
} from "@chakra-ui/react";
import { Field, Form, Formik } from "formik";
import { updatePost } from "@/lib/posts";
Expand All @@ -22,6 +30,9 @@ import NextLink from "next/link";
import { useRouter } from "next/router";
import { isEditor } from "@/lib/current-user";
import ScheduleMenu from "./schedule-menu";
import { useDebouncedCallback } from "use-debounce";

const Icon = chakra(FontAwesomeIcon);

const PostForm = ({ tags, user, authors, post }) => {
const toast = useToast();
Expand All @@ -43,6 +54,16 @@ const PostForm = ({ tags, user, authors, post }) => {

const [featureImage, setFeatureImageUrl] = useState();
const [featureImageId, setFeatureImageId] = useState();
const [saveStatus, setSaveStatus] = useState(null);
const [postStatus, setPostStatus] = useState(null);

const debouncedContentSave = useDebouncedCallback(
() => {
handleSubmit({ isAutoSave: true });
},
3000,
{ maxWait: 5000 },
);

useEffect(() => {
if (post) {
Expand All @@ -63,6 +84,14 @@ const PostForm = ({ tags, user, authors, post }) => {
);
setFeatureImageId(feature_image.data.id);
}

if (post.attributes.publishedAt != null) {
setPostStatus("published");
} else if (post.attributes.scheduled_at != null) {
setPostStatus("scheduled");
} else {
setPostStatus("draft");
}
}
}, [post]);

Expand Down Expand Up @@ -97,7 +126,13 @@ const PostForm = ({ tags, user, authors, post }) => {
};

const handleSubmit = useCallback(
async (shouldPublish = null, scheduledDate = "", scheduledTime = "") => {
async ({
shouldPublish = null,
scheduledDate = "",
scheduledTime = "",
isAutoSave = false,
} = {}) => {
setSaveStatus("saving");
const nonce = uuidv4();
const token = user.jwt;

Expand Down Expand Up @@ -146,27 +181,34 @@ const PostForm = ({ tags, user, authors, post }) => {
if (shouldPublish === "unpublished") {
data.data.publishedAt = null;
data.data.scheduled_at = null;
setPostStatus("draft");
}

if (shouldPublish === "later") {
data.data.scheduled_at = handleSchedule();
setPostStatus("scheduled");
}

if (shouldPublish == "now") {
data.data.publishedAt = new Date().toISOString();
data.data.scheduled_at = null;
setPostStatus("published");
}
try {
await updatePost(postId, data, token);
toast({
title: getTitle(),
description: `The post ${
shouldPublish != null ? "status" : ""
} has been updated.`,
status: "success",
duration: 5000,
isClosable: true,
});
if (!isAutoSave) {
toast({
title: getTitle(),
description: `The post ${
shouldPublish != null ? "status" : ""
} has been updated.`,
status: "success",
duration: 5000,
isClosable: true,
});
}
debouncedContentSave.cancel();
setSaveStatus("saved");
setUnsavedChanges(false);
} catch (error) {
toast({
Expand All @@ -176,6 +218,7 @@ const PostForm = ({ tags, user, authors, post }) => {
duration: 5000,
isClosable: true,
});
setSaveStatus("error");
}
},
[
Expand All @@ -188,6 +231,7 @@ const PostForm = ({ tags, user, authors, post }) => {
author,
postId,
user,
debouncedContentSave,
],
);

Expand Down Expand Up @@ -239,6 +283,10 @@ const PostForm = ({ tags, user, authors, post }) => {

function handleContentChange(content) {
setContent(content);
if (postStatus === "draft") {
// Enable auto save only for drafts
debouncedContentSave(content);
}

if (!unsavedChanges) {
setUnsavedChanges(true);
Expand All @@ -265,7 +313,32 @@ const PostForm = ({ tags, user, authors, post }) => {
<Text fontSize="2xl">Posts</Text>
</Button>
</Box>
<Box ml="auto" display={"flex"}>
<Box ml="auto" display="flex" alignItems="center">
{saveStatus !== null && (
<>
<Flex alignItems="center">
{saveStatus === "saved" && (
<>
<Icon mr={3} icon={faCircleCheck} />
<Text fontSize="lg">Saved</Text>
</>
)}
{saveStatus === "saving" && (
<>
<Spinner mr={3} size="sm" />
<Text fontSize="lg">Saving...</Text>
</>
)}
{saveStatus === "error" && (
<>
<Icon mr={3} icon={faCircleXmark} />
<Text fontSize="lg">Error</Text>
</>
)}
</Flex>
<Divider orientation="vertical" mx="1rem" />
</>
)}
{isEditor(user) && (
<ScheduleMenu handleSubmit={handleSubmit} post={post} />
)}
Expand Down
8 changes: 6 additions & 2 deletions apps/frontend/src/components/schedule-menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ const PublishedMenu = ({
setScheduleOption("now");
}

handleSubmit(scheduleOption);
handleSubmit({ shouldPublish: scheduleOption });
onClose();
}}
mr="1rem"
Expand Down Expand Up @@ -283,7 +283,11 @@ const NotPublishedMenu = ({
setScheduleOption("now");
}

handleSubmit(scheduleOption, scheduledDate, scheduledTime);
handleSubmit({
shouldPublish: scheduleOption,
scheduledDate,
scheduledTime,
});
onClose();
}}
isDisabled={
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import BaseHeading from "@tiptap/extension-heading";
import slugify from "slugify";

export const Heading = BaseHeading.configure({
levels: [1, 2, 3, 4, 5, 6],
}).extend({
addAttributes() {
return {
...this.parent?.(),
id: {
default: null,
parseHTML: (element) => ({
id: slugify(element.textContent, {
lower: true,
}),
}),
renderHTML: (attributes) => ({
id: attributes.id,
}),
},
};
},

onSelectionUpdate({ editor }) {
const { $from } = editor.state.selection;

// This line gets the node at the depth of
// the start of the selection. If the selection
// starts in a heading, this will be the heading node.

const node = $from.node($from.depth);

if (node.type.name === "heading") {
editor.commands.updateAttributes("heading", {
id: slugify(node.textContent, {
lower: true,
}),
});
}
},

renderHTML({ node }) {
return [
"h" + node.attrs.level,
{
id: slugify(node.textContent, {
lower: true,
}),
},
0,
];
},
});
9 changes: 7 additions & 2 deletions apps/frontend/src/components/tiptap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
MenuList,
Text,
useDisclosure,
useColorMode,
useColorModeValue
} from "@chakra-ui/react";
import {
autoUpdate,
Expand Down Expand Up @@ -244,6 +246,7 @@ function ToolBar({ editor, user }) {
}

function BubbleMenuBar({ editor, isLinkHover }) {
const menuBgColor = useColorModeValue("white", "gray.700");
const addLink = () => {
const url = window.prompt("URL");

Expand All @@ -263,7 +266,7 @@ function BubbleMenuBar({ editor, isLinkHover }) {
borderRadius="lg"
overflowX="auto"
id="bubble-menu"
background="white"
background={menuBgColor}
>
<Button
variant="ghost"
Expand Down Expand Up @@ -308,14 +311,16 @@ function HoverMenuBar({
linkEl,
setLinkEl,
}) {
const menuBgColor = useColorModeValue("white", "gray.700");

return (
<Box
p="0.2rem"
border="1px solid silver"
borderRadius="lg"
overflowX="auto"
id="bubble-menu"
background="white"
background={menuBgColor}
width="fit-content"
visibility={
floatingMiddleware.hide?.referenceHidden ? "hidden" : "visible"
Expand Down
3 changes: 3 additions & 0 deletions apps/frontend/src/lib/editor-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CharacterCount from "@tiptap/extension-character-count";
import Image from "../components/tip-tap-extensions/image-extension";
import Placeholder from "@tiptap/extension-placeholder";
import Link from "@tiptap/extension-link";
import { Heading } from "@/components/tip-tap-extensions/heading-extension";

export const extensions = [
StarterKit.configure({
Expand All @@ -18,6 +19,7 @@ export const extensions = [
keepMarks: true,
keepAttributes: false,
},
heading: false,
codeBlock: false,
}),
Placeholder.configure({
Expand Down Expand Up @@ -47,4 +49,5 @@ export const extensions = [
lowlight,
}),
CharacterCount.configure({}),
Heading,
];
Loading

0 comments on commit 383e553

Please sign in to comment.