Fix dependancy / config issues, implement new md only form

This commit is contained in:
austinkelsay 2024-07-21 14:12:19 -05:00
parent ff0a0facaf
commit 4ac66f234b
12 changed files with 4183 additions and 1739 deletions

3
.gitignore vendored
View File

@ -1,11 +1,12 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
.env
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
.env
# testing
/coverage

View File

@ -8,6 +8,6 @@ EXPOSE 3000
COPY . .
RUN npm install --legacy-peer-deps
RUN npm install
CMD ["npm", "run", "dev"]

View File

@ -1,9 +1,12 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: false,
// next.config.js
const removeImports = require("next-remove-imports")();
module.exports = removeImports({
reactStrictMode: true,
images: {
domains: ['localhost', 'secure.gravatar.com'],
},
}
module.exports = nextConfig;
webpack(config, options) {
return config;
},
});

5680
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{
"name": "plebdevs-new",
"name": "plebdevs",
"version": "0.1.0",
"private": true,
"scripts": {
@ -9,36 +9,27 @@
"lint": "next lint"
},
"dependencies": {
"@getalby/bitcoin-connect-react": "^3.2.2",
"@prisma/client": "^5.9.1",
"@reduxjs/toolkit": "^2.1.0",
"axios": "^1.6.7",
"@getalby/bitcoin-connect-react": "^3.5.3",
"@prisma/client": "^5.17.0",
"@uiw/react-md-editor": "^3.6.0",
"axios": "^1.7.2",
"bech32": "^2.0.0",
"classnames": "^2.5.1",
"light-bolt11-decoder": "^3.1.1",
"next": "14.0.4",
"next-auth": "^4.24.5",
"nostr-tools": "^2.1.5",
"primeicons": "^6.0.1",
"primereact": "^10.2.1",
"quill": "^1.3.7",
"next": "14.2.5",
"next-auth": "^4.24.7",
"next-remove-imports": "^1.0.12",
"nostr-tools": "^2.7.1",
"primeicons": "^7.0.0",
"primereact": "^10.7.0",
"react": "^18",
"react-dom": "^18",
"react-markdown": "^9.0.1",
"react-redux": "^9.1.0",
"react-typist": "^2.0.5",
"redux": "^5.0.1",
"rehype-raw": "^7.0.0",
"uuid": "^9.0.1",
"uuid": "^10.0.0",
"zapthreads": "^0.5.2"
},
"devDependencies": {
"@types/node": "20.11.21",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.0.4",
"eslint-config-next": "14.2.5",
"postcss": "^8",
"prisma": "^5.9.1",
"tailwindcss": "^3.3.0"
"tailwindcss": "^3.4.1"
}
}

View File

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

8
postcss.config.mjs Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View File

@ -1,15 +1,21 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useCallback } from "react";
import axios from "axios";
import { InputText } from "primereact/inputtext";
import { InputNumber } from "primereact/inputnumber";
import { InputSwitch } from "primereact/inputswitch";
import { Editor } from "primereact/editor";
import { Button } from "primereact/button";
import { useRouter } from "next/router";
import { useNostr } from "@/hooks/useNostr";
import { useLocalStorageWithEffect } from "@/hooks/useLocalStorage";
import EditorHeader from "./Editor/EditorHeader";
import { useToast } from "@/hooks/useToast";
import dynamic from 'next/dynamic';
const MDEditor = dynamic(
() => import("@uiw/react-md-editor"),
{
ssr: false,
}
);
import 'primeicons/primeicons.css';
const ResourceForm = ({ draft = null }) => {
@ -17,15 +23,19 @@ const ResourceForm = ({ draft = null }) => {
const [summary, setSummary] = useState(draft?.summary || '');
const [isPaidResource, setIsPaidResource] = useState(draft?.price ? true : false);
const [price, setPrice] = useState(draft?.price || 0);
const [text, setText] = useState(draft?.content || '');
const [coverImage, setCoverImage] = useState(draft?.image || '');
const [topics, setTopics] = useState(draft?.topics || ['']);
const [content, setContent] = useState(draft?.content || '');
const [user] = useLocalStorageWithEffect('user', {});
const { showToast } = useToast();
const { publishAll } = useNostr();
const router = useRouter();
const handleContentChange = useCallback((value) => {
setContent(value || '');
}, []);
useEffect(() => {
if (draft) {
setTitle(draft.title);
@ -53,7 +63,7 @@ const ResourceForm = ({ draft = null }) => {
summary,
type: 'resource',
price: isPaidResource ? price : null,
content: text,
content,
image: coverImage,
topics: [...topics.map(topic => topic.trim().toLowerCase()), 'plebdevs', 'resource']
};
@ -279,52 +289,50 @@ const ResourceForm = ({ draft = null }) => {
);
return (
<form onSubmit={handleSubmit}>
<div className="p-inputgroup flex-1">
<InputText value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Title" />
</div>
<div className="p-inputgroup flex-1 mt-8">
<InputText value={summary} onChange={(e) => setSummary(e.target.value)} placeholder="Summary" />
</div>
<div className="p-inputgroup flex-1 mt-8">
<InputText value={coverImage} onChange={(e) => setCoverImage(e.target.value)} placeholder="Cover Image URL" />
</div>
<div className="p-inputgroup flex-1 mt-8 flex-col">
<p className="py-2">Paid Resource</p>
<InputSwitch checked={isPaidResource} onChange={(e) => setIsPaidResource(e.value)} />
{isPaidResource && (
<div className="p-inputgroup flex-1 py-4">
<InputNumber value={price} onValueChange={(e) => setPrice(e.value)} placeholder="Price (sats)" />
</div>
)}
</div>
<div className="p-inputgroup flex-1 flex-col mt-8">
<span>Content</span>
<Editor
value={text}
onTextChange={(e) => setText(e.htmlValue)}
style={{ height: '320px' }}
headerTemplate={<EditorHeader quill={null} />}
/>
</div>
<div className="mt-8 flex-col w-full">
{topics.map((topic, index) => (
<div className="p-inputgroup flex-1" key={index}>
<InputText value={topic} onChange={(e) => handleTopicChange(index, e.target.value)} placeholder="Topic" className="w-full mt-2" />
{index > 0 && (
<Button icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeTopic(e, index)} />
)}
</div>
))}
<div className="w-full flex flex-row items-end justify-end py-2">
<Button icon="pi pi-plus" onClick={addTopic} />
<form onSubmit={handleSubmit}>
<div className="p-inputgroup flex-1">
<InputText value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Title" />
</div>
</div>
<div className="flex justify-center mt-8">
<Button type="submit" severity="success" outlined label={draft ? "Update" : "Submit"} />
</div>
</form>
<div className="p-inputgroup flex-1 mt-8">
<InputText value={summary} onChange={(e) => setSummary(e.target.value)} placeholder="Summary" />
</div>
<div className="p-inputgroup flex-1 mt-8">
<InputText value={coverImage} onChange={(e) => setCoverImage(e.target.value)} placeholder="Cover Image URL" />
</div>
<div className="p-inputgroup flex-1 mt-8 flex-col">
<p className="py-2">Paid Resource</p>
<InputSwitch checked={isPaidResource} onChange={(e) => setIsPaidResource(e.value)} />
{isPaidResource && (
<div className="p-inputgroup flex-1 py-4">
<InputNumber value={price} onValueChange={(e) => setPrice(e.value)} placeholder="Price (sats)" />
</div>
)}
</div>
<div className="p-inputgroup flex-1 flex-col mt-8">
<span>Content</span>
<MDEditor
value={content}
onChange={handleContentChange}
/>
</div>
<div className="mt-8 flex-col w-full">
{topics.map((topic, index) => (
<div className="p-inputgroup flex-1" key={index}>
<InputText value={topic} onChange={(e) => handleTopicChange(index, e.target.value)} placeholder="Topic" className="w-full mt-2" />
{index > 0 && (
<Button icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeTopic(e, index)} />
)}
</div>
))}
<div className="w-full flex flex-row items-end justify-end py-2">
<Button icon="pi pi-plus" onClick={addTopic} />
</div>
</div>
<div className="flex justify-center mt-8">
<Button type="submit" severity="success" outlined label={draft ? "Update" : "Submit"} />
</div>
</form>
);
}

View File

@ -353,7 +353,6 @@ export function useNostr() {
{
onevent: (event) => {
if (hasRequiredTags(event.tags)) {
console.log('event:', event);
resources.push(event);
}
},
@ -371,7 +370,6 @@ export function useNostr() {
// Set a timeout to resolve the promise after collecting events
setTimeout(() => {
subscription?.close();
console.log('Resolving with resources:', resources);
resolve(resources);
}, 2000); // Adjust the timeout value as needed
});
@ -379,7 +377,6 @@ export function useNostr() {
const fetchWorkshops = useCallback(async () => {
const filter = [{ kinds: [30023, 30402], authors: [AUTHOR_PUBKEY] }];
console.log('filter:', filter);
const hasRequiredTags = (tags) => {
const hasPlebDevs = tags.some(([tag, value]) => tag === "t" && value === "plebdevs");
const hasWorkshop = tags.some(([tag, value]) => tag === "t" && value === "workshop");
@ -392,12 +389,8 @@ export function useNostr() {
filter,
{
onevent: (event) => {
console.log('Received workshop event:', event);
if (hasRequiredTags(event.tags)) {
console.log('Workshop event passed tag check, adding to workshops');
workshops.push(event);
} else {
console.log('Workshop event did not pass tag check');
}
},
onerror: (error) => {
@ -413,7 +406,6 @@ export function useNostr() {
setTimeout(() => {
subscription?.close();
console.log('Resolving with workshops:', workshops);
resolve(workshops);
}, 2000); // Adjust the timeout value as needed
});
@ -433,12 +425,8 @@ export function useNostr() {
filter,
{
onevent: (event) => {
console.log('Received course event:', event);
if (hasRequiredTags(event.tags)) {
console.log('Course event passed tag check, adding to courses');
courses.push(event);
} else {
console.log('Course event did not pass tag check');
}
},
onerror: (error) => {
@ -454,7 +442,6 @@ export function useNostr() {
setTimeout(() => {
subscription?.close();
console.log('Resolving with courses:', courses);
resolve(courses);
}, 2000); // Adjust the timeout value as needed
});

View File

@ -1,15 +1,19 @@
import { PrimeReactProvider } from 'primereact/api';
import { useEffect } from 'react';
import Navbar from '@/components/navbar/Navbar';
import { ToastProvider } from '@/hooks/useToast';
import Layout from '@/components/Layout';
import '@/styles/globals.css'
import 'primereact/resources/themes/lara-dark-indigo/theme.css';
import "@uiw/react-md-editor/markdown-editor.css";
import "@uiw/react-markdown-preview/markdown.css";
import Sidebar from '@/components/sidebar/Sidebar';
import { NostrProvider } from '@/context/NostrContext';
export default function MyApp({
Component, pageProps: { ...pageProps }
}) {
return (
<PrimeReactProvider>
<NostrProvider>

View File

@ -18,20 +18,10 @@ import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
const MarkdownContent = ({ content }) => {
// Function to strip HTML tags
const stripHtml = (html) => {
let tmp = document.createElement("DIV");
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || "";
};
// Strip HTML tags from the content
const plainContent = stripHtml(content);
return (
<div>
<ReactMarkdown rehypePlugins={[rehypeRaw]} className='markdown-content'>
{plainContent}
{content}
</ReactMarkdown>
</div>
);

View File

@ -90,25 +90,3 @@ h3 {
.p-tabmenu .p-tabmenu-nav::-webkit-scrollbar {
display: none;
}
/* markdown content display styles */
.markdown-content {
font-family: Arial, sans-serif;
line-height: 1.6;
}
.markdown-content h1, .markdown-content h2 {
padding-bottom: 0.3em;
}
.markdown-content pre {
border-radius: 3px;
padding: 16px;
overflow: auto;
}
.markdown-content code {
border-radius: 3px;
font-size: 85%;
margin: 0;
}