From 22d2858c1cc9c65f4b7e6c8b9ba6dd50315365ed Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Tue, 12 Dec 2023 00:00:44 +0900 Subject: [PATCH] refs #4653 Add emoji picker for emoji reactions --- renderer/components/compose/Compose.tsx | 7 ++- renderer/components/detail/Detail.tsx | 24 ++++++++-- renderer/components/detail/Profile.tsx | 4 +- renderer/components/detail/Reply.tsx | 11 ++++- renderer/components/detail/Thread.tsx | 11 ++++- .../components/detail/profile/Timeline.tsx | 3 ++ .../components/timelines/Notifications.tsx | 1 + renderer/components/timelines/Timeline.tsx | 3 +- .../timelines/notification/Notification.tsx | 12 ++++- .../components/timelines/status/Actions.tsx | 45 ++++++++++++++++++- .../components/timelines/status/Status.tsx | 4 +- 11 files changed, 112 insertions(+), 13 deletions(-) diff --git a/renderer/components/compose/Compose.tsx b/renderer/components/compose/Compose.tsx index 1eb9244d..118ca613 100644 --- a/renderer/components/compose/Compose.tsx +++ b/renderer/components/compose/Compose.tsx @@ -36,7 +36,7 @@ const customTheme: CustomFlowbiteTheme = { content: 'focus:outline-none', floating: { item: { - base: '' + base: 'hidden' } } } @@ -162,6 +162,8 @@ export default function Compose(props: Props) { } else if (emoji.shortcodes) { setBody(current => `${current.slice(0, cursor)}${emoji.shortcodes} ${current.slice(cursor)}`) } + const dummy = document.getElementById('dummy-emoji-picker') + dummy.click() } return ( @@ -197,8 +199,9 @@ export default function Compose(props: Props) { )} > + - + diff --git a/renderer/components/detail/Detail.tsx b/renderer/components/detail/Detail.tsx index c4d03548..8bddbf3d 100644 --- a/renderer/components/detail/Detail.tsx +++ b/renderer/components/detail/Detail.tsx @@ -5,9 +5,11 @@ import Thread from './Thread' import { Entity, MegalodonInterface } from 'megalodon' import Reply from './Reply' import Profile from './Profile' +import { Account } from '@/db' type Props = { client: MegalodonInterface + account: Account openMedia: (media: Entity.Attachment) => void } & HTMLAttributes @@ -43,11 +45,25 @@ export default function Detail(props: Props) { - {target === 'status' && } - {target === 'reply' && ( - + {target === 'status' && ( + + )} + {target === 'reply' && ( + + )} + {target === 'profile' && ( + )} - {target === 'profile' && } )} diff --git a/renderer/components/detail/Profile.tsx b/renderer/components/detail/Profile.tsx index 561e7e5a..122f0427 100644 --- a/renderer/components/detail/Profile.tsx +++ b/renderer/components/detail/Profile.tsx @@ -8,9 +8,11 @@ import Timeline from './profile/Timeline' import Followings from './profile/Followings' import Followers from './profile/Followers' import { findLink } from '@/utils/statusParser' +import { Account } from '@/db' type Props = { client: MegalodonInterface + account: Account user_id: string openMedia: (media: Entity.Attachment) => void } @@ -122,7 +124,7 @@ export default function Profile(props: Props) {
- + diff --git a/renderer/components/detail/Reply.tsx b/renderer/components/detail/Reply.tsx index 19143aa8..c8dd52ee 100644 --- a/renderer/components/detail/Reply.tsx +++ b/renderer/components/detail/Reply.tsx @@ -3,9 +3,11 @@ import { useEffect, useRef, useState } from 'react' import { Virtuoso } from 'react-virtuoso' import Status from '../timelines/status/Status' import Compose from '../compose/Compose' +import { Account } from '@/db' type Props = { client: MegalodonInterface + account: Account status_id: string openMedia: (media: Entity.Attachment) => void } @@ -49,7 +51,14 @@ export default function Reply(props: Props) { style={{ height: `calc(100% - ${composeHeight}px)` }} data={[...ancestors, status].filter(s => s !== null)} itemContent={(_, status) => ( - {}} openMedia={props.openMedia} /> + {}} + openMedia={props.openMedia} + /> )} />
diff --git a/renderer/components/detail/Thread.tsx b/renderer/components/detail/Thread.tsx index c28a7325..311bb8a5 100644 --- a/renderer/components/detail/Thread.tsx +++ b/renderer/components/detail/Thread.tsx @@ -2,9 +2,11 @@ import { Entity, MegalodonInterface } from 'megalodon' import { useEffect, useState } from 'react' import { Virtuoso } from 'react-virtuoso' import Status from '../timelines/status/Status' +import { Account } from '@/db' type Props = { client: MegalodonInterface + account: Account status_id: string openMedia: (media: Entity.Attachment) => void } @@ -33,7 +35,14 @@ export default function Thread(props: Props) { style={{ height: 'calc(100% - 50px)' }} data={[...ancestors, status, ...descendants].filter(s => s !== null)} itemContent={(_, status) => ( - {}} openMedia={props.openMedia} /> + {}} + openMedia={props.openMedia} + /> )} /> diff --git a/renderer/components/detail/profile/Timeline.tsx b/renderer/components/detail/profile/Timeline.tsx index 13bdad1e..46366ddb 100644 --- a/renderer/components/detail/profile/Timeline.tsx +++ b/renderer/components/detail/profile/Timeline.tsx @@ -1,9 +1,11 @@ import Status from '@/components/timelines/status/Status' +import { Account } from '@/db' import { Entity, MegalodonInterface } from 'megalodon' import { useEffect, useState } from 'react' type Props = { client: MegalodonInterface + account: Account user_id: string openMedia: (media: Entity.Attachment) => void } @@ -43,6 +45,7 @@ export default function Timeline(props: Props) { {statuses.map((status, index) => ( setStatuses(current => updateStatus(current, status))} diff --git a/renderer/components/timelines/Notifications.tsx b/renderer/components/timelines/Notifications.tsx index a3fa88d7..33de176b 100644 --- a/renderer/components/timelines/Notifications.tsx +++ b/renderer/components/timelines/Notifications.tsx @@ -125,6 +125,7 @@ export default function Notifications(props: Props) { itemContent={(_, notification) => ( ( setStatuses(current => updateStatus(current, status))} @@ -194,7 +195,7 @@ export default function Timeline(props: Props) {
- props.setAttachment(media)} /> + props.setAttachment(media)} /> ) } diff --git a/renderer/components/timelines/notification/Notification.tsx b/renderer/components/timelines/notification/Notification.tsx index 11e076dd..cc9a0ffc 100644 --- a/renderer/components/timelines/notification/Notification.tsx +++ b/renderer/components/timelines/notification/Notification.tsx @@ -2,9 +2,11 @@ import { Entity, MegalodonInterface } from 'megalodon' import Status from '../status/Status' import Reaction from './Reaction' import Follow from './Follow' +import { Account } from '@/db' type Props = { notification: Entity.Notification + account: Account client: MegalodonInterface onRefresh: (status: Entity.Status) => void openMedia: (media: Entity.Attachment) => void @@ -14,7 +16,15 @@ export default function Notification(props: Props) { switch (props.notification.type) { case 'mention': { if (props.notification.status) { - return + return ( + + ) } else { return null } diff --git a/renderer/components/timelines/status/Actions.tsx b/renderer/components/timelines/status/Actions.tsx index 5a2b79b4..397b9147 100644 --- a/renderer/components/timelines/status/Actions.tsx +++ b/renderer/components/timelines/status/Actions.tsx @@ -1,13 +1,29 @@ +import { CustomFlowbiteTheme, Dropdown, Flowbite } from 'flowbite-react' import { Entity, MegalodonInterface } from 'megalodon' import { useRouter } from 'next/router' -import { FaBookmark, FaEllipsis, FaReply, FaRetweet, FaStar } from 'react-icons/fa6' +import { FaBookmark, FaEllipsis, FaFaceLaughBeam, FaReply, FaRetweet, FaStar } from 'react-icons/fa6' +import Picker from '@emoji-mart/react' +import { data } from '@/utils/emojiData' +import { Account } from '@/db' type Props = { status: Entity.Status + account: Account client: MegalodonInterface onRefresh: () => void } +const customTheme: CustomFlowbiteTheme = { + dropdown: { + content: 'focus:outline-none', + floating: { + item: { + base: 'hidden' + } + } + } +} + export default function Actions(props: Props) { const router = useRouter() @@ -42,12 +58,39 @@ export default function Actions(props: Props) { props.onRefresh() } + const onEmojiSelect = async emoji => { + await props.client.createEmojiReaction(props.status.id, emoji.native) + const dummy = document.getElementById('dummy-emoji-picker') + dummy.click() + props.onRefresh() + } + return (
+ {props.account.sns !== 'mastodon' && ( + + ( + + + + )} + > + + + + + + + )} +
) diff --git a/renderer/components/timelines/status/Status.tsx b/renderer/components/timelines/status/Status.tsx index 9e2e8f3b..8b87e085 100644 --- a/renderer/components/timelines/status/Status.tsx +++ b/renderer/components/timelines/status/Status.tsx @@ -11,9 +11,11 @@ import Actions from './Actions' import { useRouter } from 'next/router' import { MouseEventHandler, useState } from 'react' import { findLink } from '@/utils/statusParser' +import { Account } from '@/db' type Props = { status: Entity.Status + account: Account client: MegalodonInterface onRefresh: (status: Entity.Status) => void openMedia: (media: Entity.Attachment) => void @@ -83,7 +85,7 @@ export default function Status(props: Props) { )} - +