mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 19:01:19 +00:00
Added generic button
This commit is contained in:
parent
046a130efa
commit
7c8273d663
@ -6,6 +6,7 @@ import { initializeBitcoinConnect } from './BitcoinConnect';
|
||||
import { LightningAddress } from '@getalby/lightning-tools';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import axios from 'axios'; // Import axios for API calls
|
||||
|
||||
const Payment = dynamic(
|
||||
@ -75,7 +76,7 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId }
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
<GenericButton
|
||||
label={`${amount} sats`}
|
||||
onClick={() => setDialogVisible(true)}
|
||||
disabled={!invoice}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button } from 'primereact/button';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { initializeBitcoinConnect } from './BitcoinConnect';
|
||||
import { LightningAddress } from '@getalby/lightning-tools';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import axios from 'axios';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
|
||||
const Payment = dynamic(
|
||||
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.Payment),
|
||||
@ -73,7 +73,7 @@ const ResourcePaymentButton = ({ lnAddress, amount, onSuccess, onError, resource
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
<GenericButton
|
||||
label={`${amount} sats`}
|
||||
icon="pi pi-wallet"
|
||||
onClick={() => setDialogVisible(true)}
|
||||
|
@ -10,6 +10,7 @@ import { useRouter } from 'next/router';
|
||||
import { Divider } from 'primereact/divider';
|
||||
import dynamic from 'next/dynamic';
|
||||
import AlbyButton from '@/components/buttons/AlbyButton';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import axios from 'axios';
|
||||
import Image from 'next/image';
|
||||
|
||||
@ -208,7 +209,7 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptio
|
||||
{!invoice && (
|
||||
<div className="w-full flex flex-row justify-between">
|
||||
{(oneTime || (!oneTime && !recurring)) && (
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Pay as you go"
|
||||
icon="pi pi-bolt"
|
||||
onClick={async () => {
|
||||
@ -220,7 +221,7 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptio
|
||||
/>
|
||||
)}
|
||||
{(recurring || (!oneTime && !recurring)) && (
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Setup Recurring Subscription"
|
||||
icon={
|
||||
<Image
|
||||
@ -253,7 +254,7 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptio
|
||||
placeholder="Enter NWC URL"
|
||||
className="w-full p-2 mb-4 border rounded"
|
||||
/>
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Submit"
|
||||
onClick={handleManualNwcSubmit}
|
||||
className="mt-4 w-fit text-[#f8f8ff]"
|
||||
|
11
src/components/buttons/GenericButton.js
Normal file
11
src/components/buttons/GenericButton.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { Button } from 'primereact/button';
|
||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||
|
||||
const GenericButton = ({ label, icon, onClick, severity, size, className, outlined = false, rounded = false, disabled = false, tooltip = null, tooltipOptions = null }) => {
|
||||
const windowWidth = useWindowWidth();
|
||||
return (
|
||||
<Button label={label} icon={icon} onClick={onClick} severity={severity} size={size || (windowWidth < 768 ? 'small' : 'normal')} className={className} outlined={outlined} rounded={rounded} disabled={disabled} tooltip={tooltip} tooltipOptions={tooltipOptions} />
|
||||
);
|
||||
}
|
||||
|
||||
export default GenericButton;
|
@ -2,21 +2,19 @@ import React from "react";
|
||||
import Image from "next/image";
|
||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||
import { formatUnixTimestamp } from "@/utils/time";
|
||||
import { Button } from 'primereact/button';
|
||||
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
const SelectedContentItem = ({ content, onRemove }) => {
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
|
||||
return (
|
||||
<div className="w-full border-2 rounded-lg border-gray-700 p-2 rounded-tr-none rounded-br-none relative">
|
||||
<Button
|
||||
<GenericButton
|
||||
icon="pi pi-times"
|
||||
className="absolute top-2 right-2 py-1 px-2 w-auto h-auto"
|
||||
severity="danger"
|
||||
size="small"
|
||||
rounded
|
||||
onClick={onRemove}
|
||||
aria-label="Remove"
|
||||
/>
|
||||
<div className="flex flex-row gap-4">
|
||||
<Image
|
||||
|
@ -25,7 +25,7 @@ const CourseTemplate = ({ course }) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md"
|
||||
className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md max-tab:px-0"
|
||||
>
|
||||
{/* Wrap the image in a div with a relative class with a padding-bottom of 56.25% representing the aspect ratio of 16:9 */}
|
||||
<div
|
||||
|
@ -26,7 +26,7 @@ const ResourceTemplate = ({ resource }) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md"
|
||||
className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md max-tab:px-0"
|
||||
>
|
||||
{/* Wrap the image in a div with a relative class with a padding-bottom of 56.25% representing the aspect ratio of 16:9 */}
|
||||
<div
|
||||
|
@ -24,7 +24,7 @@ const WorkshopTemplate = ({ workshop }) => {
|
||||
if (zapsError) return <div>Error: {zapsError}</div>;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md">
|
||||
<div className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md max-tab:px-0">
|
||||
<div
|
||||
onClick={() => router.replace(`/details/${workshop.id}`)}
|
||||
className="relative w-full h-0 hover:opacity-80 transition-opacity duration-300 cursor-pointer"
|
||||
|
@ -2,7 +2,7 @@ import React, { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import Image from 'next/image';
|
||||
import dynamic from 'next/dynamic';
|
||||
import axios from 'axios';
|
||||
@ -407,9 +407,9 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
|
||||
</div>
|
||||
<div className='w-[75vw] mx-auto flex flex-row justify-end mt-12'>
|
||||
<div className='w-fit flex flex-row justify-between'>
|
||||
<Button onClick={handleSubmit} label="Publish" severity='success' outlined className="w-auto m-2" />
|
||||
<Button onClick={() => router.push(`/course/${draftId}/draft/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<Button onClick={handleDelete} label="Delete" severity='danger' outlined className="w-auto m-2 mr-0" />
|
||||
<GenericButton onClick={handleSubmit} label="Publish" severity='success' outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={() => router.push(`/course/${draftId}/draft/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={handleDelete} label="Delete" severity='danger' outlined className="w-auto m-2 mr-0" />
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-[75vw] mx-auto mt-12 p-12 border-t-2 border-gray-300 max-tab:p-0 max-mob:p-0 max-tab:max-w-[100vw] max-mob:max-w-[100vw]'>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Tag } from "primereact/tag";
|
||||
import { Message } from "primereact/message";
|
||||
import { Button } from "primereact/button";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import Image from "next/image";
|
||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||
import { formatDateTime, formatUnixTimestamp } from "@/utils/time";
|
||||
@ -87,14 +87,14 @@ const DraftCourseLesson = ({ lesson, course }) => {
|
||||
{isPublished ? (
|
||||
<>
|
||||
<Message severity="success" text="published" className="w-auto m-2" />
|
||||
<Button onClick={() => router.push(`/details/${lesson.id}`)} label="View" outlined className="w-auto m-2" />
|
||||
<Button onClick={() => router.push(`/details/${lesson.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={() => router.push(`/details/${lesson.id}`)} label="View" outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={() => router.push(`/details/${lesson.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Message severity="info" text="draft (unpublished)" />
|
||||
<Button onClick={() => router.push(`/draft/${lesson.id}`)} label="View" outlined className="w-auto m-2" />
|
||||
<Button onClick={() => router.push(`/draft/${lesson.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={() => router.push(`/draft/${lesson.id}`)} label="View" outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={() => router.push(`/draft/${lesson.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@ import React, {useEffect} from "react";
|
||||
import Image from "next/image";
|
||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||
import { formatUnixTimestamp } from "@/utils/time";
|
||||
import { Button } from "primereact/button";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
|
||||
const ContentDropdownItem = ({ content, onSelect }) => {
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
@ -25,7 +25,7 @@ const ContentDropdownItem = ({ content, onSelect }) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-end">
|
||||
<Button label="Select" onClick={() => onSelect(content)} />
|
||||
<GenericButton label="Select" onClick={() => onSelect(content)} className="text-[#f8f8ff]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, {useEffect} from "react";
|
||||
import Image from "next/image";
|
||||
import { Button } from "primereact/button";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||
import { useRouter } from "next/router";
|
||||
import { Divider } from 'primereact/divider';
|
||||
@ -44,7 +44,7 @@ const ContentListItem = (content) => {
|
||||
<span>{content.summary}</span>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<Button
|
||||
<GenericButton
|
||||
onClick={handleClick}
|
||||
label="Open"
|
||||
outlined
|
||||
|
@ -1,16 +1,15 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Avatar } from 'primereact/avatar';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { Button } from 'primereact/button';
|
||||
import React from 'react';
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
import { useDiscordQuery } from '@/hooks/communityQueries/useDiscordQuery';
|
||||
import { useRouter } from 'next/router';
|
||||
import { highlightText } from '@/utils/text';
|
||||
import CommunityMessage from '@/components/feeds/messages/CommunityMessage';
|
||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||
|
||||
const DiscordFeed = ({ searchQuery }) => {
|
||||
const router = useRouter();
|
||||
const { data, error, isLoading } = useDiscordQuery({page: router.query.page});
|
||||
const windowWidth = useWindowWidth();
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@ -24,54 +23,22 @@ const DiscordFeed = ({ searchQuery }) => {
|
||||
return <div className="text-red-500 text-center p-4">Failed to load messages. Please try again later.</div>;
|
||||
}
|
||||
|
||||
const header = (message) => (
|
||||
<div className="flex flex-row w-full items-center justify-between p-4 bg-gray-800 rounded-t-lg">
|
||||
<div className="flex flex-row items-center">
|
||||
<Avatar image={message.avatar} shape="circle" size="large" className="border-2 border-blue-400" />
|
||||
<p className="pl-4 font-bold text-xl text-white">{message.author}</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start justify-between">
|
||||
<div className="flex flex-row w-full justify-between items-center my-1 max-sidebar:flex-col max-sidebar:items-start">
|
||||
<Tag value={message.channel} severity="primary" className="w-fit text-[#f8f8ff] bg-gray-600 mr-2 max-sidebar:mr-0" />
|
||||
<Tag icon="pi pi-discord" value="discord" className="w-fit text-[#f8f8ff] bg-blue-400 max-sidebar:mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const footer = (message) => (
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<span className="bg-gray-800 rounded-lg p-2 text-sm text-gray-300">
|
||||
{new Date(message.timestamp).toLocaleString()}
|
||||
</span>
|
||||
<Button
|
||||
label="View in Discord"
|
||||
icon="pi pi-external-link"
|
||||
outlined
|
||||
size="small"
|
||||
className='my-2'
|
||||
onClick={() => window.open(`https://discord.com/channels/${message.channelId}/${message.id}`, '_blank')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const filteredData = data.filter(message =>
|
||||
message.content.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
||||
<div className="mx-4 mt-4">
|
||||
<div className="mx-4">
|
||||
{filteredData && filteredData.length > 0 ? (
|
||||
filteredData.map(message => (
|
||||
<Card
|
||||
key={message.id}
|
||||
header={() => header(message)}
|
||||
footer={() => footer(message)}
|
||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||
>
|
||||
<p className="m-0 text-lg text-gray-200">{highlightText(message.content, searchQuery)}</p>
|
||||
</Card>
|
||||
<CommunityMessage
|
||||
key={message.id}
|
||||
message={message}
|
||||
searchQuery={searchQuery}
|
||||
windowWidth={windowWidth}
|
||||
platform="discord"
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="text-gray-400 text-center p-4">No messages available.</div>
|
||||
|
@ -1,8 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Avatar } from 'primereact/avatar';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { Button } from 'primereact/button';
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
import { useDiscordQuery } from '@/hooks/communityQueries/useDiscordQuery';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
@ -14,8 +10,9 @@ import { findKind0Fields } from '@/utils/nostr';
|
||||
import NostrIcon from '../../../public/images/nostr.png';
|
||||
import Image from 'next/image';
|
||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||
import { highlightText } from '@/utils/text';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import CommunityMessage from '@/components/feeds/messages/CommunityMessage';
|
||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||
|
||||
const StackerNewsIconComponent = () => (
|
||||
<svg width="16" height="16" className='mr-2' viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
@ -36,6 +33,7 @@ const GlobalFeed = ({searchQuery}) => {
|
||||
const { communityNotes: nostrData, error: nostrError, isLoading: nostrLoading } = useCommunityNotes();
|
||||
const { ndk } = useNDKContext();
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
const windowWidth = useWindowWidth();
|
||||
|
||||
const [authorData, setAuthorData] = useState({});
|
||||
|
||||
@ -91,17 +89,6 @@ const GlobalFeed = ({searchQuery}) => {
|
||||
return <div className="text-red-500 text-center p-4">Failed to load feed. Please try again later.</div>;
|
||||
}
|
||||
|
||||
const getAvatarImage = (item) => {
|
||||
if (item.type === 'discord') {
|
||||
return item.avatar ? returnImageProxy(item.avatar) : null;
|
||||
} else if (item.type === 'nostr') {
|
||||
return authorData[item.pubkey]?.avatar ? returnImageProxy(authorData[item.pubkey]?.avatar) : null;
|
||||
} else if (item.type === 'stackernews') {
|
||||
return item.user.image ? returnImageProxy(item.user.image) : null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const combinedFeed = [
|
||||
...(discordData || []).map(item => ({ ...item, type: 'discord' })),
|
||||
...(stackerNewsData || []).map(item => ({ ...item, type: 'stackernews' })),
|
||||
@ -120,101 +107,38 @@ const GlobalFeed = ({searchQuery}) => {
|
||||
return false;
|
||||
});
|
||||
|
||||
const header = (item) => (
|
||||
<div className="flex flex-row w-full items-center justify-between p-4 bg-gray-800 rounded-t-lg">
|
||||
<div className="flex flex-row items-center">
|
||||
<Avatar
|
||||
image={getAvatarImage(item)}
|
||||
icon={getAvatarImage(item) ? null : 'pi pi-user'}
|
||||
shape="circle"
|
||||
size="large"
|
||||
className="border-2 border-blue-400"
|
||||
/>
|
||||
<p className="pl-4 font-bold text-xl text-white">
|
||||
{item.type === 'discord' ? item.author :
|
||||
item.type === 'stackernews' ? item.user.name :
|
||||
authorData[item.pubkey]?.username || item.pubkey.substring(0, 12) + '...'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start justify-between">
|
||||
<div className="flex flex-row w-full justify-between items-center my-1 max-sidebar:flex-col max-sidebar:items-start">
|
||||
{item.type === 'discord' && (
|
||||
<>
|
||||
<Tag value={item.channel} severity="primary" className="w-fit text-[#f8f8ff] bg-gray-600 mr-2 max-sidebar:mr-0" />
|
||||
<Tag icon="pi pi-discord" value="discord" className="w-fit text-[#f8f8ff] bg-blue-400 max-sidebar:mt-1" />
|
||||
</>
|
||||
)}
|
||||
{item.type === 'stackernews' && (
|
||||
<>
|
||||
<Tag value="~devs" severity="contrast" className="w-fit text-[#f8f8ff] mr-2 max-sidebar:mr-0" />
|
||||
<Tag icon={<StackerNewsIconComponent />} value="stackernews" className="w-fit bg-gray-600 text-[#f8f8ff] max-sidebar:mt-1" />
|
||||
</>
|
||||
)}
|
||||
{item.type === 'nostr' && (
|
||||
<>
|
||||
<Tag icon="pi pi-hashtag" value="plebdevs" severity="primary" className="w-fit text-[#f8f8ff] bg-gray-600 mr-2 max-sidebar:mr-0" />
|
||||
<Tag icon={<Image src={NostrIcon} alt="Nostr" width={14} height={14} className='mr-[1px]' />} value="nostr" className="w-fit text-[#f8f8ff] bg-blue-400 max-sidebar:mt-1" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const footer = (item) => (
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<span className="bg-gray-800 rounded-lg p-2 text-sm text-gray-300">
|
||||
{item.type === 'nostr'
|
||||
? new Date(item.created_at * 1000).toLocaleString()
|
||||
: new Date(item.timestamp || item.createdAt).toLocaleString()}
|
||||
</span>
|
||||
<Button
|
||||
label={item.type === 'discord' ? "View in Discord" :
|
||||
item.type === 'stackernews' ? "View on StackerNews" :
|
||||
"View on Nostr"}
|
||||
icon="pi pi-external-link"
|
||||
outlined
|
||||
severity={item.type === 'discord' ? "info" :
|
||||
item.type === 'stackernews' ? "warning" :
|
||||
"success"}
|
||||
size="small"
|
||||
className='my-2'
|
||||
onClick={() => window.open(
|
||||
item.type === 'discord' ? `https://discord.com/channels/${item.channelId}/${item.id}` :
|
||||
item.type === 'stackernews' ? `https://stacker.news/items/${item.id}` :
|
||||
`https://nostr.band/${nip19.noteEncode(item.id)}`,
|
||||
'_blank'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
||||
<div className="mx-4 mt-4">
|
||||
{combinedFeed.length > 0 ? (
|
||||
combinedFeed.map(item => (
|
||||
<Card
|
||||
key={item.id}
|
||||
header={() => header(item)}
|
||||
footer={() => footer(item)}
|
||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||
>
|
||||
{item.type === 'discord' || item.type === 'nostr' ? (
|
||||
<p className="m-0 text-lg text-gray-200 overflow-hidden break-words">
|
||||
{highlightText(item.content, searchQuery)}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<h3 className="m-0 text-lg text-gray-200">
|
||||
{highlightText(item.title, searchQuery)}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-400">
|
||||
Comments: {item.comments.length} | Sats: {item.sats}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
<CommunityMessage
|
||||
key={item.id}
|
||||
message={{
|
||||
id: item.id,
|
||||
author: item.type === 'discord' ? item.author :
|
||||
item.type === 'stackernews' ? item.user.name :
|
||||
authorData[item.pubkey]?.username || item.pubkey.substring(0, 12) + '...',
|
||||
avatar: item.type === 'discord' ? item.avatar :
|
||||
item.type === 'stackernews' ? item.user.image :
|
||||
authorData[item.pubkey]?.avatar,
|
||||
content: item.type === 'stackernews' ? item.title : item.content,
|
||||
timestamp: item.type === 'nostr' ? item.created_at * 1000 : new Date(item.timestamp || item.createdAt),
|
||||
channel: item.type === 'discord' ? item.channel :
|
||||
item.type === 'stackernews' ? "~devs" :
|
||||
"plebdevs"
|
||||
}}
|
||||
searchQuery={searchQuery}
|
||||
windowWidth={windowWidth}
|
||||
platform={item.type}
|
||||
platformIcon={item.type === 'stackernews' ? <StackerNewsIconComponent /> :
|
||||
item.type === 'nostr' ? <Image src={NostrIcon} alt="Nostr" width={14} height={14} className='mr-[1px]' /> :
|
||||
null}
|
||||
platformLink={item.type === 'discord' ? `https://discord.com/channels/${item.channelId}/${item.id}` :
|
||||
item.type === 'stackernews' ? `https://stacker.news/items/${item.id}` :
|
||||
`https://nostr.band/${nip19.noteEncode(item.id)}`}
|
||||
additionalContent={item.type === 'stackernews' ? `Comments: ${item.comments.length} | Sats: ${item.sats}` : null}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="text-gray-400 text-center p-4">
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { Panel } from 'primereact/panel';
|
||||
import { useNDKContext } from "@/context/NDKContext";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
@ -8,8 +8,10 @@ import { useToast } from '@/hooks/useToast';
|
||||
|
||||
const MessageInput = ({ onMessageSent }) => {
|
||||
const [message, setMessage] = useState('');
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const { ndk, addSigner } = useNDKContext();
|
||||
const { showToast } = useToast();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!message.trim() || !ndk) return;
|
||||
|
||||
@ -33,20 +35,23 @@ const MessageInput = ({ onMessageSent }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const headerTemplate = (options) => {
|
||||
return (
|
||||
<div className="flex align-items-center justify-content-between my-1 py-2">
|
||||
<GenericButton outlined severity="primary" size="small" className="py-1" onClick={options.onTogglerClick} icon={options.collapsed ? 'pi pi-chevron-down' : 'pi pi-chevron-up'} />
|
||||
<h3 className="m-0 ml-2">New Message</h3>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Panel header={null} toggleable collapsed={false} className="w-full" pt={{
|
||||
header: {
|
||||
className: 'bg-transparent',
|
||||
border: 'none',
|
||||
},
|
||||
toggler: {
|
||||
className: 'bg-transparent',
|
||||
border: 'none',
|
||||
},
|
||||
togglerIcon: {
|
||||
display: 'none',
|
||||
},
|
||||
}}>
|
||||
<Panel
|
||||
headerTemplate={headerTemplate}
|
||||
toggleable
|
||||
collapsed={collapsed}
|
||||
onToggle={(e) => setCollapsed(e.value)}
|
||||
className="w-full"
|
||||
>
|
||||
<div className="w-full flex flex-col">
|
||||
<InputTextarea
|
||||
value={message}
|
||||
@ -59,11 +64,12 @@ const MessageInput = ({ onMessageSent }) => {
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full flex flex-row justify-end mt-4">
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Send"
|
||||
icon="pi pi-send"
|
||||
outlined
|
||||
onClick={handleSubmit}
|
||||
className="w-fit py-2"
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
|
@ -1,8 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Avatar } from 'primereact/avatar';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { Button } from 'primereact/button';
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import { useSession } from 'next-auth/react';
|
||||
@ -10,9 +6,10 @@ import { findKind0Fields } from '@/utils/nostr';
|
||||
import NostrIcon from '../../../public/images/nostr.png';
|
||||
import Image from 'next/image';
|
||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { useCommunityNotes } from '@/hooks/nostr/useCommunityNotes';
|
||||
import { highlightText } from '@/utils/text';
|
||||
import CommunityMessage from '@/components/feeds/messages/CommunityMessage';
|
||||
import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
|
||||
|
||||
const NostrFeed = ({ searchQuery }) => {
|
||||
@ -23,6 +20,8 @@ const NostrFeed = ({ searchQuery }) => {
|
||||
const [authorData, setAuthorData] = useState({});
|
||||
const [npub, setNpub] = useState(null);
|
||||
|
||||
const windowWidth = useWindowWidth();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAuthors = async () => {
|
||||
const authorDataMap = {};
|
||||
@ -68,61 +67,6 @@ const NostrFeed = ({ searchQuery }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getAvatarImage = (message) => {
|
||||
return authorData[message.pubkey]?.avatar ? returnImageProxy(authorData[message.pubkey]?.avatar) : null;
|
||||
};
|
||||
|
||||
const renderHeader = (message) => (
|
||||
<div className="flex flex-row w-full items-center justify-between p-4 bg-gray-800 rounded-t-lg">
|
||||
<div className="flex flex-row items-center">
|
||||
<Avatar
|
||||
image={getAvatarImage(message)}
|
||||
icon={getAvatarImage(message) ? null : 'pi pi-user'}
|
||||
shape="circle"
|
||||
size="large"
|
||||
className="border-2 border-blue-400"
|
||||
/>
|
||||
<p className="pl-4 font-bold text-xl text-white">
|
||||
{authorData[message.pubkey]?.username || message.pubkey.substring(0, 12) + '...'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start justify-between">
|
||||
<div className="flex flex-row w-full justify-between items-center my-1 max-sidebar:flex-col max-sidebar:items-start">
|
||||
<Tag icon="pi pi-hashtag" value="plebdevs" severity="primary" className="w-fit text-[#f8f8ff] bg-gray-600 mr-2 max-sidebar:mr-0" />
|
||||
<Tag icon={<Image src={NostrIcon} alt="Nostr" width={14} height={14} className='mr-[1px]' />} value="nostr" className="w-fit text-[#f8f8ff] bg-blue-400 max-sidebar:mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const footer = (message) => (
|
||||
<>
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<span className="bg-gray-800 rounded-lg p-2 text-sm text-gray-300">
|
||||
{new Date(message.created_at * 1000).toLocaleString()}
|
||||
</span>
|
||||
<Button
|
||||
label="View on Nostr"
|
||||
icon="pi pi-external-link"
|
||||
outlined
|
||||
size="small"
|
||||
className='my-2'
|
||||
onClick={() => window.open(`https://nostr.band/${nip19.noteEncode(message.id)}`, '_blank')}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
{session?.user?.pubkey && (
|
||||
<ZapThreadsWrapper
|
||||
anchor={nip19.noteEncode(message.id)}
|
||||
user={npub}
|
||||
relays="wss://nos.lol/, wss://relay.damus.io/, wss://relay.snort.social/, wss://relay.nostr.band/, wss://nostr.mutinywallet.com/, wss://relay.mutinywallet.com/, wss://relay.primal.net/"
|
||||
disable=""
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="h-[100vh] min-bottom-bar:w-[86vw] max-sidebar:w-[100vw]">
|
||||
@ -144,16 +88,32 @@ const NostrFeed = ({ searchQuery }) => {
|
||||
<div className="mx-4 mt-4">
|
||||
{filteredNotes.length > 0 ? (
|
||||
filteredNotes.map(message => (
|
||||
<Card
|
||||
<CommunityMessage
|
||||
key={message.id}
|
||||
header={renderHeader(message)}
|
||||
footer={() => footer(message)}
|
||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||
>
|
||||
<p className="m-0 text-lg text-gray-200 overflow-hidden break-words">
|
||||
{highlightText(message.content, searchQuery)}
|
||||
</p>
|
||||
</Card>
|
||||
message={{
|
||||
id: message.id,
|
||||
author: authorData[message.pubkey]?.username || message.pubkey.substring(0, 12) + '...',
|
||||
avatar: authorData[message.pubkey]?.avatar ? returnImageProxy(authorData[message.pubkey]?.avatar) : null,
|
||||
content: message.content,
|
||||
timestamp: message.created_at * 1000,
|
||||
channel: "plebdevs"
|
||||
}}
|
||||
searchQuery={searchQuery}
|
||||
windowWidth={windowWidth}
|
||||
platform="nostr"
|
||||
platformIcon={<Image src={NostrIcon} alt="Nostr" width={14} height={14} className='mr-[1px]' />}
|
||||
platformLink={`https://nostr.band/${nip19.noteEncode(message.id)}`}
|
||||
additionalFooter={
|
||||
session?.user?.pubkey && (
|
||||
<ZapThreadsWrapper
|
||||
anchor={nip19.noteEncode(message.id)}
|
||||
user={npub}
|
||||
relays="wss://nos.lol/, wss://relay.damus.io/, wss://relay.snort.social/, wss://relay.nostr.band/, wss://nostr.mutinywallet.com/, wss://relay.mutinywallet.com/, wss://relay.primal.net/"
|
||||
disable=""
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="text-gray-400 text-center p-4">No messages available.</div>
|
||||
|
@ -1,104 +1,72 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Avatar } from 'primereact/avatar';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { Button } from 'primereact/button';
|
||||
import React from 'react';
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { highlightText } from '@/utils/text';
|
||||
import axios from 'axios';
|
||||
import CommunityMessage from '@/components/feeds/messages/CommunityMessage';
|
||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||
|
||||
const StackerNewsIconComponent = () => (
|
||||
<svg width="16" height="16" className='mr-2' viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#facc15" fillRule="evenodd" d="m41.7 91.4 41.644 59.22-78.966 69.228L129.25 155.94l-44.083-58.14 54.353-65.441Z"/>
|
||||
<path fill="#facc15" fillRule="evenodd" d="m208.355 136.74-54.358-64.36-38.4 128.449 48.675-74.094 64.36 65.175L251.54 42.497Z"/>
|
||||
</svg>
|
||||
<svg width="16" height="16" className='mr-2' viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#facc15" fillRule="evenodd" d="m41.7 91.4 41.644 59.22-78.966 69.228L129.25 155.94l-44.083-58.14 54.353-65.441Z"/>
|
||||
<path fill="#facc15" fillRule="evenodd" d="m208.355 136.74-54.358-64.36-38.4 128.449 48.675-74.094 64.36 65.175L251.54 42.497Z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const fetchStackerNews = async () => {
|
||||
const response = await axios.get('/api/stackernews');
|
||||
return response.data.data.items.items; // Note the change here
|
||||
const response = await axios.get('/api/stackernews');
|
||||
return response.data.data.items.items;
|
||||
};
|
||||
|
||||
const StackerNewsFeed = ({ searchQuery }) => {
|
||||
const { data: items, isLoading, error } = useQuery({queryKey: ['stackerNews'], queryFn: fetchStackerNews});
|
||||
const { data: items, isLoading, error } = useQuery({queryKey: ['stackerNews'], queryFn: fetchStackerNews});
|
||||
const windowWidth = useWindowWidth();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(items);
|
||||
}, [items]);
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="h-[100vh] min-bottom-bar:w-[86vw] max-sidebar:w-[100vw]">
|
||||
<ProgressSpinner className='w-full mt-24 mx-auto' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="h-[100vh] min-bottom-bar:w-[86vw] max-sidebar:w-[100vw]">
|
||||
<ProgressSpinner className='w-full mt-24 mx-auto' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (error) {
|
||||
console.error('Error fetching Stacker News:', error);
|
||||
return <div className="text-red-500 text-center p-4">Error loading data. Please try again later.</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
console.error('Error fetching Stacker News:', error);
|
||||
return <div className="text-red-500 text-center p-4">Error loading data. Please try again later.</div>;
|
||||
}
|
||||
const filteredItems = items.filter(item =>
|
||||
item.title.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
const header = (item) => (
|
||||
<div className="flex flex-row w-full items-center justify-between p-4 bg-gray-800 rounded-t-lg">
|
||||
<div className="flex flex-row items-center">
|
||||
<Avatar icon="pi pi-user" shape="circle" size="large" className="border-2 border-blue-400" />
|
||||
<p className="pl-4 font-bold text-xl text-white">{item.user.name}</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start justify-between">
|
||||
<div className="flex flex-row w-full justify-between items-center my-1 max-sidebar:flex-col max-sidebar:items-start">
|
||||
<Tag value="~devs" severity="contrast" className="w-fit text-[#f8f8ff] mr-2 max-sidebar:mr-0" />
|
||||
<Tag icon={<StackerNewsIconComponent />} value="stackernews" className="w-fit bg-gray-600 text-[#f8f8ff] max-sidebar:mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const footer = (item) => (
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<span className="bg-gray-800 rounded-lg p-2 text-sm text-gray-300">
|
||||
{new Date(item.createdAt).toLocaleString()}
|
||||
</span>
|
||||
<Button
|
||||
label="View on Stacker News"
|
||||
icon="pi pi-external-link"
|
||||
severity="warning"
|
||||
outlined
|
||||
size="small"
|
||||
className='my-2'
|
||||
onClick={() => window.open(`https://stacker.news/items/${item.id}`, '_blank')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const filteredItems = items.filter(item =>
|
||||
item.title.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
||||
<div className="mx-4 mt-4">
|
||||
{filteredItems && filteredItems.length > 0 ? (
|
||||
filteredItems.map(item => (
|
||||
<Card
|
||||
key={item.id}
|
||||
header={() => header(item)}
|
||||
footer={() => footer(item)}
|
||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||
>
|
||||
<h3 className="m-0 text-lg text-gray-200">{highlightText(item.title, searchQuery)}</h3>
|
||||
<p className="text-sm text-gray-400">
|
||||
Comments: {item.comments.length} | Sats: {item.sats}
|
||||
</p>
|
||||
</Card>
|
||||
))
|
||||
) : (
|
||||
<div className="text-gray-400 text-center p-4">No items available.</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
||||
<div className="mx-4 mt-4">
|
||||
{filteredItems && filteredItems.length > 0 ? (
|
||||
filteredItems.map(item => (
|
||||
<CommunityMessage
|
||||
key={item.id}
|
||||
message={{
|
||||
id: item.id,
|
||||
author: item.user.name,
|
||||
avatar: item.user.image,
|
||||
content: item.title,
|
||||
timestamp: item.createdAt,
|
||||
channel: "~devs",
|
||||
additionalContent: `Comments: ${item.comments.length} | Sats: ${item.sats}`
|
||||
}}
|
||||
searchQuery={searchQuery}
|
||||
windowWidth={windowWidth}
|
||||
platform="stackernews"
|
||||
platformIcon={<StackerNewsIconComponent />}
|
||||
platformLink={`https://stacker.news/items/${item.id}`}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="text-gray-400 text-center p-4">No items available.</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StackerNewsFeed;
|
95
src/components/feeds/messages/CommunityMessage.js
Normal file
95
src/components/feeds/messages/CommunityMessage.js
Normal file
@ -0,0 +1,95 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Avatar } from 'primereact/avatar';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { highlightText } from '@/utils/text';
|
||||
import NostrIcon from '../../../../public/images/nostr.png';
|
||||
import Image from 'next/image';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
|
||||
const StackerNewsIconComponent = () => (
|
||||
<svg width="16" height="16" className='mr-2' viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#facc15" fillRule="evenodd" d="m41.7 91.4 41.644 59.22-78.966 69.228L129.25 155.94l-44.083-58.14 54.353-65.441Z"/>
|
||||
<path fill="#facc15" fillRule="evenodd" d="m208.355 136.74-54.358-64.36-38.4 128.449 48.675-74.094 64.36 65.175L251.54 42.497Z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CommunityMessage = ({ message, searchQuery, windowWidth, platform }) => {
|
||||
const header = (
|
||||
<div className="flex flex-row w-full items-center justify-between p-4 bg-gray-800 rounded-t-lg">
|
||||
<div className="flex flex-row items-center">
|
||||
<Avatar image={message.avatar} shape="circle" size="large" className="border-2 border-blue-400" />
|
||||
<p className="pl-4 font-bold text-xl text-white">{message.author}</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start justify-between">
|
||||
<div className="flex flex-row w-full justify-between items-center my-1 max-sidebar:flex-col max-sidebar:items-start">
|
||||
<Tag value={message.channel} severity="primary" className="w-fit text-[#f8f8ff] bg-gray-600 mr-2 max-sidebar:mr-0" />
|
||||
<Tag icon={getPlatformIcon(platform)} value={platform} className="w-fit text-[#f8f8ff] bg-blue-400 max-sidebar:mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const footer = (
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<span className="rounded-lg text-sm text-gray-300">
|
||||
{new Date(message.timestamp).toLocaleString()}
|
||||
</span>
|
||||
<GenericButton
|
||||
label={windowWidth > 768 ? `View in ${platform}` : null}
|
||||
icon="pi pi-external-link"
|
||||
outlined
|
||||
size="small"
|
||||
className='my-2'
|
||||
onClick={() => window.open(getPlatformLink(platform, message.id), '_blank')}
|
||||
tooltip={windowWidth < 768 ? `View in ${platform}` : null}
|
||||
tooltipOptions={{ position: 'left' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
header={header}
|
||||
footer={footer}
|
||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||
pt={{
|
||||
footer: {
|
||||
className: 'mt-0 pt-0'
|
||||
},
|
||||
content: {
|
||||
className: 'mt-0 pt-0'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<p className="m-0 text-lg text-gray-200 break-words">{highlightText(message.content, searchQuery)}</p>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const getPlatformLink = (platform, id) => {
|
||||
switch (platform) {
|
||||
case 'discord':
|
||||
return "https://discord.gg/t8DCMcq39d";
|
||||
case 'stackernews':
|
||||
return `https://stacker.news/items/${id}`;
|
||||
case 'nostr':
|
||||
return `https://nostr.band/${nip19.noteEncode(id)}`;
|
||||
default:
|
||||
return "#";
|
||||
}
|
||||
};
|
||||
|
||||
const getPlatformIcon = (platform) => {
|
||||
switch (platform) {
|
||||
case 'stackernews':
|
||||
return <StackerNewsIconComponent />;
|
||||
case 'nostr':
|
||||
return <Image src={NostrIcon} alt="Nostr" width={16} height={16} className='mr-1' />;
|
||||
default:
|
||||
return `pi pi-${platform}`;
|
||||
}
|
||||
};
|
||||
|
||||
export default CommunityMessage;
|
@ -4,8 +4,8 @@ import { InputText } from "primereact/inputtext";
|
||||
import { InputTextarea } from "primereact/inputtextarea";
|
||||
import { InputNumber } from "primereact/inputnumber";
|
||||
import { InputSwitch } from "primereact/inputswitch";
|
||||
import { Button } from "primereact/button";
|
||||
import { useRouter } from "next/router";;
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { useNDKContext } from "@/context/NDKContext";
|
||||
@ -263,12 +263,12 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
<div className="p-inputgroup flex-1" key={index}>
|
||||
<InputText value={link} onChange={(e) => handleAdditionalLinkChange(index, e.target.value)} placeholder="https://plebdevs.com" className="w-full mt-2" />
|
||||
{index > 0 && (
|
||||
<Button icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeAdditionalLink(e, index)} />
|
||||
<GenericButton icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeAdditionalLink(e, index)} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="w-full flex flex-row items-end justify-end py-2">
|
||||
<Button icon="pi pi-plus" onClick={addAdditionalLink} />
|
||||
<GenericButton icon="pi pi-plus" onClick={addAdditionalLink} />
|
||||
</div>
|
||||
<Tooltip target=".pi-info-circle" />
|
||||
</div>
|
||||
@ -277,16 +277,16 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
<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)} />
|
||||
<GenericButton 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} />
|
||||
<GenericButton icon="pi pi-plus" onClick={addTopic} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-center mt-8">
|
||||
<Button type="submit" severity="success" outlined label={draft ? "Update Draft" : "Save Draft"} />
|
||||
<GenericButton type="submit" severity="success" outlined label={draft ? "Update Draft" : "Save Draft"} />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import 'primeicons/primeicons.css';
|
||||
@ -179,12 +179,12 @@ const WorkshopForm = ({ draft = null }) => {
|
||||
<div className="p-inputgroup flex-1" key={index}>
|
||||
<InputText value={link} onChange={(e) => handleLinkChange(index, e.target.value)} placeholder="https://example.com" className="w-full mt-2" />
|
||||
{index > 0 && (
|
||||
<Button icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeLink(e, index)} />
|
||||
<GenericButton icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeLink(e, index)} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="w-full flex flex-row items-end justify-end py-2">
|
||||
<Button icon="pi pi-plus" onClick={addLink} />
|
||||
<GenericButton icon="pi pi-plus" onClick={addLink} />
|
||||
</div>
|
||||
<Tooltip target=".pi-info-circle" />
|
||||
</div>
|
||||
@ -193,16 +193,16 @@ const WorkshopForm = ({ draft = null }) => {
|
||||
<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)} />
|
||||
<GenericButton 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} />
|
||||
<GenericButton 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"} />
|
||||
<GenericButton type="submit" severity="success" outlined label={draft ? "Update" : "Submit"} />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useRouter } from 'next/router';
|
||||
@ -213,14 +213,14 @@ const CourseForm = ({ draft = null }) => {
|
||||
<div key={index} className="p-inputgroup flex-1 mt-4">
|
||||
<InputText value={topic} onChange={(e) => handleTopicChange(index, e.target.value)} placeholder={`Topic #${index + 1}`} className="w-full" />
|
||||
{index > 0 && (
|
||||
<Button icon="pi pi-times" className="p-button-danger mt-2" onClick={() => removeTopic(index)} />
|
||||
<GenericButton icon="pi pi-times" className="p-button-danger mt-2" onClick={() => removeTopic(index)} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<Button type="button" icon="pi pi-plus" onClick={addTopic} className="p-button-outlined mt-2" />
|
||||
<GenericButton icon="pi pi-plus" onClick={addTopic} className="p-button-outlined mt-2" />
|
||||
</div>
|
||||
<div className="flex justify-center mt-8">
|
||||
<Button type="submit" label={draft ? 'Update Course Draft' : 'Create Course Draft'} className="p-button-raised p-button-success" />
|
||||
<GenericButton type="submit" severity="success" outlined label={draft ? 'Update Course Draft' : 'Create Course Draft'} />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion';
|
||||
import EmbeddedResourceForm from '@/components/forms/course/embedded/EmbeddedResourceForm';
|
||||
@ -141,7 +141,7 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
return (
|
||||
<div className="flex justify-between items-center">
|
||||
<p>Lesson {index + 1}</p>
|
||||
<Button icon="pi pi-times" className="p-button-danger" onClick={() => removeLesson(index)} />
|
||||
<GenericButton icon="pi pi-times" className="p-button-danger" onClick={() => removeLesson(index)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -167,8 +167,8 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
<div className="flex mt-4">
|
||||
{lesson.id ? null : (
|
||||
<>
|
||||
<Button label="New Resource" onClick={(e) => {e.preventDefault(); setShowResourceForm(true)}} className="mr-2" />
|
||||
<Button label="New Workshop" onClick={(e) => {e.preventDefault(); setShowWorkshopForm(true)}} className="mr-2" />
|
||||
<GenericButton label="New Resource" onClick={(e) => {e.preventDefault(); setShowResourceForm(true)}} className="mr-2" />
|
||||
<GenericButton label="New Workshop" onClick={(e) => {e.preventDefault(); setShowWorkshopForm(true)}} className="mr-2" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@ -183,7 +183,7 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
</AccordionTab>
|
||||
))}
|
||||
</Accordion>
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Add New Lesson"
|
||||
onClick={addNewLesson}
|
||||
className="mt-4"
|
||||
|
@ -3,7 +3,7 @@ import axios from "axios";
|
||||
import { InputText } from "primereact/inputtext";
|
||||
import { InputNumber } from "primereact/inputnumber";
|
||||
import { InputSwitch } from "primereact/inputswitch";
|
||||
import { Button } from "primereact/button";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { useNDKContext } from "@/context/NDKContext";
|
||||
@ -194,12 +194,12 @@ const EmbeddedResourceForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
<div className="p-inputgroup flex-1" key={index}>
|
||||
<InputText value={link} onChange={(e) => handleAdditionalLinkChange(index, e.target.value)} placeholder="https://plebdevs.com" className="w-full mt-2" />
|
||||
{index > 0 && (
|
||||
<Button icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeAdditionalLink(e, index)} />
|
||||
<GenericButton icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeAdditionalLink(e, index)} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="w-full flex flex-row items-end justify-end py-2">
|
||||
<Button icon="pi pi-plus" onClick={addAdditionalLink} />
|
||||
<GenericButton icon="pi pi-plus" onClick={addAdditionalLink} />
|
||||
</div>
|
||||
<Tooltip target=".pi-info-circle" />
|
||||
</div>
|
||||
@ -208,16 +208,16 @@ const EmbeddedResourceForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
<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)} />
|
||||
<GenericButton 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} />
|
||||
<GenericButton 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"} />
|
||||
<GenericButton type="submit" severity="success" outlined label={draft ? "Update" : "Submit"} />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import { Button } from 'primereact/button';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import 'primeicons/primeicons.css';
|
||||
@ -156,12 +155,12 @@ const EmbeddedWorkshopForm = ({ draft = null, onSave, isPaid }) => {
|
||||
<div className="p-inputgroup flex-1" key={index}>
|
||||
<InputText value={link} onChange={(e) => handleLinkChange(index, e.target.value)} placeholder="https://example.com" className="w-full mt-2" />
|
||||
{index > 0 && (
|
||||
<Button icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeLink(e, index)} />
|
||||
<GenericButton icon="pi pi-times" className="p-button-danger mt-2" onClick={(e) => removeLink(e, index)} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="w-full flex flex-row items-end justify-end py-2">
|
||||
<Button icon="pi pi-plus" onClick={addLink} />
|
||||
<GenericButton icon="pi pi-plus" onClick={addLink} />
|
||||
</div>
|
||||
<Tooltip target=".pi-info-circle" />
|
||||
</div>
|
||||
@ -170,16 +169,16 @@ const EmbeddedWorkshopForm = ({ draft = null, onSave, isPaid }) => {
|
||||
<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)} />
|
||||
<GenericButton 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} />
|
||||
<GenericButton 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"} />
|
||||
<GenericButton type="submit" severity="success" outlined label={draft ? "Update" : "Submit"} />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { TabMenu } from 'primereact/tabmenu';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import Image from 'next/image';
|
||||
import StackerNewsIcon from '../../../public/images/sn.svg';
|
||||
import NostrIcon from '../../../public/images/nostr.png';
|
||||
@ -35,12 +35,12 @@ const CommunityMenuTab = ({ selectedTopic, onTabChange }) => {
|
||||
|
||||
return {
|
||||
label: (
|
||||
<Button
|
||||
<GenericButton
|
||||
className={`${selectedTopic === item ? 'bg-primary text-white' : ''}`}
|
||||
onClick={() => onTabChange(item)}
|
||||
outlined={selectedTopic !== item}
|
||||
rounded
|
||||
size='small'
|
||||
size="small"
|
||||
label={item}
|
||||
icon={icon}
|
||||
/>
|
||||
|
@ -2,7 +2,7 @@ import React, { useRef, useState, useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { Menu } from 'primereact/menu';
|
||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||
import { useSession, signOut } from 'next-auth/react';
|
||||
@ -87,7 +87,7 @@ const UserAvatar = () => {
|
||||
} else {
|
||||
userAvatar = (
|
||||
<div className='flex flex-row items-center justify-between'>
|
||||
<Button severity='help' rounded label="About" className='text-[#f8f8ff] mr-4' onClick={() => setVisible(true)} size={windowWidth < 768 ? 'small' : 'normal'} />
|
||||
<GenericButton severity='help' rounded label="About" className='text-[#f8f8ff] mr-4' onClick={() => setVisible(true)} size={windowWidth < 768 ? 'small' : 'normal'} />
|
||||
<Dialog header="About" visible={visible} onHide={() => { if (!visible) return; setVisible(false); }} className='w-[50vw] max-tab:w-[95vw]'>
|
||||
<div className="space-y-6">
|
||||
<p className="text-lg"><i className="pi pi-info-circle mr-2"></i>PlebDevs is a custom-built education platform designed to help aspiring developers, with a special focus on Bitcoin Lightning and Nostr technologies.</p>
|
||||
@ -136,7 +136,7 @@ const UserAvatar = () => {
|
||||
<p className="italic text-lg"><i className="pi pi-flag mr-2"></i>PlebDevs aims to provide a comprehensive, decentralized learning experience for aspiring developers, with a strong emphasis on emerging technologies in the Bitcoin ecosystem.</p>
|
||||
</div>
|
||||
</Dialog>
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Login"
|
||||
icon="pi pi-user"
|
||||
className="text-[#f8f8ff]"
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { Button } from "primereact/button";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import MenuTab from "@/components/menutab/MenuTab";
|
||||
import { useCourses } from "@/hooks/nostr/useCourses";
|
||||
import { useResources } from "@/hooks/nostr/useResources";
|
||||
@ -13,6 +13,7 @@ import { useToast } from "@/hooks/useToast";
|
||||
import { Divider } from "primereact/divider";
|
||||
import ContentList from "@/components/content/lists/ContentList";
|
||||
import { parseEvent } from "@/utils/nostr";
|
||||
import { ProgressSpinner } from "primereact/progressspinner";
|
||||
import { useNDKContext } from "@/context/NDKContext";
|
||||
|
||||
const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY;
|
||||
@ -122,7 +123,7 @@ const UserContent = () => {
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={setActiveIndex}
|
||||
/>
|
||||
<Button
|
||||
<GenericButton
|
||||
onClick={() => router.push("/create")}
|
||||
label="Create"
|
||||
severity="success"
|
||||
@ -134,7 +135,7 @@ const UserContent = () => {
|
||||
<div className="w-full mx-auto my-8">
|
||||
<div className="w-full mx-auto px-8 max-tab:px-0">
|
||||
{isLoading ? (
|
||||
<p>Loading...</p>
|
||||
<ProgressSpinner className="w-full mx-auto" />
|
||||
) : isError ? (
|
||||
<p>Error loading content.</p>
|
||||
) : content.length > 0 ? (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { Button } from "primereact/button";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import { DataTable } from "primereact/datatable";
|
||||
import { Column } from "primereact/column";
|
||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||
@ -28,8 +28,13 @@ const UserSettings = () => {
|
||||
"wss://relay.nostr.band/",
|
||||
"wss://nostr.mutinywallet.com/",
|
||||
"wss://relay.mutinywallet.com/",
|
||||
"wss://relay.primal.net/"
|
||||
];
|
||||
"wss://relay.primal.net/",
|
||||
"wss://nostr21.com/",
|
||||
"wss://nostrue.com/",
|
||||
"wss://nostr.band/",
|
||||
"wss://nostr.land/",
|
||||
"wss://purplerelay.com/",
|
||||
];
|
||||
|
||||
const relayStatusBody = (url) => {
|
||||
// Placeholder for relay status, replace with actual logic later
|
||||
@ -42,8 +47,8 @@ const UserSettings = () => {
|
||||
const relayActionsBody = () => {
|
||||
return (
|
||||
<div>
|
||||
<Button icon="pi pi-plus" className="p-button-rounded p-button-success p-button-text mr-2" />
|
||||
<Button icon="pi pi-trash" className="p-button-rounded p-button-danger p-button-text" />
|
||||
<GenericButton icon="pi pi-plus" className="p-button-rounded p-button-success p-button-text mr-2" />
|
||||
<GenericButton icon="pi pi-trash" className="p-button-rounded p-button-danger p-button-text" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -51,7 +56,7 @@ const UserSettings = () => {
|
||||
const header = (
|
||||
<div className="flex flex-row justify-between">
|
||||
<span className="text-xl text-900 font-bold text-[#f8f8ff]">Relays</span>
|
||||
<Button icon="pi pi-plus" className="p-button-rounded p-button-success p-button-text mr-2" />
|
||||
<GenericButton icon="pi pi-plus" className="p-button-rounded p-button-success p-button-text mr-2" />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -8,7 +8,7 @@ import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Badge } from 'primereact/badge';
|
||||
import { Button } from "primereact/button";
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { Menu } from "primereact/menu";
|
||||
import { Message } from "primereact/message";
|
||||
|
||||
@ -126,7 +126,7 @@ const SubscribeModal = ({ user }) => {
|
||||
{(!subscribed && !subscriptionExpiredAt) && (
|
||||
<div className="flex flex-col">
|
||||
<Message className="w-fit" severity="info" text="You currently have no active subscription" />
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Subscribe"
|
||||
className="w-auto mt-8 text-[#f8f8ff]"
|
||||
onClick={() => setVisible(true)}
|
||||
@ -136,7 +136,7 @@ const SubscribeModal = ({ user }) => {
|
||||
{subscriptionExpiredAt && (
|
||||
<div className="flex flex-col">
|
||||
<Message className="w-fit" severity="warn" text={`Your subscription expired on ${subscriptionExpiredAt.toLocaleDateString()}`} />
|
||||
<Button
|
||||
<GenericButton
|
||||
label="Subscribe"
|
||||
className="w-auto mt-8 text-[#f8f8ff]"
|
||||
onClick={() => setVisible(true)}
|
||||
|
@ -5,6 +5,7 @@ import { useSession, signOut } from 'next-auth/react';
|
||||
import { Button } from 'primereact/button';
|
||||
import 'primeicons/primeicons.css';
|
||||
import styles from "./sidebar.module.css";
|
||||
import { Divider } from 'primereact/divider';
|
||||
|
||||
const Sidebar = () => {
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
@ -102,6 +103,7 @@ const Sidebar = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Divider className='pt-0 mt-0' />
|
||||
<div className='mt-auto'>
|
||||
{isExpanded ? (
|
||||
<div className='mt-auto'>
|
||||
|
@ -11,7 +11,12 @@ const relayUrls = [
|
||||
"wss://relay.nostr.band/",
|
||||
"wss://nostr.mutinywallet.com/",
|
||||
"wss://relay.mutinywallet.com/",
|
||||
"wss://relay.primal.net/"
|
||||
"wss://relay.primal.net/",
|
||||
"wss://nostr21.com/",
|
||||
"wss://nostrue.com/",
|
||||
"wss://nostr.band/",
|
||||
"wss://nostr.land/",
|
||||
"wss://purplerelay.com/",
|
||||
];
|
||||
|
||||
export const NDKProvider = ({ children }) => {
|
||||
|
@ -9,18 +9,18 @@ export function useDraftsQuery() {
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (session) {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (session?.user) {
|
||||
setUser(session.user);
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const fetchDraftsDB = async () => {
|
||||
try {
|
||||
if (!user.id) {
|
||||
if (!user?.id) {
|
||||
return [];
|
||||
}
|
||||
const response = await axios.get(`/api/drafts/all/${user.id}`);
|
||||
@ -34,11 +34,9 @@ export function useDraftsQuery() {
|
||||
};
|
||||
|
||||
const { data: drafts, isLoading: draftsLoading, error: draftsError, refetch: refetchDrafts } = useQuery({
|
||||
queryKey: ['drafts', isClient],
|
||||
queryKey: ['drafts', isClient, user?.id],
|
||||
queryFn: fetchDraftsDB,
|
||||
// staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
// refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient && !!user.id, // Only enable if client-side and user ID is available
|
||||
enabled: isClient && !!user?.id,
|
||||
});
|
||||
|
||||
return { drafts, draftsLoading, draftsError, refetchDrafts };
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { signIn, useSession } from "next-auth/react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { useNDKContext } from "@/context/NDKContext";
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
|
||||
export default function SignIn() {
|
||||
@ -43,21 +43,21 @@ export default function SignIn() {
|
||||
return (
|
||||
<div className="w-[100vw] min-bottom-bar:w-[86vw] mx-auto mt-24 flex flex-col justify-center">
|
||||
<h1 className="text-center mb-8">Sign In</h1>
|
||||
<Button
|
||||
<GenericButton
|
||||
label={"login with nostr"}
|
||||
icon="pi pi-user"
|
||||
className="text-[#f8f8ff] w-[250px] my-4 mx-auto"
|
||||
rounded
|
||||
onClick={handleNostrSignIn}
|
||||
/>
|
||||
<Button
|
||||
<GenericButton
|
||||
label={"login anonymously"}
|
||||
icon="pi pi-user"
|
||||
className="text-[#f8f8ff] w-[250px] my-4 mx-auto"
|
||||
rounded
|
||||
onClick={handleAnonymousSignIn}
|
||||
/>
|
||||
<Button
|
||||
<GenericButton
|
||||
label={"login with email"}
|
||||
icon="pi pi-envelope"
|
||||
className="text-[#f8f8ff] w-[250px] my-4 mx-auto"
|
||||
@ -73,7 +73,7 @@ export default function SignIn() {
|
||||
placeholder="Enter your email"
|
||||
className="w-[250px] my-4"
|
||||
/>
|
||||
<Button
|
||||
<GenericButton
|
||||
type="submit"
|
||||
label={"Submit"}
|
||||
icon="pi pi-check"
|
||||
|
@ -7,7 +7,7 @@ import { useCourses } from '@/hooks/nostr/useCourses';
|
||||
import { TabMenu } from 'primereact/tabmenu';
|
||||
import 'primeicons/primeicons.css';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const MenuTab = ({ items, selectedTopic, onTabChange }) => {
|
||||
@ -26,7 +26,7 @@ const MenuTab = ({ items, selectedTopic, onTabChange }) => {
|
||||
|
||||
return {
|
||||
label: (
|
||||
<Button
|
||||
<GenericButton
|
||||
className={`${isActive ? 'bg-primary text-white' : ''}`}
|
||||
onClick={() => {
|
||||
onTabChange(item);
|
||||
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import { parseEvent, findKind0Fields } from '@/utils/nostr';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { nip19, nip04 } from 'nostr-tools';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
@ -247,8 +247,8 @@ export default function Details() {
|
||||
{authorView && (
|
||||
<div className='w-[75vw] mx-auto flex flex-row justify-end mt-12'>
|
||||
<div className='w-fit flex flex-row justify-between'>
|
||||
<Button onClick={() => router.push(`/details/${processedEvent.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<Button onClick={handleDelete} label="Delete" severity='danger' outlined className="w-auto m-2 mr-0" />
|
||||
<GenericButton onClick={() => router.push(`/details/${processedEvent.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={handleDelete} label="Delete" severity='danger' outlined className="w-auto m-2 mr-0" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -6,7 +6,7 @@ import { nip19, nip04 } from 'nostr-tools';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
@ -314,9 +314,9 @@ export default function Draft() {
|
||||
</div>
|
||||
<div className='w-[75vw] mx-auto flex flex-row justify-end mt-12'>
|
||||
<div className='w-fit flex flex-row justify-between'>
|
||||
<Button onClick={handleSubmit} label="Publish" severity='success' outlined className="w-auto m-2" />
|
||||
<Button onClick={() => router.push(`/draft/${draft?.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<Button onClick={handleDelete} label="Delete" severity='danger' outlined className="w-auto m-2 mr-0" />
|
||||
<GenericButton onClick={handleSubmit} label="Publish" severity='success' outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={() => router.push(`/draft/${draft?.id}/edit`)} label="Edit" severity='warning' outlined className="w-auto m-2" />
|
||||
<GenericButton onClick={handleDelete} label="Delete" severity='danger' outlined className="w-auto m-2 mr-0" />
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-[75vw] mx-auto mt-12 p-12 border-t-2 border-gray-300 max-tab:p-0 max-mob:p-0 max-tab:max-w-[100vw] max-mob:max-w-[100vw]'>
|
||||
|
@ -10,7 +10,7 @@ import { useRouter } from 'next/router';
|
||||
import MessageInput from '@/components/feeds/MessageInput';
|
||||
import StackerNewsIcon from '../../public/images/sn.svg';
|
||||
import NostrIcon from '../../public/images/nostr.png';
|
||||
import { Button } from 'primereact/button';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { Divider } from 'primereact/divider';
|
||||
|
||||
const Feed = () => {
|
||||
@ -59,7 +59,7 @@ const Feed = () => {
|
||||
<div className="w-[100vw] min-bottom-bar:w-[86vw] px-4 pt-4 flex flex-col items-start">
|
||||
<div className='mb-4 flex flex-row items-end'>
|
||||
<h2 className="font-bold mb-0">Community</h2>
|
||||
<Button
|
||||
<GenericButton
|
||||
icon={getTagIcon(title)}
|
||||
className='ml-2 text-sm p-2 py-1 flex items-center cursor-default hover:bg-transparent'
|
||||
outlined
|
||||
|
Loading…
x
Reference in New Issue
Block a user