mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-01 13:34:30 +01:00
Merge branch 'dev' into feature/linking-accounts
This commit is contained in:
23
src/Components/Card/Card.stories.tsx
Normal file
23
src/Components/Card/Card.stories.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import Card from './Card';
|
||||
|
||||
export default {
|
||||
title: 'Shared/Card',
|
||||
component: Card,
|
||||
argTypes: {
|
||||
backgroundColor: { control: 'color' },
|
||||
},
|
||||
} as ComponentMeta<typeof Card>;
|
||||
|
||||
|
||||
const Template: ComponentStory<typeof Card> = (args) => <Card {...args} > <p className="text-body4 text-gray-700">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Voluptas corrupti molestias, accusantium porro vitae mollitia voluptatibus omnis. Itaque assumenda minus cum reprehenderit sit, cupiditate, impedit doloribus ad modi corporis maiores. Corrupti praesentium, dolor vero veniam suscipit architecto accusamus beatae minus iste sed ea harum aperiam quibusdam fugiat molestias dolores magni!</p>
|
||||
</Card>
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
onlyMd: true
|
||||
}
|
||||
33
src/Components/Card/Card.tsx
Normal file
33
src/Components/Card/Card.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
import React, { PropsWithChildren } from 'react'
|
||||
|
||||
interface Props extends React.ComponentPropsWithoutRef<'div'> {
|
||||
onlyMd?: boolean;
|
||||
defaultPadding?: boolean
|
||||
|
||||
}
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, PropsWithChildren<Props>>(({
|
||||
onlyMd = false,
|
||||
defaultPadding = true,
|
||||
className,
|
||||
...props
|
||||
}, ref) => {
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={`
|
||||
${onlyMd ?
|
||||
`md:bg-white md:rounded-16 md:border-2 border-gray-200 ${defaultPadding && "md:p-24"}` :
|
||||
`bg-white rounded-12 md:rounded-16 border-2 border-gray-200 ${defaultPadding && "p-16 md:p-24"}`
|
||||
}
|
||||
${className}
|
||||
`}
|
||||
></div>
|
||||
)
|
||||
})
|
||||
|
||||
export default Card;
|
||||
@@ -7,6 +7,7 @@ import DOMPurify from 'dompurify';
|
||||
import { Vote_Item_Type } from "src/graphql";
|
||||
import { useVote } from "src/utils/hooks";
|
||||
import { useState } from "react";
|
||||
import Card from "src/Components/Card/Card";
|
||||
|
||||
|
||||
interface Props {
|
||||
@@ -25,13 +26,12 @@ export default function CommentCard({ comment, canReply, onReply }: Props) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="border-2 border-gray-200 rounded-12 md:rounded-16 p-24">
|
||||
<Card>
|
||||
<Header author={comment.author} date={new Date(comment.created_at).toISOString()} />
|
||||
<div
|
||||
className="text-body4 mt-16 whitespace-pre-line"
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(marked.parse(comment.body)) }}
|
||||
>
|
||||
|
||||
</div>
|
||||
<div className="flex gap-24 mt-16 items-center">
|
||||
<VoteButton
|
||||
@@ -46,6 +46,6 @@ export default function CommentCard({ comment, canReply, onReply }: Props) {
|
||||
<BiComment /> <span className="align-middle text-body5">Reply</span>
|
||||
</button>}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AiOutlineClose } from 'react-icons/ai'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { createRoute, PAGES_ROUTES } from 'src/utils/routing'
|
||||
import Preferences from 'src/services/preferences.service'
|
||||
import Card from 'src/Components/Card/Card';
|
||||
|
||||
// const createWorker = createWorkerFactory(() => import('./comments.worker'));
|
||||
|
||||
@@ -44,8 +45,7 @@ export default function CommentsSection({ type, id }: Props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="md:border-2 border-gray-200 rounded-12 md:rounded-16 md:p-32 bg-white">
|
||||
|
||||
<Card onlyMd>
|
||||
<div className="flex flex-wrap justify-between">
|
||||
<h6 className="text-body2 font-bolder">Discussion</h6>
|
||||
{connectionStatus.status === 'Connected' && <div className="bg-green-50 text-green-500 text-body5 font-medium py-4 px-12 rounded-48"> • <span className="hidden md:inline">Connected to {connectionStatus.connectedRelaysCount} relays</span> 📡</div>}
|
||||
@@ -88,6 +88,6 @@ export default function CommentsSection({ type, id }: Props) {
|
||||
onReply={content => handleNewComment(content, comment.nostr_id.toString())}
|
||||
/>)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Author, Tag, Vote_Item_Type } from 'src/graphql';
|
||||
import Badge from "src/Components/Badge/Badge"
|
||||
import { createRoute } from "src/utils/routing"
|
||||
import { BiComment } from "react-icons/bi"
|
||||
import Card from "src/Components/Card/Card"
|
||||
|
||||
|
||||
export type StoryCardType = Pick<Story,
|
||||
@@ -34,7 +35,7 @@ export default function StoryCard({ story }: Props) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-12 overflow-hidden border-2 border-gray-200">
|
||||
<Card className="overflow-hidden" defaultPadding={false}>
|
||||
{story.cover_image && <img src={story.cover_image} className='h-[200px] w-full object-cover' alt="" />}
|
||||
<div className="p-24">
|
||||
<Header author={story.author} date={story.createdAt} />
|
||||
@@ -56,6 +57,7 @@ export default function StoryCard({ story }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Card from 'src/Components/Card/Card'
|
||||
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'
|
||||
import { useTrendingPostsQuery } from 'src/graphql'
|
||||
import { random } from 'src/utils/helperFunctions'
|
||||
@@ -12,7 +13,7 @@ export default function TrendingCard() {
|
||||
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-12 md:border-2 border-gray-200 md:p-16">
|
||||
<Card onlyMd>
|
||||
<h3 className="text-body2 font-bolder mb-16">Trending on BOLT.FUN</h3>
|
||||
<ul className='flex flex-col'>
|
||||
{
|
||||
@@ -34,6 +35,6 @@ export default function TrendingCard() {
|
||||
}
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import Slider from 'src/Components/Slider/Slider';
|
||||
import { Tag, usePopularTagsQuery } from 'src/graphql';
|
||||
import { MEDIA_QUERIES } from 'src/utils/theme';
|
||||
import { capitalize } from 'src/utils/helperFunctions';
|
||||
import Card from 'src/Components/Card/Card';
|
||||
|
||||
export type FilterTag = Pick<Tag, 'id' | 'title' | "icon">
|
||||
|
||||
@@ -30,7 +31,8 @@ export default function PopularTagsFilter({ value, onChange }: Props) {
|
||||
return (
|
||||
<div className='overflow-hidden'>
|
||||
{isMdScreen ?
|
||||
<div className='bg-white border-2 border-gray-200 rounded-12 p-16'>
|
||||
<Card>
|
||||
|
||||
<p className="text-body2 font-bolder text-black mb-16">Popular Tags</p>
|
||||
<ul className=' flex flex-col gap-16'>
|
||||
{tagsQuery.loading ?
|
||||
@@ -60,7 +62,8 @@ export default function PopularTagsFilter({ value, onChange }: Props) {
|
||||
</span>
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
:
|
||||
<>
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import dayjs from "dayjs";
|
||||
import { Link } from "react-router-dom";
|
||||
import Button from "src/Components/Button/Button";
|
||||
import Card from "src/Components/Card/Card";
|
||||
import { Author } from "src/features/Posts/types";
|
||||
import Avatar from "src/features/Profiles/Components/Avatar/Avatar";
|
||||
import { trimText } from "src/utils/helperFunctions";
|
||||
@@ -16,7 +17,7 @@ interface Props {
|
||||
|
||||
export default function AuthorCard({ author }: Props) {
|
||||
return (
|
||||
<div className="bg-white p-16 border-2 border-gray-200 rounded-12">
|
||||
<Card>
|
||||
<div className='flex gap-8'>
|
||||
<Avatar width={48} src={author.avatar} />
|
||||
<div className="overflow-hidden">
|
||||
@@ -31,6 +32,6 @@ export default function AuthorCard({ author }: Props) {
|
||||
className="mt-16">
|
||||
Maker's Profile
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useAppSelector } from "src/utils/hooks";
|
||||
import { useUpdateStory } from './useUpdateStory'
|
||||
import { FaPen } from "react-icons/fa";
|
||||
import DOMPurify from 'dompurify';
|
||||
import Card from "src/Components/Card/Card";
|
||||
|
||||
|
||||
interface Props {
|
||||
@@ -26,9 +27,8 @@ export default function StoryPageContent({ story }: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="content" className="bg-white md:p-32 md:border-2 border-gray-200 rounded-16 relative">
|
||||
|
||||
|
||||
<div id="content" className="bg-white md:p-32 md:border-2 border-gray-200 rounded-16 relative"> </div>
|
||||
<Card id="content" onlyMd className="relative">
|
||||
{story.cover_image &&
|
||||
<img src={story.cover_image}
|
||||
className='w-full object-cover rounded-12 md:rounded-16 mb-16'
|
||||
@@ -72,7 +72,8 @@ export default function StoryPageContent({ story }: Props) {
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(marked.parse(story.body)) }}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
{/* <div id="comments" className="mt-10 comments_col">
|
||||
<CommentsSection comments={story.comments} />
|
||||
</div> */}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Button from 'src/Components/Button/Button';
|
||||
import { useAppDispatch } from 'src/utils/hooks';
|
||||
import { openModal } from 'src/redux/features/modals.slice';
|
||||
import Card from 'src/Components/Card/Card';
|
||||
import { useMyWalletsKeysQuery, useUpdateUserWalletsKeysMutation } from 'src/graphql';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { useReducer } from 'react';
|
||||
@@ -116,10 +117,8 @@ export default function AccountCard({ }: Props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="rounded-16 bg-white border-2 border-gray-200 p-24">
|
||||
<p className="text-body2 font-bold">Account Settings</p>
|
||||
|
||||
|
||||
<Card>
|
||||
<p className="text-body2 font-bold">🔒 Linking Accounts</p>
|
||||
<div className='mt-24 flex flex-col gap-16'>
|
||||
<p className="text-body3 font-bold">Linked Wallets</p>
|
||||
<p className="text-body4 text-gray-600">
|
||||
@@ -186,8 +185,6 @@ export default function AccountCard({ }: Props) {
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import NotFoundPage from "src/features/Shared/pages/NotFoundPage/NotFoundPage";
|
||||
import Slider from "src/Components/Slider/Slider";
|
||||
import { useProfileQuery } from "src/graphql";
|
||||
import { useAppSelector, useMediaQuery } from "src/utils/hooks";
|
||||
import CommentsSettingsCard from "../ProfilePage/CommentsSettingsCard/CommentsSettingsCard";
|
||||
import UpdateMyProfileCard from "./UpdateMyProfileCard/UpdateMyProfileCard";
|
||||
import UpdateMyProfileTab from "./UpdateMyProfileTab/UpdateMyProfileTab";
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { MEDIA_QUERIES } from "src/utils/theme";
|
||||
import AccountCard from "./AccountCard/AccountCard";
|
||||
import PreferencesTab from "./PreferencesTab/PreferencesTab";
|
||||
import Card from "src/Components/Card/Card";
|
||||
|
||||
|
||||
const links = [
|
||||
@@ -56,7 +57,7 @@ export default function EditProfilePage() {
|
||||
<div className="page-container grid grid-cols-1 md:grid-cols-4 gap-24">
|
||||
<aside>
|
||||
{isMediumScreen ?
|
||||
<div className='bg-white border-2 border-gray-200 rounded-12 p-16 sticky-side-element' >
|
||||
<Card className="sticky-side-element">
|
||||
<p className="text-body2 font-bolder text-black mb-16">Edit maker profile</p>
|
||||
<ul className=' flex flex-col gap-8'>
|
||||
{links.map((link, idx) =>
|
||||
@@ -72,7 +73,7 @@ export default function EditProfilePage() {
|
||||
</NavLink>
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
</Card>
|
||||
:
|
||||
<div className="border-b-2 border-gray-200">
|
||||
<Slider>
|
||||
@@ -96,9 +97,9 @@ export default function EditProfilePage() {
|
||||
<main className="md:col-span-3">
|
||||
<Routes>
|
||||
<Route index element={<Navigate to='my-profile' />} />
|
||||
<Route path='my-profile' element={<UpdateMyProfileCard data={profileQuery.data.profile} />} />
|
||||
<Route path='my-profile' element={<UpdateMyProfileTab data={profileQuery.data.profile} />} />
|
||||
<Route path='account' element={<AccountCard />} />
|
||||
<Route path='preferences' element={<CommentsSettingsCard nostr_prv_key={profileQuery.data.profile.nostr_prv_key} nostr_pub_key={profileQuery.data.profile.nostr_pub_key} isOwner={true} />
|
||||
<Route path='preferences' element={<PreferencesTab nostr_prv_key={profileQuery.data.profile.nostr_prv_key} nostr_pub_key={profileQuery.data.profile.nostr_pub_key} isOwner={true} />
|
||||
} />
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useToggle } from '@react-hookz/web'
|
||||
import { FaChevronDown } from 'react-icons/fa';
|
||||
import { Nullable } from 'remirror';
|
||||
import Button from 'src/Components/Button/Button';
|
||||
import CopyToClipboard from 'src/Components/CopyToClipboard/CopyToClipboard';
|
||||
import IconButton from 'src/Components/IconButton/IconButton';
|
||||
import { CONSTS } from 'src/utils';
|
||||
import { motion } from "framer-motion";
|
||||
import Card from 'src/Components/Card/Card';
|
||||
|
||||
|
||||
interface Props {
|
||||
nostr_pub_key: Nullable<string>;
|
||||
nostr_prv_key: Nullable<string>;
|
||||
|
||||
}
|
||||
|
||||
export default function CommentsSettingsCard({ nostr_prv_key, nostr_pub_key }: Props) {
|
||||
|
||||
const [relaysDropdownOpen, toggleRelaysDropdownOpen] = useToggle(false)
|
||||
|
||||
|
||||
return (
|
||||
<Card>
|
||||
|
||||
<p className="text-body2 font-bold">💬 Nostr comments <span className="bg-green-50 text-green-500 text-body5 font-medium py-4 px-12 rounded-48 ml-8">Experimental</span></p>
|
||||
<p className="mt-8 text-body4 text-gray-600">
|
||||
Our commenting system is experimental and uses Nostr to store and relay your messages and replies to our own relay, as well as relays ran by other people in the community.
|
||||
We generate Nostr keys for you since there are no popular wallets which support it.
|
||||
</p>
|
||||
|
||||
<div className='mt-24 flex flex-col gap-16'>
|
||||
<p className="text-body3 font-bold">Nostr keys</p>
|
||||
{nostr_prv_key && <div>
|
||||
<p className="text-body5 font-bold">
|
||||
Your Nostr Private Key
|
||||
</p>
|
||||
<div className="input-wrapper mt-8 relative">
|
||||
<input
|
||||
type={'password'}
|
||||
className="input-text"
|
||||
defaultValue={nostr_prv_key}
|
||||
readOnly
|
||||
/>
|
||||
|
||||
<CopyToClipboard text={nostr_prv_key} />
|
||||
</div>
|
||||
</div>}
|
||||
<div>
|
||||
<p className="text-body5 font-bold">
|
||||
Your Nostr Public Key
|
||||
</p>
|
||||
<div className="input-wrapper mt-8 relative">
|
||||
<input
|
||||
type='text'
|
||||
className="input-text"
|
||||
defaultValue={nostr_pub_key!}
|
||||
readOnly
|
||||
/>
|
||||
<CopyToClipboard text={nostr_pub_key ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* <p className="text-body4 font-bold mt-24">
|
||||
Connect your Nostr identity
|
||||
</p>
|
||||
<div className="mt-8 py-12 relative">
|
||||
<p className="text-body4 text-gray-400 font-bold">
|
||||
🚧 Coming Soon 🚧
|
||||
</p>
|
||||
</div> */}
|
||||
<div className='mt-24'>
|
||||
<div className="flex justify-between">
|
||||
<p className="text-body4 font-bold">
|
||||
Nostr relays
|
||||
</p>
|
||||
<IconButton onClick={() => toggleRelaysDropdownOpen()}>
|
||||
<motion.div
|
||||
animate={{ rotate: relaysDropdownOpen ? 180 : 0 }}
|
||||
>
|
||||
<FaChevronDown />
|
||||
</motion.div>
|
||||
</IconButton>
|
||||
</div>
|
||||
{relaysDropdownOpen &&
|
||||
<motion.ul
|
||||
initial={{ y: '-50%', opacity: 0 }}
|
||||
animate={{ y: '0', opacity: 1 }}
|
||||
className="mt-8 relative flex flex-col gap-8">
|
||||
{CONSTS.DEFAULT_RELAYS.map((url, idx) => <li key={idx} className="text-body4 border-b py-12 px-16 border border-gray-200 rounded-16">{url}</li>)}
|
||||
</motion.ul>}
|
||||
</div>
|
||||
|
||||
<Button color='gray' fullWidth disabled className='mt-24'>
|
||||
Connect your Nostr ID (coming soon)
|
||||
</Button>
|
||||
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Nullable } from 'remirror';
|
||||
import CommentsSettingsCard from './CommentsSettingsCard/CommentsSettingsCard';
|
||||
|
||||
|
||||
interface Props {
|
||||
isOwner?: boolean;
|
||||
nostr_pub_key: Nullable<string>;
|
||||
nostr_prv_key: Nullable<string>;
|
||||
|
||||
}
|
||||
|
||||
export default function PreferencesTab({ nostr_prv_key, nostr_pub_key, isOwner }: Props) {
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-24">
|
||||
<div className="col-span-2">
|
||||
<CommentsSettingsCard nostr_prv_key={nostr_prv_key} nostr_pub_key={nostr_pub_key} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Button from 'src/Components/Button/Button'
|
||||
import Card from 'src/Components/Card/Card'
|
||||
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'
|
||||
import { useProfileQuery } from 'src/graphql'
|
||||
import { trimText } from 'src/utils/helperFunctions'
|
||||
@@ -33,7 +34,7 @@ export default function SaveChangesCard(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="md:p-24 rounded-16 bg-white md:border-2 border-gray-200 flex flex-col gap-24">
|
||||
<Card onlyMd className='flex flex-col gap-24'>
|
||||
<div className='hidden md:flex gap-8'>
|
||||
<Link
|
||||
className='shrink-0'
|
||||
@@ -49,7 +50,7 @@ export default function SaveChangesCard(props: Props) {
|
||||
</p>} */}
|
||||
</div>
|
||||
<p className="hidden md:block text-body5">{trimText(profileQuery.data.profile.bio, 120)}</p>
|
||||
<div className="flex md:flex-col gap-16 justify-end">
|
||||
<div className="flex flex-col gap-16">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={props.onSubmit}
|
||||
@@ -65,6 +66,6 @@ export default function SaveChangesCard(props: Props) {
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Avatar from "src/features/Profiles/Components/Avatar/Avatar";
|
||||
import { usePrompt } from "src/utils/hooks";
|
||||
import SaveChangesCard from "../SaveChangesCard/SaveChangesCard";
|
||||
import { toast } from "react-toastify";
|
||||
import Card from "src/Components/Card/Card";
|
||||
|
||||
interface Props {
|
||||
data: Pick<User,
|
||||
@@ -62,7 +63,7 @@ const schema: yup.SchemaOf<IFormInputs> = yup.object({
|
||||
|
||||
}).required();
|
||||
|
||||
export default function UpdateMyProfileCard({ data, onClose }: Props) {
|
||||
export default function UpdateMyProfileTab({ data, onClose }: Props) {
|
||||
|
||||
const { register, formState: { errors, isDirty, }, handleSubmit, reset } = useForm<IFormInputs>({
|
||||
defaultValues: data,
|
||||
@@ -110,7 +111,7 @@ export default function UpdateMyProfileCard({ data, onClose }: Props) {
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-24">
|
||||
<div className="col-span-2 rounded-16 bg-white border-2 border-gray-200">
|
||||
<Card className="md:col-span-2">
|
||||
<div className="bg-gray-600 relative h-[160px] rounded-t-16">
|
||||
<div className="absolute left-24 bottom-0 translate-y-1/2">
|
||||
<Avatar src={data.avatar} width={120} />
|
||||
@@ -274,7 +275,7 @@ export default function UpdateMyProfileCard({ data, onClose }: Props) {
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div className="self-start sticky-side-element">
|
||||
<SaveChangesCard
|
||||
isLoading={mutationStatus.loading}
|
||||
@@ -3,7 +3,7 @@ import { User } from "src/graphql"
|
||||
import { trimText, withHttp } from "src/utils/helperFunctions"
|
||||
import { FiGithub, FiGlobe, FiLinkedin, FiTwitter } from 'react-icons/fi'
|
||||
import Button from "src/Components/Button/Button";
|
||||
import { PAGES_ROUTES } from "src/utils/routing";
|
||||
import Card from "src/Components/Card/Card";
|
||||
|
||||
interface Props {
|
||||
isOwner?: boolean;
|
||||
@@ -53,7 +53,8 @@ export default function AboutCard({ user, isOwner }: Props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="rounded-16 bg-white border-2 border-gray-200">
|
||||
<Card defaultPadding={false}>
|
||||
|
||||
<div className="bg-gray-600 relative h-[160px] rounded-t-16">
|
||||
<div className="absolute left-24 bottom-0 translate-y-1/2">
|
||||
<Avatar src={user.avatar} width={120} />
|
||||
@@ -64,8 +65,8 @@ export default function AboutCard({ user, isOwner }: Props) {
|
||||
</div>
|
||||
<div className="p-24 pt-0">
|
||||
<div className="flex flex-col gap-16">
|
||||
<h1 className="text-h2 font-bolder">
|
||||
{trimText(user.name, 20)}
|
||||
<h1 className="text-h2 font-bolder break-words">
|
||||
{user.name}
|
||||
</h1>
|
||||
|
||||
{links.some(link => link.hasValue) && <div className="flex flex-wrap gap-16">
|
||||
@@ -99,6 +100,7 @@ export default function AboutCard({ user, isOwner }: Props) {
|
||||
</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
import { useToggle } from '@react-hookz/web'
|
||||
import { FaChevronDown } from 'react-icons/fa';
|
||||
import { Nullable } from 'remirror';
|
||||
import Button from 'src/Components/Button/Button';
|
||||
import CopyToClipboard from 'src/Components/CopyToClipboard/CopyToClipboard';
|
||||
import IconButton from 'src/Components/IconButton/IconButton';
|
||||
import { CONSTS } from 'src/utils';
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
|
||||
interface Props {
|
||||
isOwner?: boolean;
|
||||
nostr_pub_key: Nullable<string>;
|
||||
nostr_prv_key: Nullable<string>;
|
||||
|
||||
}
|
||||
|
||||
export default function CommentsSettingsCard({ nostr_prv_key, nostr_pub_key, isOwner }: Props) {
|
||||
|
||||
const [relaysDropdownOpen, toggleRelaysDropdownOpen] = useToggle(false)
|
||||
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-24">
|
||||
<div className="col-span-2 rounded-16 bg-white border-2 border-gray-200 p-24">
|
||||
<p className="text-body2 font-bold">💬 Nostr comments <span className="bg-green-50 text-green-500 text-body5 font-medium py-4 px-12 rounded-48 ml-8">Experimental</span></p>
|
||||
<p className="mt-8 text-body4 text-gray-600">
|
||||
Our commenting system is experimental and uses Nostr to store and relay your messages and replies to our own relay, as well as relays ran by other people in the community.
|
||||
We generate Nostr keys for you since there are no popular wallets which support it.
|
||||
</p>
|
||||
|
||||
<div className='mt-24 flex flex-col gap-16'>
|
||||
<p className="text-body3 font-bold">Nostr keys</p>
|
||||
{nostr_prv_key && <div>
|
||||
<p className="text-body5 font-bold">
|
||||
Your Nostr Private Key
|
||||
</p>
|
||||
<div className="input-wrapper mt-8 relative">
|
||||
<input
|
||||
type={'password'}
|
||||
className="input-text"
|
||||
defaultValue={nostr_prv_key}
|
||||
readOnly
|
||||
/>
|
||||
|
||||
<CopyToClipboard text={nostr_prv_key} />
|
||||
</div>
|
||||
</div>}
|
||||
<div>
|
||||
<p className="text-body5 font-bold">
|
||||
Your Nostr Public Key
|
||||
</p>
|
||||
<div className="input-wrapper mt-8 relative">
|
||||
<input
|
||||
type='text'
|
||||
className="input-text"
|
||||
defaultValue={nostr_pub_key!}
|
||||
readOnly
|
||||
/>
|
||||
<CopyToClipboard text={nostr_pub_key ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* <p className="text-body4 font-bold mt-24">
|
||||
Connect your Nostr identity
|
||||
</p>
|
||||
<div className="mt-8 py-12 relative">
|
||||
<p className="text-body4 text-gray-400 font-bold">
|
||||
🚧 Coming Soon 🚧
|
||||
</p>
|
||||
</div> */}
|
||||
<div className='mt-24'>
|
||||
<div className="flex justify-between">
|
||||
<p className="text-body4 font-bold">
|
||||
Nostr relays
|
||||
</p>
|
||||
<IconButton onClick={() => toggleRelaysDropdownOpen()}>
|
||||
<motion.div
|
||||
animate={{ rotate: relaysDropdownOpen ? 180 : 0 }}
|
||||
>
|
||||
<FaChevronDown />
|
||||
</motion.div>
|
||||
</IconButton>
|
||||
</div>
|
||||
{relaysDropdownOpen &&
|
||||
<motion.ul
|
||||
initial={{ y: '-50%', opacity: 0 }}
|
||||
animate={{ y: '0', opacity: 1 }}
|
||||
className="mt-8 relative flex flex-col gap-8">
|
||||
{CONSTS.DEFAULT_RELAYS.map((url, idx) => <li key={idx} className="text-body4 border-b py-12 px-16 border border-gray-200 rounded-16">{url}</li>)}
|
||||
</motion.ul>}
|
||||
</div>
|
||||
|
||||
<Button color='gray' fullWidth disabled className='mt-24'>
|
||||
Connect your Nostr ID (coming soon)
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import { Helmet } from 'react-helmet'
|
||||
import { useAppSelector } from 'src/utils/hooks';
|
||||
import styles from './styles.module.scss'
|
||||
import StoriesCard from "./StoriesCard/StoriesCard"
|
||||
import CommentsSettingsCard from "./CommentsSettingsCard/CommentsSettingsCard"
|
||||
|
||||
export default function ProfilePage() {
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Badge from 'src/Components/Badge/Badge'
|
||||
import Button from 'src/Components/Button/Button'
|
||||
import Card from 'src/Components/Card/Card'
|
||||
import { Story } from 'src/features/Posts/types'
|
||||
import { getDateDifference } from 'src/utils/helperFunctions'
|
||||
import { Tag } from 'src/utils/interfaces'
|
||||
@@ -24,7 +25,7 @@ interface Props {
|
||||
|
||||
export default function StoriesCard({ stories, isOwner }: Props) {
|
||||
return (
|
||||
<div className="rounded-16 bg-white border-2 border-gray-200 p-24">
|
||||
<Card>
|
||||
<p className="text-body2 font-bold">Stories ({stories.length})</p>
|
||||
{stories.length > 0 &&
|
||||
<ul className="">
|
||||
@@ -64,6 +65,6 @@ export default function StoriesCard({ stories, isOwner }: Props) {
|
||||
</Button>}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function HottestPage() {
|
||||
<title>{`Hottest Lightning Products`}</title>
|
||||
<meta property="og:title" content={`Hottest Lightning Products`} />
|
||||
</Helmet>
|
||||
<div className='px-32'>
|
||||
<div className='page-container'>
|
||||
<HeaderImage
|
||||
isLoading={loading}
|
||||
title={"Hottest Projects"}
|
||||
|
||||
@@ -6,10 +6,12 @@ import App from './App';
|
||||
|
||||
|
||||
if (process.env.REACT_APP_ENABLE_MOCKS) {
|
||||
const { worker } = require('./mocks/browser')
|
||||
worker.start({
|
||||
onUnhandledRequest: 'bypass'
|
||||
})
|
||||
import('./mocks/browser')
|
||||
.then(({ worker }) => {
|
||||
worker.start({
|
||||
onUnhandledRequest: 'bypass'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ export const user: User = {
|
||||
email: "mtg0987654321@gmail.com",
|
||||
avatar: "https://avatars.dicebear.com/api/bottts/Mtgmtg.svg",
|
||||
bio: "Lorem asiop asklh kluiw wekjhl shkj kljhsva klu khsc klhlkbs mjklwqr kmlk sadlfui mewr qiumnk, asdjomi cskhsdf.",
|
||||
name: "Mtg",
|
||||
name: "123123124asdfsadfsa8d7fsadfasdf",
|
||||
github: "MTG2000",
|
||||
jobTitle: "Front-end Web Developer",
|
||||
join_date: new Date(2021).toISOString(),
|
||||
|
||||
@@ -62,6 +62,8 @@ export function lazyModal<T extends ComponentType<any>>
|
||||
}
|
||||
|
||||
export function trimText(text: string | undefined | null, length: number) {
|
||||
console.log(text);
|
||||
|
||||
if (!text) return '';
|
||||
return text.slice(0, length) + (text.length > length ? "..." : "")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user