diff --git a/package.json b/package.json index 7d0e0faf..0a18ad76 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-popover": "^1.0.5", "@radix-ui/react-tabs": "^1.0.3", + "@radix-ui/react-tooltip": "^1.0.5", "@rehooks/local-storage": "^2.4.4", "@supabase/supabase-js": "^2.15.0", "@tauri-apps/api": "^1.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab3d07c4..f17b177c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,7 @@ specifiers: '@radix-ui/react-icons': ^1.3.0 '@radix-ui/react-popover': ^1.0.5 '@radix-ui/react-tabs': ^1.0.3 + '@radix-ui/react-tooltip': ^1.0.5 '@rehooks/local-storage': ^2.4.4 '@supabase/supabase-js': ^2.15.0 '@tailwindcss/typography': ^0.5.9 @@ -61,6 +62,7 @@ dependencies: '@radix-ui/react-icons': 1.3.0_react@18.2.0 '@radix-ui/react-popover': 1.0.5_zn3vyfk3tbnwebg5ldvieekjaq '@radix-ui/react-tabs': 1.0.3_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-tooltip': 1.0.5_zn3vyfk3tbnwebg5ldvieekjaq '@rehooks/local-storage': 2.4.4_react@18.2.0 '@supabase/supabase-js': 2.15.0 '@tauri-apps/api': 1.2.0 @@ -928,6 +930,32 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false + /@radix-ui/react-tooltip/1.0.5_zn3vyfk3tbnwebg5ldvieekjaq: + resolution: + { integrity: sha512-cDKVcfzyO6PpckZekODJZDe5ZxZ2fCZlzKzTmPhe4mX9qTHRfLcKgqb0OKf22xLwDequ2tVleim+ZYx3rabD5w== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-popper': 1.1.1_zn3vyfk3tbnwebg5ldvieekjaq + '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + '@radix-ui/react-visually-hidden': 1.0.2_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + /@radix-ui/react-use-callback-ref/1.0.0_react@18.2.0: resolution: { integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg== } @@ -992,6 +1020,19 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-visually-hidden/1.0.2_biqbaboplfbrettd7655fr4n2y: + resolution: + { integrity: sha512-qirnJxtYn73HEk1rXL12/mXnu2rwsNHDID10th2JGtdK25T9wX+mxRmGt7iPSahw512GbZOc0syZX1nLQGoEOg== } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + /@radix-ui/rect/1.0.0: resolution: { integrity: sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg== } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b957e8ea..261ef5f6 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -35,6 +35,7 @@ struct CreateAccountData { #[derive(Deserialize, Type)] struct GetPlebData { account_id: i32, + kind: i32, } #[derive(Deserialize, Type)] @@ -141,7 +142,10 @@ async fn create_account(db: DbState<'_>, data: CreateAccountData) -> Result, data: GetPlebData) -> Result, ()> { db.pleb() - .find_many(vec![pleb::account_id::equals(data.account_id)]) + .find_many(vec![ + pleb::account_id::equals(data.account_id), + pleb::kind::equals(data.kind), + ]) .exec() .await .map_err(|_| ()) diff --git a/src/assets/icons/hide.tsx b/src/assets/icons/hide.tsx new file mode 100644 index 00000000..edbcf916 --- /dev/null +++ b/src/assets/icons/hide.tsx @@ -0,0 +1,18 @@ +export default function HideIcon({ className }: { className: string }) { + return ( + + + + ); +} diff --git a/src/assets/icons/mute.tsx b/src/assets/icons/mute.tsx new file mode 100644 index 00000000..f1af1c0a --- /dev/null +++ b/src/assets/icons/mute.tsx @@ -0,0 +1,18 @@ +export default function MuteIcon({ className }: { className: string }) { + return ( + + + + ); +} diff --git a/src/assets/icons/reply.tsx b/src/assets/icons/reply.tsx new file mode 100644 index 00000000..a645e93a --- /dev/null +++ b/src/assets/icons/reply.tsx @@ -0,0 +1,18 @@ +export default function ReplyIcon({ className }: { className: string }) { + return ( + + + + ); +} diff --git a/src/components/channels/channelMessageItem.tsx b/src/components/channels/channelMessageItem.tsx deleted file mode 100644 index 3884bd7c..00000000 --- a/src/components/channels/channelMessageItem.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { MessageUser } from '@components/chats/messageUser'; - -import { memo } from 'react'; - -const ChannelMessageItem = ({ data }: { data: any }) => { - return ( -
-
- -
-
-
- {data.content} -
-
-
-
-
- ); -}; - -export default memo(ChannelMessageItem); diff --git a/src/components/channels/channelMessageList.tsx b/src/components/channels/messages/index.tsx similarity index 86% rename from src/components/channels/channelMessageList.tsx rename to src/components/channels/messages/index.tsx index 4414fd50..bc18c4ac 100644 --- a/src/components/channels/channelMessageList.tsx +++ b/src/components/channels/messages/index.tsx @@ -1,9 +1,9 @@ -import ChannelMessageItem from '@components/channels/channelMessageItem'; +import ChannelMessageItem from '@components/channels/messages/item'; import { useCallback, useRef } from 'react'; import { Virtuoso } from 'react-virtuoso'; -export const ChannelMessageList = ({ data }: { data: any }) => { +export const ChannelMessages = ({ data }: { data: any }) => { const virtuosoRef = useRef(null); const itemContent: any = useCallback( diff --git a/src/components/channels/messages/item.tsx b/src/components/channels/messages/item.tsx new file mode 100644 index 00000000..b2654c50 --- /dev/null +++ b/src/components/channels/messages/item.tsx @@ -0,0 +1,37 @@ +import { ReplyButton } from '@components/channels/messages/replyButton'; +import { MessageUser } from '@components/chats/messageUser'; + +import HideIcon from '@assets/icons/hide'; +import MuteIcon from '@assets/icons/mute'; + +import { memo } from 'react'; + +const ChannelMessageItem = ({ data }: { data: any }) => { + return ( +
+
+ +
+
+
+ {data.content} +
+
+
+
+
+
+ + + +
+
+
+ ); +}; + +export default memo(ChannelMessageItem); diff --git a/src/components/channels/messages/replyButton.tsx b/src/components/channels/messages/replyButton.tsx new file mode 100644 index 00000000..9ce76a7d --- /dev/null +++ b/src/components/channels/messages/replyButton.tsx @@ -0,0 +1,38 @@ +import { channelReplyAtom } from '@stores/channel'; + +import ReplyIcon from '@assets/icons/reply'; + +import * as Tooltip from '@radix-ui/react-tooltip'; +import { useSetAtom } from 'jotai'; + +export const ReplyButton = ({ id, pubkey, content }: { id: string; pubkey: string; content: string }) => { + const setChannelReplyAtom = useSetAtom(channelReplyAtom); + + const createReply = () => { + setChannelReplyAtom({ id: id, pubkey: pubkey, content: content }); + }; + + return ( + + + + + + + + Reply + + + + + + ); +}; diff --git a/src/components/chats/chatModal.tsx b/src/components/chats/chatModal.tsx index 24239ea2..ae6b88b3 100644 --- a/src/components/chats/chatModal.tsx +++ b/src/components/chats/chatModal.tsx @@ -11,7 +11,7 @@ export const ChatModal = () => { const fetchPlebsByAccount = useCallback(async (id) => { const { getPlebs } = await import('@utils/bindings'); - return await getPlebs({ account_id: id }); + return await getPlebs({ account_id: id, kind: 0 }); }, []); useEffect(() => { diff --git a/src/components/form/channelMessage.tsx b/src/components/form/channelMessage.tsx index 668b8212..dd4db315 100644 --- a/src/components/form/channelMessage.tsx +++ b/src/components/form/channelMessage.tsx @@ -1,9 +1,15 @@ import ImagePicker from '@components/form/imagePicker'; import { RelayContext } from '@components/relaysProvider'; +import { UserMini } from '@components/user/mini'; + +import { channelReplyAtom } from '@stores/channel'; import { dateToUnix } from '@utils/getDate'; +import { Cross1Icon } from '@radix-ui/react-icons'; import useLocalStorage from '@rehooks/local-storage'; +import { useAtomValue } from 'jotai'; +import { useResetAtom } from 'jotai/utils'; import { getEventHash, signEvent } from 'nostr-tools'; import { useCallback, useContext, useState } from 'react'; @@ -13,13 +19,28 @@ export default function FormChannelMessage({ eventId }: { eventId: string | stri const [value, setValue] = useState(''); const [activeAccount]: any = useLocalStorage('activeAccount', {}); + const channelReply = useAtomValue(channelReplyAtom); + const resetChannelReply = useResetAtom(channelReplyAtom); + const submitEvent = useCallback(() => { + let tags; + + if (channelReply.id !== null) { + tags = [ + ['e', eventId, '', 'root'], + ['e', channelReply.id, '', 'reply'], + ['p', channelReply.pubkey, ''], + ]; + } else { + tags = [['e', eventId, '', 'root']]; + } + const event: any = { content: value, created_at: dateToUnix(), kind: 42, pubkey: activeAccount.pubkey, - tags: [['e', eventId, '', 'root']], + tags: tags, }; event.id = getEventHash(event); event.sig = signEvent(event, activeAccount.privkey); @@ -28,7 +49,19 @@ export default function FormChannelMessage({ eventId }: { eventId: string | stri pool.publish(event, relays); // reset state setValue(''); - }, [activeAccount.privkey, activeAccount.pubkey, eventId, value, pool, relays]); + // reset channel reply + resetChannelReply(); + }, [ + value, + channelReply.id, + channelReply.pubkey, + activeAccount.pubkey, + activeAccount.privkey, + eventId, + resetChannelReply, + pool, + relays, + ]); const handleEnterPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { @@ -37,18 +70,44 @@ export default function FormChannelMessage({ eventId }: { eventId: string | stri } }; + const stopReply = () => { + resetChannelReply(); + }; + return ( -
-
-