diff --git a/package.json b/package.json index ec66d971..f0119695 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@radix-ui/react-slot": "1.0.2", "@radix-ui/react-tabs": "1.0.4", "@radix-ui/react-toast": "1.1.5", + "@radix-ui/react-tooltip": "1.0.7", "@reduxjs/toolkit": "1.7.1", "@tanstack/react-query": "5.17.12", "@tanstack/react-table": "8.11.3", diff --git a/src/components/Comments/index.module.scss b/src/components/Comments/index.module.scss deleted file mode 100644 index 559f3292..00000000 --- a/src/components/Comments/index.module.scss +++ /dev/null @@ -1,37 +0,0 @@ -.repliesHeader { - display: flex; - padding: 16px 20px; - flex-direction: column; - align-items: flex-start; - gap: 20px; - height: auto; - - .header { - width: 100%; - display: flex; - justify-content: space-between; - - .title { - color: var(--neutral-09, #1e1e1e); - - /* Subheader1-bold */ - font-size: 16px; - font-family: Poppins, sans-serif; - font-weight: 600; - line-height: 24px; - } - - .tab { - font-size: 14px; - border-radius: 20px; - background: var(--hover, rgb(30 30 30 / 4%)); - display: flex; - padding: 2px 4px; - align-items: center; - } - } -} - -.subRepliesContainer { - padding-left: 75px; -} diff --git a/src/components/Comments/index.tsx b/src/components/Comments/index.tsx index 4a2c25ec..4b5c6a3b 100644 --- a/src/components/Comments/index.tsx +++ b/src/components/Comments/index.tsx @@ -1,4 +1,3 @@ -import { Divider, Segmented, Tag } from 'antd'; import ReplyEventInput from 'components/ReplyNoteInput'; import { EventWithSeen } from 'pages/type'; import { useCallWorker } from 'hooks/useWorker'; @@ -12,9 +11,8 @@ import { getEventIdsFromETags } from 'core/nostr/util'; import { CallRelayType } from 'core/worker/type'; import dynamic from 'next/dynamic'; -import styles from './index.module.scss'; import PostItems from 'components/PostItems'; -import classNames from 'classnames'; +import Segmented from 'components/shared/ui/Segmented'; const SubPostUI = dynamic( async () => { @@ -148,13 +146,14 @@ const Comments: React.FC = ({ rootEvent, className }) => { }; return ( -
-
-
-
Replies{`(${commentList.length})`}
+
+
+
+
+ Replies{`(${commentList.length})`} +
setCommentOrder(val as string)} value={commentOrder} @@ -185,7 +184,7 @@ const Comments: React.FC = ({ rootEvent, className }) => { /> {n.children.length > 0 && ( <> -
+
{n.children.map(c => ( = ({ rootEvent, className }) => { ))} {commentOrder !== 'recent' && ( - - Feature Under Construction 🚧 - +
+ Feature Under Construction 🚧 +
)}
); diff --git a/src/components/CopyText/CopyText.tsx b/src/components/CopyText/CopyText.tsx index 3295f136..3c21b98b 100644 --- a/src/components/CopyText/CopyText.tsx +++ b/src/components/CopyText/CopyText.tsx @@ -1,4 +1,4 @@ -import { message } from 'antd'; +import { useToast } from 'components/shared/ui/Toast/use-toast'; export interface CopyTextProps { name: string; @@ -15,8 +15,8 @@ export const CopyText = ({ getTextToCopy, successMsg, failedMsg, - alertLastSecs = 5, }: CopyTextProps) => { + const { toast } = useToast(); const copy = async (text: string) => { const inputElement = document.createElement('input'); inputElement.value = text; @@ -53,17 +53,17 @@ export const CopyText = ({ await copy(text); } catch (error: any) { console.error('copy failed: ', error.message); - message.error( - failedMsg || 'Failed to copied to clipboard!', - alertLastSecs, - ); + toast({ + title: failedMsg || 'Failed to copied to clipboard!', + status: 'error', + }); return; } - message.success( - successMsg || 'Text copied to clipboard!', - alertLastSecs, - ); + toast({ + title: successMsg || 'Text copied to clipboard!', + status: 'success', + }); }} > {name} diff --git a/src/components/PostItems/PostArticleComment/index.tsx b/src/components/PostItems/PostArticleComment/index.tsx index c66cafd8..e0505c09 100644 --- a/src/components/PostItems/PostArticleComment/index.tsx +++ b/src/components/PostItems/PostArticleComment/index.tsx @@ -1,6 +1,5 @@ import { Nip23 } from 'core/nip/23'; import { Event } from 'core/nostr/Event'; -import { Avatar } from 'antd'; import { useTranslation } from 'next-i18next'; import Icon from 'components/Icon'; @@ -23,6 +22,7 @@ import { useLiveQuery } from 'dexie-react-hooks'; import { dbQuery, dexieDb } from 'core/db'; import { DbEvent } from 'core/db/schema'; import { seedRelays } from 'core/relay/pool/seed'; +import Avatar from 'components/shared/ui/Avatar'; interface PostArticleCommentProps { event: Event; diff --git a/src/components/PostItems/PostContent/Embed/Nprofile.tsx b/src/components/PostItems/PostContent/Embed/Nprofile.tsx index 4bcbe806..af6c7bc9 100644 --- a/src/components/PostItems/PostContent/Embed/Nprofile.tsx +++ b/src/components/PostItems/PostContent/Embed/Nprofile.tsx @@ -1,10 +1,10 @@ -import { Avatar } from 'antd'; import { Paths } from 'constants/path'; import { i18n } from 'next-i18next'; import { shortifyPublicKey } from 'core/nostr/content'; import { NprofileResult } from 'core/nip/21'; import styles from './index.module.scss'; +import Avatar from 'components/shared/ui/Avatar'; export const Nprofile: React.FC<{ nprofile: NprofileResult }> = ({ nprofile, @@ -24,7 +24,12 @@ export const Nprofile: React.FC<{ nprofile: NprofileResult }> = ({
- @ + {' '} + @ {nprofile.profile.name || shortifyPublicKey(nprofile.decodedMetadata.pubkey)}
diff --git a/src/components/PostItems/PostContent/Embed/Npub.tsx b/src/components/PostItems/PostContent/Embed/Npub.tsx index 21725b60..971754ba 100644 --- a/src/components/PostItems/PostContent/Embed/Npub.tsx +++ b/src/components/PostItems/PostContent/Embed/Npub.tsx @@ -1,10 +1,10 @@ -import { Avatar } from 'antd'; import { Paths } from 'constants/path'; import { i18n } from 'next-i18next'; import { shortifyPublicKey } from 'core/nostr/content'; import { NpubResult } from 'core/nip/21'; import styles from './index.module.scss'; +import Avatar from 'components/shared/ui/Avatar'; export const Npub: React.FC<{ npub: NpubResult }> = ({ npub }) => { if (npub.profile) { @@ -21,8 +21,12 @@ export const Npub: React.FC<{ npub: NpubResult }> = ({ npub }) => {
- @ - {npub.profile.name || shortifyPublicKey(npub.pubkey)} + {' '} + @{npub.profile.name || shortifyPublicKey(npub.pubkey)}
{npub.profile.about}
diff --git a/src/components/PostItems/PostContent/Media/LightingInvoice.tsx b/src/components/PostItems/PostContent/Media/LightingInvoice.tsx index 60cf4c46..eb037dc2 100644 --- a/src/components/PostItems/PostContent/Media/LightingInvoice.tsx +++ b/src/components/PostItems/PostContent/Media/LightingInvoice.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react'; import { decode } from 'core/lighting/bolt11'; import { getParams, LNURLPayParams } from 'js-lnurl'; import { useTranslation } from 'next-i18next'; -import { Button } from 'antd'; import Icon from 'components/Icon'; +import { Button } from 'components/shared/ui/Button'; export function LightingInvoice({ url }: { url: string }) { const { t } = useTranslation(); diff --git a/src/components/PostItems/PostContent/index.tsx b/src/components/PostItems/PostContent/index.tsx index 7c41be7b..7f1f49a6 100644 --- a/src/components/PostItems/PostContent/index.tsx +++ b/src/components/PostItems/PostContent/index.tsx @@ -1,7 +1,6 @@ import { EventTags } from 'core/nostr/type'; import { Event } from 'core/nostr/Event'; import { useMemo, useState } from 'react'; -import { Button } from 'antd'; import { CallWorker } from 'core/worker/caller'; import { isNsfwEvent } from 'utils/validator'; import { renderContent } from './content'; @@ -13,6 +12,7 @@ import { DecodedNeventResult, Nip19, Nip19DataType } from 'core/nip/19'; import styles from './index.module.scss'; import dynamic from 'next/dynamic'; +import { Button } from 'components/shared/ui/Button'; const SubPostItem = dynamic( async () => { @@ -113,7 +113,7 @@ export const PostContent: React.FC = ({
diff --git a/src/components/PostItems/PostItem/ui.tsx b/src/components/PostItems/PostItem/ui.tsx index 6434f69e..24310362 100644 --- a/src/components/PostItems/PostItem/ui.tsx +++ b/src/components/PostItems/PostItem/ui.tsx @@ -2,7 +2,6 @@ import { Event } from 'core/nostr/Event'; import { CallWorker } from 'core/worker/caller'; import { toUnSeenEvent } from 'core/nostr/util'; import { PostCommunityHeader } from '../PostCommunityHeader'; -import { message } from 'antd'; import { DbEvent } from 'core/db/schema'; import { EventSetMetadataContent } from 'core/nostr/type'; import { cn } from 'utils/classnames'; @@ -43,7 +42,7 @@ export interface PostUIProp { showFromCommunity?: boolean; extraMenu?: { label: string; - onClick: (event: Event, msg: typeof message) => any; + onClick: (event: Event) => any; }[]; extraHeader?: React.ReactNode; } @@ -59,8 +58,9 @@ export const PostUI: React.FC = ({ }) => { const { toast } = useToast(); const menu = useMemo(() => { - const onBroadcastEvent = async (event: Event, msg: typeof message) => { - if (!worker) return msg.error('worker not found.'); + const onBroadcastEvent = async (event: Event) => { + if (!worker) + return toast({ title: 'worker not found.', status: 'error' }); const pubHandler = worker.pubEvent(event); noticePubEventResult(toast, worker.relays.length, pubHandler); }; diff --git a/src/components/PostItems/PostReactions/index.module.scss b/src/components/PostItems/PostReactions/index.module.scss deleted file mode 100644 index a7a6b3b0..00000000 --- a/src/components/PostItems/PostReactions/index.module.scss +++ /dev/null @@ -1,21 +0,0 @@ -.reactions{ - display: flex; - padding: 0; - margin: var(--basePadding3) 0; - list-style: none; - width: 100%; - justify-content: space-between; - - li{ - svg{ - width: 18px; - height: 18px; - cursor: pointer; - vertical-align: middle; - - path{ - fill: var(--neutral-06); - } - } - } -} diff --git a/src/components/PostItems/PostReactions/index.tsx b/src/components/PostItems/PostReactions/index.tsx index 26e216f0..aa7f3ab3 100644 --- a/src/components/PostItems/PostReactions/index.tsx +++ b/src/components/PostItems/PostReactions/index.tsx @@ -1,4 +1,3 @@ -import { Tooltip, message } from 'antd'; import { useTranslation } from 'next-i18next'; import { fetchPublicBookmarkListEvent } from './util'; import { useReadonlyMyPublicKey } from 'hooks/useMyPublicKey'; @@ -20,8 +19,8 @@ import { useRouter } from 'next/router'; import { dexieDb } from 'core/db'; import Icon from 'components/Icon'; -import styles from './index.module.scss'; import { useToast } from 'components/shared/ui/Toast/use-toast'; +import Tooltip from 'components/shared/ui/Tooltip'; interface PostReactionsProp { ownerEvent: Event; @@ -42,17 +41,20 @@ const PostReactions: React.FC = ({ const signEvent = useSelector( (state: RootState) => state.loginReducer.signEvent, ); - const [messageApi, contextHolder] = message.useMessage(); const [isBookmarking, setIsBookMarking] = useState(false); const repost = async () => { if (signEvent == null) return; if (seen == null || seen[0] == null) - return messageApi.error('repost required seen relay, not found!'); + return toast({ + title: 'repost required seen relay, not found!', + status: 'error', + }); if (ownerEvent.kind !== WellKnownEventKind.text_note) - return messageApi.error( - 'non kind-1 repost feature are not available now, WIP', - ); + return toast({ + title: 'non kind-1 repost feature are not available now, WIP', + status: 'error', + }); const rawEvent = Nip18.createRepost(ownerEvent, seen[0]); const event = await signEvent(rawEvent); @@ -105,9 +107,10 @@ const PostReactions: React.FC = ({ if (data.pr) { sendPaymentInWebLn(data.pr); } else { - messageApi.error( - `something seems wrong with the zap endpoint response data`, - ); + toast({ + title: `something seems wrong with the zap endpoint response data`, + status: 'error', + }); console.debug(`invalid zapEndpoint response data`, data); } }; @@ -119,7 +122,8 @@ const PostReactions: React.FC = ({ const bookmark = async () => { if (signEvent == null) return; if (!worker == null) return; - if (isBookmarking) return messageApi.error('already execute bookmarking..'); + if (isBookmarking) + return toast({ title: 'already execute bookmarking..', status: 'error' }); setIsBookMarking(true); @@ -139,38 +143,45 @@ const PostReactions: React.FC = ({ }; return ( -
    - {contextHolder} -
  • - - - -
  • -
  • - - - -
  • -
  • - +
    + +
    + +
    +
    + +
    + +
    +
    + +
    - -
  • -
  • - +
+ + +
- - - +
+
+
); }; diff --git a/src/components/PostItems/PostRepost/index.tsx b/src/components/PostItems/PostRepost/index.tsx index 12f76fa6..840f0355 100644 --- a/src/components/PostItems/PostRepost/index.tsx +++ b/src/components/PostItems/PostRepost/index.tsx @@ -5,7 +5,6 @@ import { EventWithSeen } from 'pages/type'; import { CallWorker } from 'core/worker/caller'; import { PostContent } from '../PostContent'; import { Paths } from 'constants/path'; -import { Button } from 'antd'; import { deserializeMetadata, shortifyEventId } from 'core/nostr/content'; import { useLiveQuery } from 'dexie-react-hooks'; import { dbQuery, dexieDb } from 'core/db'; @@ -15,6 +14,7 @@ import { PostUI } from '../PostItem/ui'; import styles from '../index.module.scss'; import Link from 'next/link'; import Icon from 'components/Icon'; +import { Button } from 'components/shared/ui/Button'; export interface PostRepostProp { event: Event; @@ -133,7 +133,7 @@ const PostRepost: React.FC = ({ ? shortifyEventId(repostTargetEventIdFromETag) : 'unkonw'} -
diff --git a/src/components/PostItems/PostUser/index.tsx b/src/components/PostItems/PostUser/index.tsx index c6875141..e49a92f2 100644 --- a/src/components/PostItems/PostUser/index.tsx +++ b/src/components/PostItems/PostUser/index.tsx @@ -1,4 +1,3 @@ -import { message } from 'antd'; import { Paths } from 'constants/path'; import { useTimeSince } from 'hooks/useTimeSince'; import { EventWithSeen } from 'pages/type'; @@ -7,8 +6,8 @@ import { useEffect, useState } from 'react'; import { isNip05DomainName } from 'core/nip/05'; import { PostUserMenu } from './menu'; import { EventSetMetadataContent } from 'core/nostr/type'; -import * as Avatar from '@radix-ui/react-avatar'; import Link from 'next/link'; +import AvatarProfile from 'components/shared/ui/Avatar'; interface PostUserProps { publicKey: string; @@ -16,7 +15,7 @@ interface PostUserProps { event: EventWithSeen; extraMenu?: { label: string; - onClick: (event: Event, msg: typeof message) => any; + onClick: (event: Event) => any; }[]; } @@ -45,16 +44,11 @@ const PostUser: React.FC = ({
- - - - {name.slice(0, 2)} - - +
{ - Modal.success({ - title: 'Seen on Relays', - content: event.seen?.map(r =>

{r}

), - }); + alert(`event seen on relays: ${JSON.stringify(event.seen, null, 2)}`); }, + // todo: use modal }, ]; @@ -81,18 +107,37 @@ export function PostUserMenu({ event, publicKey, extraMenu }) { label: option.label, key: (items.length + 1).toString(), onClick: () => { - option.onClick(event, message); + option.onClick(event); }, }); } } return ( - - - + + + + + + {items.map(item => { + if (item.type === 'divider') { + return
; + } + + return ( + + {item.label} + + ); + })} +
+
); } diff --git a/src/components/PostItems/index.tsx b/src/components/PostItems/index.tsx index 208eb3fe..cac353ff 100644 --- a/src/components/PostItems/index.tsx +++ b/src/components/PostItems/index.tsx @@ -1,6 +1,5 @@ import { Event } from 'core/nostr/Event'; import { CallWorker } from 'core/worker/caller'; -import { message } from 'antd'; import { useLiveQuery } from 'dexie-react-hooks'; import { dexieDb } from 'core/db'; import { DbEvent } from 'core/db/schema'; @@ -25,7 +24,7 @@ interface PostItemsProps { showFromCommunity?: boolean; extraMenu?: { label: string; - onClick: (event: Event, msg: typeof message) => any; + onClick: (event: Event) => any; }[]; extraHeader?: React.ReactNode; truncate?: boolean; diff --git a/src/components/PubNoteTextarea/index.tsx b/src/components/PubNoteTextarea/index.tsx index 01b5d8b2..115769d0 100644 --- a/src/components/PubNoteTextarea/index.tsx +++ b/src/components/PubNoteTextarea/index.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'next-i18next'; import { FormEvent, useEffect, useRef, useState } from 'react'; import { LoginMode, SignEvent } from 'store/loginReducer'; import { useReadonlyMyPublicKey } from 'hooks/useMyPublicKey'; -import { Button, Mentions, Popover, Select, Tooltip } from 'antd'; +import { Mentions, Popover, Select, Tooltip } from 'antd'; import { handleFileSelect, handleSubmitText } from './util'; import { IMentions, useLoadCommunities, useSetMentions } from './hooks'; import { useToast } from 'components/shared/ui/Toast/use-toast'; @@ -21,6 +21,7 @@ import { Naddr } from 'core/nostr/type'; import { maxStrings } from 'utils/common'; import dynamic from 'next/dynamic'; import { noticePubEventResult } from 'components/PubEventNotice'; +import { Button } from 'components/shared/ui/Button'; const Picker = dynamic(() => import('@emoji-mart/react'), { ssr: false, @@ -38,7 +39,7 @@ interface Props { export const SubmitButton = ({ disabled }: { disabled: boolean }) => { const { t } = useTranslation(); return ( - ); diff --git a/src/components/ReplyNoteInput/index.module.scss b/src/components/ReplyNoteInput/index.module.scss deleted file mode 100644 index 020fb38e..00000000 --- a/src/components/ReplyNoteInput/index.module.scss +++ /dev/null @@ -1,118 +0,0 @@ -.replyBox { - width: 100%; - - .replyInput { - width: 100%; - display: flex; - justify-content: flex-start; - gap: 12px; - - .input { - display: flex; - padding: 8px 12px; - align-items: center; - gap: 4px; - flex: 1 0 0; - font-size: 14px; - font-family: 'Noto Sans', sans-serif; - line-height: 24px; - border-radius: 8px; - background: var(--neutral-white, #fff); - color: var(--neutral-06, #787878); - - &.focus { - background: var(--neutral-white); - border: 1px solid var(--primary-06); - - /* halo */ - box-shadow: 0 0 2px 1px rgb(89 128 34 / 16%); - } - } - } - - .btn { - height: 0; - transition: height 0.3s ease; - overflow: hidden; - margin-left: 50px; - - &.focus { - height: 45px; - } - - .container { - padding-top: 10px; - display: flex; - justify-content: space-between; - align-items: center; - height: inherit; - - .icons { - input { - display: none; - } - - .upload, - .emoji { - width: 24px; - height: 24px; - margin-right: 20px; - fill: var(--primary-06); - vertical-align: middle; - } - - .upload { - cursor: pointer; - } - - > a { - display: inline-block; - - svg { - vertical-align: middle; - } - } - } - - > button { - height: 24px; - padding: 0 var(--basePadding4); - } - } - } - - .imgs { - padding: 16px 0 0; - margin-left: 50px; - - .imgItem { - display: inline-block; - margin-right: 16px; - margin-bottom: 10px; - vertical-align: bottom; - line-height: 0; - position: relative; - - img { - font-size: 0; - width: 40px; - height: 40px; - border: 1px solid var(--neutral-03); - padding: 1px; - border-radius: var(--baseBorderRadius4); - } - - svg { - width: 16px; - height: 16px; - background-color: var(--neutral-03); - border-radius: 50%; - position: absolute; - right: -8px; - top: -8px; - z-index: 1; - cursor: pointer; - } - } - } -} diff --git a/src/components/ReplyNoteInput/index.tsx b/src/components/ReplyNoteInput/index.tsx index 16751170..5a1cb1a3 100644 --- a/src/components/ReplyNoteInput/index.tsx +++ b/src/components/ReplyNoteInput/index.tsx @@ -1,6 +1,6 @@ -import { Avatar, Button, Input, Popover, Tooltip } from 'antd'; +import { Popover } from 'antd'; import { useReadonlyMyPublicKey } from 'hooks/useMyPublicKey'; -import { EventSetMetadataContent, UserMap } from 'core/nostr/type'; +import { EventSetMetadataContent } from 'core/nostr/type'; import { connect, useSelector } from 'react-redux'; import { RootState } from 'store/configureStore'; import { @@ -18,14 +18,15 @@ import { handleFileSelect } from 'components/PubNoteTextarea/util'; import { useRouter } from 'next/router'; import { noticePubEventResult } from 'components/PubEventNotice'; -import classNames from 'classnames'; -import styles from './index.module.scss'; import Icon from 'components/Icon'; import Picker from '@emoji-mart/react'; import { Nip23 } from 'core/nip/23'; -import { dbQuery, dexieDb } from 'core/db'; -import { seedRelays } from 'core/relay/pool/seed'; +import { dexieDb } from 'core/db'; import { useToast } from 'components/shared/ui/Toast/use-toast'; +import { Button } from 'components/shared/ui/Button'; +import AvatarProfile from 'components/shared/ui/Avatar'; +import { isValidPublicKey } from 'utils/validator'; +import { Input } from 'components/shared/ui/Input'; export interface ReplyEventInputProp { replyTo: EventWithSeen; @@ -56,7 +57,7 @@ export const ReplyEventInput: React.FC = ({ useState(); const loadUserProfile = async () => { - if (myPublicKey) return; + if (!isValidPublicKey(myPublicKey)) return; // todo: set relay urls with correct one const profileEvent = await dexieDb.profileEvent.get(myPublicKey); @@ -119,36 +120,44 @@ export const ReplyEventInput: React.FC = ({ const SubmitButton = ({ disabled }: { disabled: boolean }) => { const { t } = useTranslation(); return ( - ); }; return ( -
-
+
+
-
setInputText(e.target.value)} + onChange={e => setInputText(e.currentTarget.value)} placeholder="write your comment" onFocus={() => setIsInputFocus(true)} + size={'large'} />
{attachImgs.length > 0 && ( -
+
{attachImgs.map((url, key) => ( -
- img +
+ img @@ -160,22 +169,21 @@ export const ReplyEventInput: React.FC = ({
)}
-
-
- - - fileInputRef.current && fileInputRef.current.click() - } - className={styles.upload} - /> - +
+
+ + fileInputRef.current && fileInputRef.current.click() + } + className="cursor-pointer w-[24px] h-[24px] mr-[20px] fill-primary-600 align-middle" + /> @@ -199,9 +207,10 @@ export const ReplyEventInput: React.FC = ({ /> } > - - - +
= ({
)} diff --git a/src/components/shared/ui/Avatar/index.tsx b/src/components/shared/ui/Avatar/index.tsx new file mode 100644 index 00000000..961caff5 --- /dev/null +++ b/src/components/shared/ui/Avatar/index.tsx @@ -0,0 +1,22 @@ +import * as Avatar from '@radix-ui/react-avatar'; + +function AvatarProfile({ + src, + alt, + fallback, +}: { + src?: string; + alt?: string; + fallback?: string; +}) { + return ( + + + + {fallback} + + + ); +} + +export default AvatarProfile; diff --git a/src/components/shared/ui/Dialog/useDialog.tsx b/src/components/shared/ui/Dialog/useDialog.tsx new file mode 100644 index 00000000..15bae10b --- /dev/null +++ b/src/components/shared/ui/Dialog/useDialog.tsx @@ -0,0 +1 @@ +export function useDialog() {} diff --git a/src/components/shared/ui/Drawer/index.stories.tsx b/src/components/shared/ui/Drawer/index.stories.tsx index 7fc2b3b0..3df1b3dc 100644 --- a/src/components/shared/ui/Drawer/index.stories.tsx +++ b/src/components/shared/ui/Drawer/index.stories.tsx @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { Button } from 'antd'; import { Drawer, DrawerClose, @@ -10,6 +9,7 @@ import { DrawerTitle, DrawerTrigger, } from '.'; +import { Button } from '../Button'; const meta: Meta = { component: Drawer, diff --git a/src/components/shared/ui/Segmented/index.tsx b/src/components/shared/ui/Segmented/index.tsx new file mode 100644 index 00000000..a11da4aa --- /dev/null +++ b/src/components/shared/ui/Segmented/index.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import * as ToggleGroup from '@radix-ui/react-toggle-group'; +import { cn } from 'utils/classnames'; +import { capitalizeString } from 'utils/str'; + +export interface SegmentedProp { + value: string; + options: Array; + onChange: (val: string) => any; + className?: string; +} + +const Segmented: React.FC = ({ + value, + options, + onChange, + className, +}) => { + return ( + + {options.map(opt => { + const val = typeof opt === 'string' ? opt : opt.value; + const label = typeof opt === 'string' ? opt : opt.label; + const isFocus = val === value; + const focusClass = + 'relative text-center cursor-pointer transition-colors duration-200 ease-in-out bg-neutral-100 text-neutral-900'; + return ( + + {capitalizeString(label)} + + ); + })} + + ); +}; + +export default Segmented; diff --git a/src/components/shared/ui/Tooltip/index.stories.tsx b/src/components/shared/ui/Tooltip/index.stories.tsx new file mode 100644 index 00000000..e674ce55 --- /dev/null +++ b/src/components/shared/ui/Tooltip/index.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import Tooltip from '.'; + +const meta: Meta = { + component: Tooltip, +}; + +export default meta; +type Story = StoryObj; + +export const Primary: Story = { + render: () => ( +
+ +
hover it
+
+
+ ), +}; diff --git a/src/components/shared/ui/Tooltip/index.tsx b/src/components/shared/ui/Tooltip/index.tsx new file mode 100644 index 00000000..3ae04591 --- /dev/null +++ b/src/components/shared/ui/Tooltip/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import * as PrimitiveTooltip from '@radix-ui/react-tooltip'; +import { Side } from '@radix-ui/react-popper'; +import { capitalizeString } from 'utils/str'; + +export interface TooltipProp { + children: React.ReactNode; + title: string; + placement?: Side; +} + +const Tooltip: React.FC = ({ children, title, placement }) => { + return ( + + + {children} + + + {capitalizeString(title)} + + + + + + ); +}; + +export default Tooltip; diff --git a/src/pages/bookmark/index.page.tsx b/src/pages/bookmark/index.page.tsx index 914e1223..523bd165 100644 --- a/src/pages/bookmark/index.page.tsx +++ b/src/pages/bookmark/index.page.tsx @@ -1,9 +1,8 @@ import { BaseLayout, Left, Right } from 'components/BaseLayout'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { useTranslation } from 'react-i18next'; -import { Empty, Select } from 'antd'; import { useCallWorker } from 'hooks/useWorker'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { EventMap, UserMap } from 'core/nostr/type'; import { useBookmarkListFeed } from './hooks/useBookmarkListFeed'; import { FilterOptions } from './filterOptions'; @@ -11,16 +10,24 @@ import PostItems from 'components/PostItems'; import { useLastReplyEvent } from './hooks/useLastReplyEvent'; import PageTitle from 'components/PageTitle'; import styles from './index.module.scss'; +import Segmented from 'components/shared/ui/Segmented'; const Bookmark = () => { const { t } = useTranslation(); + const options = useMemo(() => { + return FilterOptions.map(opt => { + return { + value: opt.value, + label: opt.name, + }; + }); + }, [FilterOptions]); + const { worker, newConn } = useCallWorker(); const [userMap, setUserMap] = useState(new Map()); const [eventMap, setEventMap] = useState(new Map()); - const [selectedValue, setSelectedValue] = useState( - FilterOptions[0].value, - ); + const [selectedValue, setSelectedValue] = useState(options[0].value); const handleSelectChange = (value: string) => { setSelectedValue(value); @@ -50,7 +57,7 @@ const Bookmark = () => { return selectFeed.length > 0 ? ( ) : ( - +
No data
); }; @@ -60,9 +67,8 @@ const Bookmark = () => {
- setReadonlyInputValue(event.target.value)} + onChange={event => setReadonlyInputValue(event.currentTarget.value)} /> -
- - + +
@@ -279,21 +296,25 @@ const LoginCard = ({ isLoggedIn, doLogin }: LoginFormProps) => { )}
setPrivKeyInputValue(event.target.value)} + onChange={event => + setPrivKeyInputValue(event.currentTarget.value) + } /> -
-
@@ -304,9 +325,7 @@ const LoginCard = ({ isLoggedIn, doLogin }: LoginFormProps) => { ); const alreadyLoggedIn = ( -
- {contextHolder}you are already sign-in! -
+
you are already sign-in!
); return isLoggedIn ? alreadyLoggedIn : notLoggedInUI; @@ -329,7 +348,7 @@ const mapDispatchToProps = dispatch => ({ dispatch(login(request)) .then(() => (window.location.href = Paths.home)) .catch(error => { - message.error(error.message, 5); + alert(error.message); }), doLogout: () => dispatch(logout()), }); diff --git a/src/pages/notification/index.page.tsx b/src/pages/notification/index.page.tsx index 1ce5cb2d..60ed7110 100644 --- a/src/pages/notification/index.page.tsx +++ b/src/pages/notification/index.page.tsx @@ -13,7 +13,7 @@ import { deserializeMetadata, shortifyNPub } from 'core/nostr/content'; import { EventSetMetadataContent, WellKnownEventKind } from 'core/nostr/type'; import { Event } from 'core/nostr/Event'; import { useSubLastReplyEvent } from 'hooks/useSubLastReplyEvent'; -import { Avatar, Badge, Button, Empty, List, Tabs, message } from 'antd'; +import { Badge, Empty, List, Tabs } from 'antd'; import { timeSince } from 'utils/time'; import { notifyKinds } from './kinds'; import { Nip172 } from 'core/nip/172'; @@ -31,6 +31,8 @@ import Link from 'next/link'; import styles from './index.module.scss'; import { useRouter } from 'next/router'; import { useToast } from 'components/shared/ui/Toast/use-toast'; +import Avatar from 'components/shared/ui/Avatar'; +import { Button } from 'components/shared/ui/Button'; export interface ItemProps { msg: Event; @@ -324,7 +326,7 @@ export function Notification({ isLoggedIn }: { isLoggedIn: boolean }) { }; const markAll = ( - ); @@ -464,9 +466,14 @@ export function Notification({ isLoggedIn }: { isLoggedIn: boolean }) { msg.tags.filter(t => Nip172.isCommunityATag(t))[0][1], ); const isAlreadyApproved = approveByMe.includes(msg.id); - const createApproval = async (postEvent: Event, message) => { - if (!worker) return message.error('worker not found'); - if (!signEvent) return message.errpr('signEvent method not found'); + const createApproval = async (postEvent: Event) => { + if (!worker) + return toast({ title: 'worker not found', status: 'error' }); + if (!signEvent) + return toast({ + title: 'signEvent method not found', + status: 'error', + }); const rawEvent = Nip172.createApprovePostRawEvent( postEvent, @@ -502,8 +509,7 @@ export function Notification({ isLoggedIn }: { isLoggedIn: boolean }) {
diff --git a/src/pages/post/[publicKey]/PostContent/index.tsx b/src/pages/post/[publicKey]/PostContent/index.tsx index ac26a565..f8b5aa71 100644 --- a/src/pages/post/[publicKey]/PostContent/index.tsx +++ b/src/pages/post/[publicKey]/PostContent/index.tsx @@ -5,8 +5,9 @@ import { useReadonlyMyPublicKey } from 'hooks/useMyPublicKey'; import Link from 'next/link'; import styles from './index.module.scss'; import ReactMarkdown from 'react-markdown'; -import { Avatar, Button } from 'antd'; import { isValidPublicKey } from 'utils/validator'; +import { Button } from 'components/shared/ui/Button'; +import Avatar from 'components/shared/ui/Avatar'; const PostContent = ({ article, publicKey, userProfile, articleId, t }) => { const myPublicKey = useReadonlyMyPublicKey(); @@ -31,7 +32,7 @@ const PostContent = ({ article, publicKey, userProfile, articleId, t }) => {
diff --git a/src/pages/post/[publicKey]/[articleId].page.tsx b/src/pages/post/[publicKey]/[articleId].page.tsx index 72ae6514..71e9d3e5 100644 --- a/src/pages/post/[publicKey]/[articleId].page.tsx +++ b/src/pages/post/[publicKey]/[articleId].page.tsx @@ -16,7 +16,6 @@ import Link from 'next/link'; import { Paths } from 'constants/path'; import { payLnUrlInWebLn } from 'core/lighting/lighting'; import Icon from 'components/Icon'; -import { Button, Input } from 'antd'; import Comments from 'components/Comments'; import PageTitle from 'components/PageTitle'; import { usePubkeyFromRouterQuery } from 'hooks/usePubkeyFromRouterQuery'; @@ -26,6 +25,7 @@ import { isValidPublicKey } from 'utils/validator'; import { dbQuery, profileQuery } from 'core/db'; import { deserializeMetadata } from 'core/nostr/content'; import { Event } from 'core/nostr/Event'; +import { Button } from 'components/shared/ui/Button'; type UserParams = { publicKey: string; diff --git a/src/pages/relay/index.page.tsx b/src/pages/relay/index.page.tsx index 38bcf083..33cdee51 100644 --- a/src/pages/relay/index.page.tsx +++ b/src/pages/relay/index.page.tsx @@ -1,6 +1,5 @@ import { Avatar, - Button, Card, Carousel, Divider, @@ -36,6 +35,7 @@ import Link from 'next/link'; import { Nip172 } from 'core/nip/172'; import { kindToReadable } from 'core/nostr/util'; import Icon from 'components/Icon'; +import { Button } from 'components/shared/ui/Button'; export function RelayPage() { const router = useRouter(); @@ -246,11 +246,10 @@ export function RelayPage() {
diff --git a/src/pages/setting/index.page.tsx b/src/pages/setting/index.page.tsx index b046f7ec..c600ba2a 100644 --- a/src/pages/setting/index.page.tsx +++ b/src/pages/setting/index.page.tsx @@ -15,7 +15,7 @@ import { BaseLayout, Left, Right } from 'components/BaseLayout'; import { loginMapStateToProps } from 'pages/helper'; import { useReadonlyMyPublicKey } from 'hooks/useMyPublicKey'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; -import { Avatar, Button, Form, Input, Tabs } from 'antd'; +import { Form, Input, Tabs } from 'antd'; import { noticePubEventResult } from 'components/PubEventNotice'; import { useRouter } from 'next/router'; import { useToast } from 'components/shared/ui/Toast/use-toast'; @@ -24,6 +24,8 @@ import dynamic from 'next/dynamic'; import PageTitle from 'components/PageTitle'; import styles from './index.module.scss'; import Key from './key'; +import { Button } from 'components/shared/ui/Button'; +import Avatar from 'components/shared/ui/Avatar'; const Preference = dynamic(() => import('./preference'), { ssr: false, @@ -157,12 +159,9 @@ export const EditProfilePage = ({ commitId }) => { >
- + {avatar && ( - )} @@ -190,7 +189,7 @@ export const EditProfilePage = ({ commitId }) => {
{banner && ( - )} @@ -228,9 +227,7 @@ export const EditProfilePage = ({ commitId }) => { - +
diff --git a/src/pages/setting/key.tsx b/src/pages/setting/key.tsx index f6870671..fd8e33c3 100644 --- a/src/pages/setting/key.tsx +++ b/src/pages/setting/key.tsx @@ -1,16 +1,14 @@ -import { useVersion } from 'hooks/useVersion'; import { useTranslation } from 'react-i18next'; -import { Button, Divider, Input, List } from 'antd'; +import { Divider, Input, List } from 'antd'; import { connect } from 'react-redux'; import { useReadonlyMyPublicKey } from 'hooks/useMyPublicKey'; -import styles from './index.module.scss'; import { CopyText } from 'components/CopyText/CopyText'; import { Nip19, Nip19DataType } from 'core/nip/19'; import { LoginMode } from 'store/loginReducer'; import { getPrivateKeyFromMetamaskSignIn } from 'core/evm/metamask'; import { getPrivateKeyFromWalletConnectSignIn } from 'core/evm/walletConnect'; -import Link from 'next/link'; +import { Button } from 'components/shared/ui/Button'; function Key({ mode, isLoggedIn, privateKey, evmUsername }) { const { t } = useTranslation(); @@ -161,7 +159,7 @@ function Key({ mode, isLoggedIn, privateKey, evmUsername }) { diff --git a/src/pages/setting/preference.tsx b/src/pages/setting/preference.tsx index d9bde5c7..b53399ac 100644 --- a/src/pages/setting/preference.tsx +++ b/src/pages/setting/preference.tsx @@ -1,4 +1,4 @@ -import { Button, Divider, Input, List, Modal, Upload, message } from 'antd'; +import { Divider, Input, List, Modal, Upload, message } from 'antd'; import { dbEventTable, dexieDb } from 'core/db'; import { RelayGroupManager } from 'core/relay/group'; import { useReadonlyMyPublicKey } from 'hooks/useMyPublicKey'; @@ -8,6 +8,7 @@ import { useCallWorker } from 'hooks/useWorker'; import { useSelector } from 'react-redux'; import { RootState } from 'store/configureStore'; import { SmallLoader } from 'components/Loader'; +import { Button } from 'components/shared/ui/Button'; export default function Preference() { const myPublicKey = useReadonlyMyPublicKey(); diff --git a/src/pages/user/[publicKey].page.tsx b/src/pages/user/[publicKey].page.tsx index 344012f1..df0af20b 100644 --- a/src/pages/user/[publicKey].page.tsx +++ b/src/pages/user/[publicKey].page.tsx @@ -18,15 +18,7 @@ import { ContactInfo, } from 'core/nostr/type'; import { RawEvent } from 'core/nostr/RawEvent'; -import { - Avatar, - Button, - Dropdown, - Input, - MenuProps, - Tooltip, - message, -} from 'antd'; +import { Avatar, Dropdown, Input, MenuProps, Tooltip, message } from 'antd'; import { copyToClipboard } from 'utils/common'; import { Followings } from './followings'; @@ -53,6 +45,7 @@ import { Nip19, Nip19DataType } from 'core/nip/19'; import { TimelineTabs } from 'components/TimelineTabs'; import { defaultProfileTimelineFilters } from 'core/timeline-filter'; import { useToast } from 'components/shared/ui/Toast/use-toast'; +import { Button } from 'components/shared/ui/Button'; type UserParams = { publicKey: PublicKey; @@ -245,7 +238,7 @@ export const ProfilePage = ({ isLoggedIn, signEvent }) => {
) : (
- @@ -324,7 +317,7 @@ export const ProfilePage = ({ isLoggedIn, signEvent }) => { }} /> -
diff --git a/src/pages/user/followings.tsx b/src/pages/user/followings.tsx index b48ae244..19549ae4 100644 --- a/src/pages/user/followings.tsx +++ b/src/pages/user/followings.tsx @@ -1,14 +1,5 @@ import { EventSetMetadataContent, PublicKey } from 'core/nostr/type'; -import { - Alert, - Avatar, - Button, - Dropdown, - List, - MenuProps, - Modal, - message, -} from 'antd'; +import { Avatar, Dropdown, List, MenuProps, Modal, message } from 'antd'; import { Paths } from 'constants/path'; import { copyToClipboard } from 'utils/common'; import { useRouter } from 'next/router'; @@ -20,6 +11,7 @@ import { CallWorker } from 'core/worker/caller'; import Icon from 'components/Icon'; import styles from './index.module.scss'; import Link from 'next/link'; +import { Button } from 'components/shared/ui/Button'; export interface FollowingsProp { buildFollowUnfollow: (publicKey: string) => { @@ -147,7 +139,7 @@ export const Followings: React.FC = ({ ); })}
-
diff --git a/src/utils/str.ts b/src/utils/str.ts new file mode 100644 index 00000000..4cb01ba1 --- /dev/null +++ b/src/utils/str.ts @@ -0,0 +1,3 @@ +export function capitalizeString(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); +} diff --git a/yarn.lock b/yarn.lock index 2fbeb16a..33eea131 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3842,6 +3842,25 @@ "@radix-ui/react-separator" "1.0.3" "@radix-ui/react-toggle-group" "1.0.4" +"@radix-ui/react-tooltip@1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e" + integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-popper" "1.1.3" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-visually-hidden" "1.0.3" + "@radix-ui/react-use-callback-ref@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"