mirror of
https://github.com/aljazceru/landscape-template.git
synced 2025-12-18 06:44:22 +01:00
feat: pubkeys-to-users api, finished the addComment component functionality, show/hide reply
This commit is contained in:
62
api/functions/pubkeys-to-users/pubkeys-to-users.js
Normal file
62
api/functions/pubkeys-to-users/pubkeys-to-users.js
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
const serverless = require('serverless-http');
|
||||
const { createExpressApp } = require('../../modules');
|
||||
const express = require('express');
|
||||
const extractKeyFromCookie = require('../../utils/extractKeyFromCookie');
|
||||
const { getUserByPubKey } = require('../../auth/utils/helperFuncs');
|
||||
const { prisma } = require('../../prisma');
|
||||
|
||||
|
||||
const mapPubkeysToUsers = async (req, res) => {
|
||||
try {
|
||||
|
||||
const pubkeys = req.body.pubkeys ?? [];
|
||||
|
||||
const usersArr = await prisma.user.findMany({
|
||||
where: {
|
||||
nostr_pub_key: {
|
||||
in: pubkeys
|
||||
}
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
avatar: true,
|
||||
nostr_pub_key: true,
|
||||
lightning_address: true
|
||||
}
|
||||
})
|
||||
|
||||
let pubkeysToUsers = {}
|
||||
usersArr.forEach(user => {
|
||||
pubkeysToUsers[user.nostr_pub_key] = user;
|
||||
});
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.json({ pubkeysToUsers });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(500).send("Unexpected error happened, please try again")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let app;
|
||||
|
||||
if (process.env.LOCAL) {
|
||||
app = createExpressApp()
|
||||
app.post('/pubkeys-to-users', mapPubkeysToUsers);
|
||||
}
|
||||
else {
|
||||
const router = express.Router();
|
||||
router.post('/pubkeys-to-users', mapPubkeysToUsers)
|
||||
app = createExpressApp(router)
|
||||
}
|
||||
|
||||
|
||||
const handler = serverless(app);
|
||||
exports.handler = async (event, context) => {
|
||||
return await handler(event, context);
|
||||
};
|
||||
@@ -51,9 +51,12 @@ functions:
|
||||
sign-event:
|
||||
handler: api/functions/sign-event/sign-event.handler
|
||||
events:
|
||||
- http:
|
||||
path: sign-event
|
||||
method: get
|
||||
- http:
|
||||
path: sign-event
|
||||
method: post
|
||||
pubkeys-to-users:
|
||||
handler: api/functions/pubkeys-to-users/pubkeys-to-users.handler
|
||||
events:
|
||||
- http:
|
||||
path: pubkeys-to-users
|
||||
method: post
|
||||
|
||||
@@ -15,7 +15,8 @@ const Template: ComponentStory<typeof AddComment> = (args) => <div className="ma
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
placeholder: "Leave a comment..."
|
||||
placeholder: "Leave a comment...",
|
||||
avatar: "https://i.pravatar.cc/150?img=8"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ import { InvalidContentHandler } from 'remirror';
|
||||
interface Props {
|
||||
initialContent?: string;
|
||||
placeholder?: string;
|
||||
name?: string;
|
||||
avatar: string;
|
||||
autoFocus?: boolean
|
||||
onSubmit?: (comment: string) => void;
|
||||
}
|
||||
|
||||
|
||||
export default function AddComment({ initialContent, placeholder, name, autoFocus, onSubmit }: Props) {
|
||||
export default function AddComment({ initialContent, placeholder, avatar, autoFocus, onSubmit }: Props) {
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const linkExtension = useMemo(() => {
|
||||
@@ -104,7 +104,7 @@ export default function AddComment({ initialContent, placeholder, name, autoFocu
|
||||
autoFocus={autoFocus}
|
||||
>
|
||||
<div className="flex gap-16 items-start pb-24 border-b border-gray-200 focus-within:border-primary-500">
|
||||
<div className="hidden sm:block mt-16 shrink-0"><Avatar width={48} src='https://i.pravatar.cc/150?img=1' /></div>
|
||||
<div className="hidden sm:block mt-16 shrink-0"><Avatar width={48} src={avatar} /></div>
|
||||
<div className="flex-grow">
|
||||
<EditorComponent
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { useAppSelector } from "src/utils/hooks";
|
||||
import AddComment from "../AddComment/AddComment";
|
||||
import CommentCard from "../CommentCard/CommentCard";
|
||||
import { CommentWithReplies } from "../types";
|
||||
@@ -8,13 +9,14 @@ import { CommentWithReplies } from "../types";
|
||||
interface Props {
|
||||
comment: CommentWithReplies
|
||||
isRoot?: boolean;
|
||||
canReply: boolean;
|
||||
onClickedReply?: () => void
|
||||
}
|
||||
|
||||
export default function Comment({ comment, isRoot, onClickedReply }: Props) {
|
||||
export default function Comment({ comment, canReply, isRoot, onClickedReply }: Props) {
|
||||
|
||||
const [replyOpen, setReplyOpen] = useState(false)
|
||||
|
||||
const user = useAppSelector(s => s.user.me)
|
||||
|
||||
const clickReply = () => {
|
||||
if (isRoot)
|
||||
@@ -25,7 +27,7 @@ export default function Comment({ comment, isRoot, onClickedReply }: Props) {
|
||||
|
||||
return (
|
||||
<div >
|
||||
<CommentCard comment={comment} onReply={clickReply} />
|
||||
<CommentCard canReply={canReply} comment={comment} onReply={clickReply} />
|
||||
{(comment.replies.length > 0 || replyOpen) && <div className="flex mt-16 gap-8 md:gap-20 pl-8">
|
||||
<div className="border-l border-b border-gray-200 w-16 md:w-24 h-40 rounded-bl-8 flex-shrink-0"></div>
|
||||
<div className="flex flex-col w-full gap-16">
|
||||
@@ -33,8 +35,9 @@ export default function Comment({ comment, isRoot, onClickedReply }: Props) {
|
||||
key={reply.id}
|
||||
comment={reply}
|
||||
onClickedReply={clickReply}
|
||||
canReply={false}
|
||||
/>)}
|
||||
{replyOpen && <AddComment autoFocus placeholder="Leave a reply..." />}
|
||||
{replyOpen && <AddComment avatar={user?.avatar!} autoFocus placeholder="Leave a reply..." />}
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
@@ -7,10 +7,11 @@ import { Comment } from "../types";
|
||||
|
||||
interface Props {
|
||||
comment: Comment
|
||||
canReply?: boolean;
|
||||
onReply?: () => void
|
||||
}
|
||||
|
||||
export default function CommentCard({ comment, onReply }: Props) {
|
||||
export default function CommentCard({ comment, canReply, onReply }: Props) {
|
||||
return (
|
||||
<div className="border rounded-12 p-24">
|
||||
<Header author={comment.author} date={new Date(comment.created_at).toISOString()} />
|
||||
@@ -19,12 +20,12 @@ export default function CommentCard({ comment, onReply }: Props) {
|
||||
</p>
|
||||
<div className="flex gap-24 mt-16 items-center">
|
||||
<VotesCount count={0} />
|
||||
<button
|
||||
{canReply && <button
|
||||
className="text-gray-600 font-medium hover:bg-gray-100 py-8 px-12 rounded-8"
|
||||
onClick={onReply}
|
||||
>
|
||||
<BiComment /> <span className="align-middle text-body5">Reply</span>
|
||||
</button>
|
||||
</button>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import CommentRoot from '../Comment/Comment'
|
||||
import AddComment from '../AddComment/AddComment'
|
||||
import { } from '../helpers'
|
||||
import { Comment, } from '../types'
|
||||
import { createWorkerFactory, useWorker } from '@shopify/react-web-worker'
|
||||
import { useAppSelector } from "src/utils/hooks";
|
||||
|
||||
import * as CommentsWorker from './comments.worker'
|
||||
import { Post_Type } from 'src/graphql'
|
||||
@@ -22,7 +22,7 @@ export default function CommentsSection({ type, id }: Props) {
|
||||
// const commentsTree = useMemo(() => convertCommentsToTree(comments), [comments])
|
||||
|
||||
const [commentsTree, setCommentsTree] = useState<Comment[]>([])
|
||||
|
||||
const user = useAppSelector(state => state.user.me)
|
||||
const filter = useMemo(() => `boltfun ${type}_comment ${id}` + (process.env.NODE_ENV === 'development' ? 'dev' : ""), [id, type])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -44,11 +44,21 @@ export default function CommentsSection({ type, id }: Props) {
|
||||
return (
|
||||
<div className="border border-gray-200 rounded-10 p-32 bg-white">
|
||||
<h6 className="text-body2 font-bolder">Discussion</h6>
|
||||
<div className="mt-24">
|
||||
<AddComment placeholder='Leave a comment...' onSubmit={handleNewComment} />
|
||||
</div>
|
||||
{!!user && <div className="mt-24">
|
||||
<AddComment
|
||||
placeholder='Leave a comment...'
|
||||
onSubmit={handleNewComment}
|
||||
avatar={user.avatar}
|
||||
/>
|
||||
</div>}
|
||||
<div className='flex flex-col gap-16 mt-32'>
|
||||
{commentsTree.map(comment => <CommentRoot key={comment.id} comment={comment} />)}
|
||||
{commentsTree.map(comment =>
|
||||
<CommentRoot
|
||||
key={comment.id}
|
||||
comment={comment}
|
||||
isRoot
|
||||
canReply={!!user}
|
||||
/>)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,33 +1,20 @@
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { generatePrivateKey, getPublicKey, relayPool } from 'nostr-tools'
|
||||
import debounce from 'lodash.debounce';
|
||||
import { relayPool } from 'nostr-tools'
|
||||
import { Nullable } from 'remirror';
|
||||
import { CONSTS } from 'src/utils';
|
||||
import { Comment } from '../types';
|
||||
import { mapPubkeysToUsers, } from './comment.server';
|
||||
|
||||
|
||||
type Author = {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: string;
|
||||
lightning_address?: string
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const pool = relayPool();
|
||||
const globalKeys = {
|
||||
prvkey: '',
|
||||
pubkey: ''
|
||||
}
|
||||
|
||||
export function now(prefix: string) {
|
||||
const hell = window.localStorage.getItem('test');
|
||||
if (!hell) window.localStorage.setItem('test', 'test');
|
||||
return hell + prefix + dayjs()
|
||||
};
|
||||
|
||||
export function connect() {
|
||||
const RELAYS = [
|
||||
@@ -45,35 +32,37 @@ export function connect() {
|
||||
})
|
||||
};
|
||||
|
||||
const events: Record<string, Required<NostrEvent>> = {};
|
||||
let events: Record<string, Required<NostrEvent>> = {};
|
||||
|
||||
export function sub(filter: string, cb: (data: Comment[]) => void) {
|
||||
|
||||
const reconstructTree = debounce(async () => {
|
||||
const newComments = await constructTree();
|
||||
cb(newComments)
|
||||
}, 1000)
|
||||
|
||||
let sub = pool.sub({
|
||||
filter: {
|
||||
"#r": [filter]
|
||||
},
|
||||
cb: async (event: Required<NostrEvent>) => {
|
||||
//Got a new event
|
||||
console.log(event);
|
||||
|
||||
if (!event.id) return;
|
||||
|
||||
if (event.id in events) return
|
||||
events[event.id] = event
|
||||
const newComments = await constructTree();
|
||||
cb(newComments)
|
||||
|
||||
reconstructTree()
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
sub.unsub();
|
||||
|
||||
events = {};
|
||||
};
|
||||
}
|
||||
|
||||
const getSignedEvents = async (event: any) => {
|
||||
async function getSignedEvents(event: any) {
|
||||
const res = await fetch(CONSTS.apiEndpoint + '/sign-event', {
|
||||
method: "post",
|
||||
body: JSON.stringify({ event }),
|
||||
@@ -86,20 +75,19 @@ const getSignedEvents = async (event: any) => {
|
||||
return data.event;
|
||||
}
|
||||
|
||||
function setKeys() {
|
||||
if (globalKeys.prvkey) return;
|
||||
|
||||
let privateKey = localStorage.getItem('nostrkey')
|
||||
if (!privateKey) {
|
||||
privateKey = generatePrivateKey()
|
||||
localStorage.setItem('nostrkey', privateKey)
|
||||
async function mapPubkeysToUsers(pubkeys: string[]) {
|
||||
const res = await fetch(CONSTS.apiEndpoint + '/pubkeys-to-users', {
|
||||
method: "post",
|
||||
body: JSON.stringify({ pubkeys }),
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
});
|
||||
const data = await res.json()
|
||||
return data.pubkeysToUsers as Record<string, Author>;
|
||||
}
|
||||
pool.setPrivateKey(privateKey)
|
||||
const pubkey = getPublicKey(privateKey)
|
||||
globalKeys.prvkey = privateKey
|
||||
globalKeys.pubkey = pubkey;
|
||||
|
||||
}
|
||||
|
||||
export async function post(data: string, filter: string) {
|
||||
|
||||
@@ -162,7 +150,7 @@ export async function constructTree() {
|
||||
|
||||
|
||||
// Extract the pubkeys used
|
||||
const pubkeysSet = new Set();
|
||||
const pubkeysSet = new Set<string>();
|
||||
sortedEvenets.forEach(e => pubkeysSet.add(e.pubkey));
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user