Skip to content

Commit

Permalink
1.16.0
Browse files Browse the repository at this point in the history
  • Loading branch information
hodgef committed May 14, 2024
1 parent de97cf1 commit cca478b
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 78 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "prss",
"version": "1.15.0",
"version": "1.16.0",
"description": "Powerful Blogging",
"license": "GPL-3.0-or-later",
"main": "build/index.js",
Expand Down
14 changes: 13 additions & 1 deletion src/common/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,19 @@ export const setCache = (name, val) => {
cache[name] = val;
};

export const getCache = <T>(name) => cache[name] as T;
export const getCache = <T>(name) => {
const entry = cache[name];
if(Array.isArray(entry) && entry.length === 2 && Number.isInteger(entry[0])){
if(Date.now() - entry[0] < 300000/*5m*/){
return entry[1] as T;
} else {
return undefined as T;
}
} else {
return cache[name] as T;
}
};

export const deleteCache = (name) => delete cache[name];

export const initDb = async () => {
Expand Down
21 changes: 15 additions & 6 deletions src/renderer/components/CreatePost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,29 @@ import { toast } from "react-toastify";
import { isValidSlug, getRootPost } from "../services/hosting";
import { getSite, getItems, updateSite, createItems } from "../services/db";
import { modal } from "./Modal";
import { ISite } from "../../common/interfaces";
import { IPostItem, ISite } from "../../common/interfaces";
import { storeInt } from "../../common/bootstrap";
import { useQuery } from "./UseQuery";

interface IProps {
setHeaderLeftComponent: (comp?: ReactNode) => void;
}

const CreatePost: FunctionComponent<IProps> = ({ setHeaderLeftComponent }) => {
const { siteId } = useParams() as any;
const query = useQuery();
const postParentSlug = query.get("parent");

const [site, setSite] = useState<ISite>(null);
const [items, setItems] = useState(null);
const [items, setItems] = useState<IPostItem[]>(null);
const { title, structure } = site || {};

const [formattedStructure, setFormattedStructure] = useState(structure);
const [postTitle, setPostTitle] = useState("");
const [postSlug, setPostSlug] = useState("");
const [postParent, setPostParent] = useState("");

const postTitleRef = useRef<HTMLInputElement>(null);

const history = useHistory();

useEffect(() => {
Expand Down Expand Up @@ -69,13 +71,18 @@ const CreatePost: FunctionComponent<IProps> = ({ setHeaderLeftComponent }) => {
const siteRes = await getSite(siteId);
const itemsRes = await getItems(siteId);
setFormattedStructure(
await walkStructure(siteId, siteRes.structure, ({ title }) => ({
await walkStructure(siteId, siteRes.structure, ({ title, slug }) => ({
title,
slug
}))
);

setSite(siteRes);
setItems(itemsRes);

if(postParentSlug){
setPostParent(itemsRes?.find(({ slug }) => slug === postParentSlug)?.uuid);
}
};
getData();
}, []);
Expand Down Expand Up @@ -150,13 +157,15 @@ const CreatePost: FunctionComponent<IProps> = ({ setHeaderLeftComponent }) => {
normalizedSlug += `-${randomString}`;
}

const isBlogPost = postParent ? items.some(item => item.slug === "blog" && item.uuid === postParent) : false;

const newItem = {
uuid: postId,
title: postTitle.trim(),
slug: normalizedSlug,
siteId: siteId,
content: "",
template: "post",
template: isBlogPost ? "post" : "page",
updatedAt: null,
createdAt: Date.now(),
vars: {},
Expand Down
34 changes: 23 additions & 11 deletions src/renderer/components/ListPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@ const ListPosts: FunctionComponent<IProps> = ({ setHeaderLeftComponent }) => {
<span>Posts</span>
</div>
<div className="right-align">
{showPublishButton && hosting.name !== "none" && (
<button
type="button"
className="btn btn-outline-success"
onClick={() => publishSite()}
>
<i className="material-symbols-outlined">publish</i>
<span>Publish Changes</span>
</button>
)}

{!!items.length && (
<button
type="button"
Expand Down Expand Up @@ -247,18 +258,19 @@ const ListPosts: FunctionComponent<IProps> = ({ setHeaderLeftComponent }) => {
}}
>
<i className="material-symbols-outlined">add</i>
<span>Add New</span>
<span>New Page</span>
</button>
<button
type="button"
className="btn btn-primary"
onClick={() => history.push(`/sites/${siteId}/posts/create?parent=blog`)}
ref={r => {
showCoachmark(r, "intro-posts-addNew", "Create a new post", "coachmark-bottom");
}}
>
<i className="material-symbols-outlined">add</i>
<span>New Blog Post</span>
</button>
{showPublishButton && hosting.name !== "none" && (
<button
type="button"
className="btn btn-outline-success"
onClick={() => publishSite()}
>
<i className="material-symbols-outlined">publish</i>
<span>Publish Changes</span>
</button>
)}
</div>
</h1>
<div className="content">
Expand Down
79 changes: 68 additions & 11 deletions src/renderer/components/SlugEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { modal } from "./Modal";
import { IPostItem, ISite } from "../../common/interfaces";
import { isPreviewActive } from "../services/preview";
import { setHook } from "../../common/bootstrap";
import { toast } from "react-toastify";

interface IProps {
site: ISite;
Expand All @@ -25,6 +26,7 @@ const SlugEditor: FunctionComponent<IProps> = ({
items,
onSave,
}) => {
const [title, setTitle] = useState<string>(post.title);
const [isUneditable] = useState(post.slug.toLowerCase() === "home" || post.slug.toLowerCase() === "blog");
const [editing, setEditing] = useState(false);
const [value, setValue] = useState(post.slug);
Expand All @@ -35,6 +37,9 @@ const SlugEditor: FunctionComponent<IProps> = ({
setHook("SlugEditor_previewMode", (value: boolean) => {
setPreviewMode(value);
});
setHook("SlugEditor_setTitle", (value: string) => {
setTitle(value);
});
}, []);

useEffect(() => {
Expand All @@ -54,6 +59,45 @@ const SlugEditor: FunctionComponent<IProps> = ({
return null;
}

const refreshSlug = async () => {
if (!post) {
return;
}

if (isUneditable) {
modal.alert(["site_slug_protected", [post.slug]]);
return;
}

const normalizedSlug = normalize(title);

if (!(await isValidSlug(normalizedSlug, site.uuid, post.uuid))) {
modal.alert(["error_invalid_slug", []]);
return;
}

/**
* Ensure slug is unique
*/
const itemsWithSlug = items.filter(
item => item.slug === normalizedSlug
);

if (itemsWithSlug.length > 1) {
toast.error('You have items with the same slug');
return;
}

if (itemsWithSlug.length === 1 && itemsWithSlug[0].uuid !== post.uuid) {
toast.error('You have an item with the same slug');
return;
}

await onSave(normalizedSlug);
setValue(normalizedSlug);
setEditing(false);
};

const save = async () => {
if (!value.trim()) {
modal.alert(["site_slug_val", []]);
Expand All @@ -79,21 +123,22 @@ const SlugEditor: FunctionComponent<IProps> = ({
/**
* Ensure slug is unique
*/
// const itemsWithSlug = items.filter(
// item => item.slug === normalizedSlug
// );
const itemsWithSlug = items.filter(
item => item.slug === normalizedSlug
);

// if (itemsWithSlug.length > 1) {
// error('You have items with the same slug');
// return;
// }
if (itemsWithSlug.length > 1) {
toast.error('You have items with the same slug');
return;
}

// if (itemsWithSlug.length === 1 && itemsWithSlug[0].uuid !== post.uuid) {
// error('You have an item with the same slug');
// return;
// }
if (itemsWithSlug.length === 1 && itemsWithSlug[0].uuid !== post.uuid) {
toast.error('You have an item with the same slug');
return;
}

await onSave(normalizedSlug);
setValue(normalizedSlug);
setEditing(false);
};

Expand Down Expand Up @@ -158,6 +203,7 @@ const SlugEditor: FunctionComponent<IProps> = ({
{!isUneditable && (
<i
className="material-symbols-outlined clickable"
title="Edit Slug"
onClick={() => {
setValue(post.slug);
setEditing(true);
Expand All @@ -167,6 +213,17 @@ const SlugEditor: FunctionComponent<IProps> = ({
</i>
)}
</>
<>
{!isUneditable && (
<i
className="material-symbols-outlined clickable"
title="Refresh Slug to match title"
onClick={refreshSlug}
>
refresh
</i>
)}
</>
</Fragment>
) : (
<>
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/components/TitleEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cx from "classnames";
import { getItem } from "../services/db";
import { isValidSlug } from "../services/hosting";
import { modal } from "./Modal";
import { runHook } from "../../common/bootstrap";

interface IProps {
siteId: string;
Expand Down Expand Up @@ -63,6 +64,7 @@ const TitleEditor: FunctionComponent<IProps> = ({

await onSave(value, slugChangeAllowed ? newSlug : null);
setPost({ ...post, title: value });
runHook("SlugEditor_setTitle", value);
setEditing(false);
};

Expand Down
Loading

0 comments on commit cca478b

Please sign in to comment.