Skip to content

Commit

Permalink
updated typr editor
Browse files Browse the repository at this point in the history
  • Loading branch information
GraemeFulton committed Aug 14, 2024
1 parent 3a951e3 commit 9a52f7a
Show file tree
Hide file tree
Showing 35 changed files with 18,765 additions and 22,958 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img style="width:250px" src="https://prototypr.io/static/images/prototypr_logo.svg"/>
</a>
<h3>Open-source Publishing Platform</h3>
<p>Built with <a href="https://github.com/Prototypr/prototypr-frontend/wiki/Building-the-Prototypr-Website">Next.js</a>, <a href="https://github.com/Prototypr/prototypr-frontend/wiki/Prototypr-Backend-CMS-(Strapi.io)">Strapi.io</a>, and <a href="https://tiptap.dev">TipTap/ProseMirror (editor)</a>, by <a href="https://github.com/GraemeFulton">graeme</a>
<p>Built with <a href="https://github.com/Prototypr/prototypr-frontend/wiki/Building-the-Prototypr-Website">Next.js</a>, <a href="https://github.com/Prototypr/prototypr-frontend/wiki/Prototypr-Backend-CMS-(Strapi.io)">Strapi.io</a>, and <ath href="https://tiptap.dev">TipTap/ProseMirror (editor)</a>, by <a href="https://github.com/GraemeFulton">graeme</a>
</p>
<p>
<a href="https://github.com/sponsors/prototypr"><img src="https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86"/>
Expand All @@ -19,19 +19,27 @@
_In the past, Prototypr has been backed by Adobe Fund, and [**Interledger Foundation**](https://interledger.org/) to build a [Web Monetized Publishing platform](https://open.prototypr.io/)._

**Important:**
* This repo is only the Next.js Frontend app, but the features below describe the whole project.
* The [backend app is built with Strapi, and the repo for that is over here](https://github.com/Prototypr/prototypr-backend).
**This repo is only the Next.js Frontend app, but the features below describe the whole project.**

---

## ⚠️ Use as reference only

At the moment, there is no succinct guide or docs to clone and set up the platform locally, but it is possible with some poking around.
Therefore, please only use this repo as a reference and for example code - there will be teething problems running it without data.
This repo is for reference only - there's currently no documentation or seed data that is required to set up the platform locally - so it's not advised to do it yet. You can install the [Prototypr Editor](https://github.com/Prototypr/typr) separately though (see below).

💓 If you really need this, consider [sponsoring me](https://github.com/sponsors/prototypr) so I can create documentation, seed data, and all things necessary to install and run the project seamlessly.
💓 If you want to set it up, consider [sponsoring me](https://github.com/sponsors/prototypr) so I can create documentation, seed data, and all things necessary to install and run the project.

[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/prototypr)


### Update: Prototypr Editor is here!
**[UPDATE: AUG 2024]** The [Prototypr Editor](https://github.com/Prototypr/typr) is now available as a standalone [NPM package](https://www.npmjs.com/package/tiptypr) that you can install separately (I still need to add a license, probably will put MIT).

<a href="https://github.com/Prototypr/typr"><img src="https://prototypr-media.sfo2.digitaloceanspaces.com/strapi/af968e21ccb808a0a57c4a7827a44237.png"/></a>


---

## Features Overview

Prototypr is an evolving community blogging ecosystem. Features have been added based on what has been needed for running the site, so ⚠️**it may be tricky to duplicate features for a new project**. But here is what has been built so far:
Expand Down
2 changes: 1 addition & 1 deletion components/BusinessNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { MenuIcon, XIcon } from "@heroicons/react/outline";
import useUser from "@/lib/iron-session/useUser";
import { useState, useEffect } from "react";
import jsCookie from "js-cookie";
import UndoRedoButtons from "tiptypr/dist/UndoRedoButtons";
import UndoRedoButtons from "tiptypr";
const NavigationMenuDemo = dynamic(() => import("./navbar-menu"), {
ssr: true,
});
Expand Down
13 changes: 5 additions & 8 deletions components/InterviewDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,14 @@ const InterviewDialog = ({

try {
const postInfo = await createPost({
user,
editor: initialEditor,
forReview: false,
relatedPost: relatedPostId,
relatedPostId: relatedPostId,
});
//set the new slug
localStorage.removeItem("wipInterview");
// localStorage.removeItem("wipInterview");

router.push(
`/toolbox/post/${relatedPostId}/interview/${postInfo?.id}`
);
// router.push(
// `/toolbox/post/${relatedPostId}/interview/${postInfo?.id}`
// );
} catch (e) {
setLoading(false);
alert("Error creating post. Please try again.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { formatDistanceToNow } from "date-fns";
import Link from "next/link";

const InterviewInviteBadge = ({ notification }) => {
if(!notification?.post){
return null;
}
return (
<div className="flex items-start justify-between">
<div className="flex gap-6">
Expand All @@ -20,10 +23,10 @@ const InterviewInviteBadge = ({ notification }) => {
<>
You're invited to answer an interview article about {" "}
<Link
href={{ pathname:`/${notification.post.type == "article" ? "post" : "toolbox"}/${notification?.post.slug}`, query: { clearNotification: notification.id } }}
href={{ pathname:`/${notification?.post?.type == "article" ? "post" : "toolbox"}/${notification?.post?.slug}`, query: { clearNotification: notification.id } }}
className="font-bold hover:underline"
>
{notification.post.title}
{notification?.post?.title}
</Link>
!
</>
Expand All @@ -34,18 +37,18 @@ const InterviewInviteBadge = ({ notification }) => {
<p className="text-sm text-gray-500 max-w-[42rem] line-clamp-2 mr-4">
<Link
className="hover:underline"
href={{ pathname:`/${notification.post.type == "article" ? "post" : "toolbox"}/${notification?.post.slug}`, query: { clearNotification: notification.id } }}
href={{ pathname:`/${notification?.post?.type == "article" ? "post" : "toolbox"}/${notification?.post?.slug}`, query: { clearNotification: notification.id } }}
// as={`/${notification.post.type == "article" ? "post" : "toolbox"}/${notification?.post.slug}`}
>
Get featured in the newsletter for telling your story about
<span className="font-medium text-gray-700">
{" "}
{notification.post.title}
{notification?.post?.title}
</span>
</Link>
</p>
<div className="text-blue-400 text-sm">
{formatDistanceToNow(new Date(notification.createdAt), {
{formatDistanceToNow(new Date(notification?.createdAt), {
addSuffix: true,
})}
</div>
Expand Down
4 changes: 3 additions & 1 deletion components/Notifications/NotificationsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ const NotificationItem = ({ notification }) => {

return (
<div className="bg-white rounded-lg shadow p-4">
{notification?.entity_type == "post" ? (
{
!notification?.post?'':
notification?.entity_type == "post" ? (
<PostNotification notification={notification} />
) : notification?.entity_type == "badge" ? (
<CreatorBadgeNotification notification={notification} />
Expand Down
5 changes: 3 additions & 2 deletions components/toolbox/forms/DescriptionExcerptForm.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useFormik } from "formik";
import toast from "react-hot-toast";
import { FormContainer } from "@/components/Jobs/FormStepper";
import { FormInput } from "@/components/Jobs/FormInput";
// import { FormInput } from "@/components/Jobs/FormInput";
import * as Yup from "yup";
import dynamic from "next/dynamic";

Expand All @@ -10,7 +10,8 @@ import Button from "@/components/Primitives/Button";
import MiniEditor from "@/components/MiniEditor/MiniEditor";
// import useLoad from "../hooks/useLoad";
import { useWizardContext } from "react-sweet-wizard";
import Editor from "tiptypr/dist/Editor";
// import Editor from "tiptypr/dist/Editor";
import {Editor} from 'tiptypr'
import { ToggleSwitch } from "@/components/atom/Switch/switch";
const Spinner = dynamic(() => import("@/components/atom/Spinner/Spinner"));
const axios = require("axios");
Expand Down
2 changes: 1 addition & 1 deletion components/toolbox/hooks/useLoad.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const useLoad = (user) => {
try {
const data = await getUserArticle(user, id);

const post = data.userPostId;
const post = data;

//only allow owner of post, and post type article
if(post?.owner==user?.id && post?.type==='tool'){
Expand Down
22 changes: 18 additions & 4 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import getUserRelatedPostsFromPostId from "./queries/getUserRelatedPostsFromPost
import likeCountQuery from "./queries/likeCountQuery";
import notificationCountQuery from "./queries/notificationCountQuery";
import singlePostByToolRelation from "./queries/singlePostByToolRelation";
import { permissionCheck } from "./editor/permissionCheck";
// import { groupNotifications } from "./notifications/groupNotifications";
/**
* main fetch
Expand Down Expand Up @@ -318,7 +319,7 @@ export async function getPostWithTool(slug, preview, type, toolId) {
...(preview ? "" : ""),
// slug: slug,
type: type ? type : "article",
toolId: toolId
toolId: toolId,
},
});

Expand Down Expand Up @@ -735,7 +736,7 @@ export async function getUserNotifications({ user, pageSize, offset }) {
pageSize,
offset,
});
if(data.userNotifications.notifications.length === 0) return data;
if (data.userNotifications.notifications.length === 0) return data;
//group the notifications
// data.userNotifications.notifications = groupNotifications(data.userNotifications?.notifications);
return data;
Expand Down Expand Up @@ -786,9 +787,22 @@ export async function getPartnerJobs({ user, pageSize, offset }) {
return data;
}
export async function getUserArticle(user, id) {
// console.log("user haha ->", user);

const data = await fetchAPIAuthenticated(user, userArticle, { id });
return data;

const hasPermission = permissionCheck(user, data?.userPostId);

if (hasPermission) {
if(data?.userPostId){
data.userPostId.versioned_content = data.userPostId.draft_content;
data.userPostId.versioned_title = data.userPostId.draft_title;
return data?.userPostId;
} else {
return false;
}
} else {
return false;
}
}

export async function getSlugFromArticleId(user, slug) {
Expand Down
67 changes: 67 additions & 0 deletions lib/editor/createPost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import axios from "axios";
import { checkSessionExpired } from "../account/checkSessionExpired";

export const createPost = async ({ entry, user }) => {

//check if jwt is expired
const sessionExpired = checkSessionExpired(user?.jwt);
if (sessionExpired) {
alert("Your sessions has expired. Please log in again.");
return false;
}

console.log('user',user)
let createData = false;

let newPostData = {
type: "article",
status: entry.status,
title: entry.title,
content: entry.content,
draft_title: entry.versioned_title,
draft_content: entry.versioned_content,
slug: entry.slug,
esES: false,
date: new Date(),
user: user?.id,
publishedAt: null,
tools: entry.relation,
};

console.log('newPostData',newPostData)
try {
createData = await axios({
method: "post",
url: `${process.env.NEXT_PUBLIC_API_URL}/api/posts`,
headers: {
Authorization: `Bearer ${user?.jwt}`,
},

data: {
data: {
...newPostData,
},
},
})
.then(async function (response) {
return response?.data?.data;
})
.catch(function (error) {
console.log(error);
return false;
});
if (createData?.id) {
let postObject = {id:createData.id, ...createData.attributes};

postObject.versioned_title = entry.draft_title;
postObject.versioned_content = entry.draft_content;

return postObject;
} else {
return false;
}
} catch (e) {
console.log(e);
return false;
}
};
55 changes: 55 additions & 0 deletions lib/editor/menus/loggedInMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { UserCircle, Article, CircleWavyCheck } from "@/components/icons"

export const loggedInMenu = {
show:true,
items: [
{
link: user =>
user?.profile?.approved
? `/people/${user?.profile?.slug}`
: `/account/profile`,
icon: <UserCircle size={26} className="opacity-80 mr-3" />,
label: "Profile",
},
{ separator: true },
{
link: "/dashboard/drafts",
icon: <Article size={26} className="opacity-80 mr-3" />,
label: "Posts",
},
{
condition: user => user?.profile?.onboardComplete !== true,
items: [
{ separator: true },
{
link: "/onboard?onboard=true",
icon: <CircleWavyCheck size={26} className="opacity-80 mr-3" />,
label: "Setup",
},
],
},
{ separator: true },
{
link: "/account",
label: "Edit profile",
},
],
adminMenu: [
{ separator: true },
{
link: "/admin/drafts",
label: "👩‍✈️ Admin",
},
{
link: "https://api.prototypr.io/admin/content-manager/collectionType/api::post.post?page=1&pageSize=10&sort=date:DESC&plugins[i18n][locale]=en",
label: "👾 Strapi",
},
],
businessMenu: [
{ separator: true },
{
link: "/dashboard/partner",
label: "Business hub",
},
],
}
Loading

0 comments on commit 9a52f7a

Please sign in to comment.