Skip to content

Commit

Permalink
feat: merge actions for toggling state
Browse files Browse the repository at this point in the history
  • Loading branch information
hyoban committed Jan 20, 2025
1 parent f52338c commit fd1209a
Show file tree
Hide file tree
Showing 15 changed files with 98 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@ const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
icon?: React.ReactNode
icon?: React.ReactNode | ((props?: { isActive?: boolean }) => React.ReactNode)
active?: boolean
}
>(({ className, inset, icon, ...props }, ref) => (
>(({ className, inset, icon, active, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
Expand All @@ -109,7 +110,9 @@ const DropdownMenuItem = React.forwardRef<
{...props}
>
{!!icon && (
<span className="mr-1.5 inline-flex size-4 items-center justify-center">{icon}</span>
<span className="mr-1.5 inline-flex size-4 items-center justify-center">
{typeof icon === "function" ? icon({ isActive: active }) : icon}
</span>
)}
{props.children}
{/* Justify Fill */}
Expand Down
26 changes: 5 additions & 21 deletions apps/renderer/src/hooks/biz/useEntryActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,10 @@ export const useEntryActions = ({ entryId, view }: { entryId: string; view?: Fee
hide: isInbox || feed?.ownerUserId === whoami()?.id,
shortcut: shortcuts.entry.tip.key,
},
{
id: COMMAND_ID.entry.unstar,
onClick: runCmdFn(COMMAND_ID.entry.unstar, [{ entryId }]),
hide: !entry?.collections,
shortcut: shortcuts.entry.toggleStarred.key,
},
{
id: COMMAND_ID.entry.star,
onClick: runCmdFn(COMMAND_ID.entry.star, [{ entryId, view }]),
hide: !!entry?.collections,
active: !!entry?.collections,
shortcut: shortcuts.entry.toggleStarred.key,
},
{
Expand All @@ -159,13 +153,8 @@ export const useEntryActions = ({ entryId, view }: { entryId: string; view?: Fee
{
id: COMMAND_ID.entry.viewSourceContent,
onClick: runCmdFn(COMMAND_ID.entry.viewSourceContent, [{ entryId }]),
hide: isMobile() || isShowSourceContent || !entry?.entries.url,
},
{
id: COMMAND_ID.entry.viewEntryContent,
onClick: runCmdFn(COMMAND_ID.entry.viewEntryContent, []),
hide: !isShowSourceContent,
active: true,
hide: isMobile() || !entry?.entries.url,
active: isShowSourceContent,
},
{
id: COMMAND_ID.entry.toggleAISummary,
Expand Down Expand Up @@ -198,13 +187,8 @@ export const useEntryActions = ({ entryId, view }: { entryId: string; view?: Fee
{
id: COMMAND_ID.entry.read,
onClick: runCmdFn(COMMAND_ID.entry.read, [{ entryId }]),
hide: !hasEntry || entry.read || !!entry.collections || !!inList,
shortcut: shortcuts.entry.toggleRead.key,
},
{
id: COMMAND_ID.entry.unread,
onClick: runCmdFn(COMMAND_ID.entry.unread, [{ entryId }]),
hide: !hasEntry || !entry.read || !!entry.collections || !!inList,
hide: !hasEntry || !!entry.collections || !!inList,
active: !!entry?.read,
shortcut: shortcuts.entry.toggleRead.key,
},
{
Expand Down
108 changes: 48 additions & 60 deletions apps/renderer/src/modules/command/commands/entry.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { FeedViewType, UserRole } from "@follow/constants"
import { IN_ELECTRON } from "@follow/shared/constants"
import { getOS } from "@follow/utils/utils"
import { cn, getOS } from "@follow/utils/utils"
import { useMutation } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { toast } from "sonner"

import { toggleShowAISummary } from "~/atoms/ai-summary"
import { toggleShowAITranslation } from "~/atoms/ai-translation"
import { setShowSourceContent, useSourceContentModal } from "~/atoms/source-content"
import {
getShowSourceContent,
toggleShowSourceContent,
useSourceContentModal,
} from "~/atoms/source-content"
import { useUserRole } from "~/atoms/user"
import { navigateEntry } from "~/hooks/biz/useNavigateEntry"
import { getRouteParams } from "~/hooks/biz/useRouteParams"
Expand Down Expand Up @@ -112,7 +116,13 @@ export const useRegisterEntryCommands = () => {
{
id: COMMAND_ID.entry.star,
label: t("entry_actions.star"),
icon: <i className="i-mgc-star-cute-re" />,
icon: (props) => (
<i
className={cn(
props?.isActive ? "i-mgc-star-cute-fi text-orange-500" : "i-mgc-star-cute-re",
)}
/>
),
run: ({ entryId, view }) => {
const entry = useEntryStore.getState().flatMapEntries[entryId]
if (!entry) {
Expand All @@ -128,20 +138,11 @@ export const useRegisterEntryCommands = () => {
// width: 252,
// })
// }
collect.mutate({ entryId, view })
},
},
{
id: COMMAND_ID.entry.unstar,
label: t("entry_actions.unstar"),
icon: <i className="i-mgc-star-cute-fi text-orange-500" />,
run: ({ entryId }) => {
const entry = useEntryStore.getState().flatMapEntries[entryId]
if (!entry) {
toast.error("Failed to unstar: entry is not available", { duration: 3000 })
return
if (entry.collections) {
uncollect.mutate(entry.entries.id)
} else {
collect.mutate({ entryId, view })
}
uncollect.mutate(entry.entries.id)
},
},
{
Expand Down Expand Up @@ -211,37 +212,31 @@ export const useRegisterEntryCommands = () => {
label: t("entry_actions.view_source_content"),
icon: <i className="i-mgc-world-2-cute-re" />,
run: ({ entryId }) => {
const entry = useEntryStore.getState().flatMapEntries[entryId]
if (!entry || !entry.entries.url) {
toast.error("Failed to view source content: url is not available", { duration: 3000 })
return
}
const routeParams = getRouteParams()
const viewPreviewInModal = [
FeedViewType.SocialMedia,
FeedViewType.Videos,
FeedViewType.Pictures,
].includes(routeParams.view)
if (viewPreviewInModal) {
showSourceContentModal({
title: entry.entries.title ?? undefined,
src: entry.entries.url,
})
return
}
const layoutEntryId = routeParams.entryId
if (layoutEntryId !== entry.entries.id) {
navigateEntry({ entryId: entry.entries.id })
if (!getShowSourceContent()) {
const entry = useEntryStore.getState().flatMapEntries[entryId]
if (!entry || !entry.entries.url) {
toast.error("Failed to view source content: url is not available", { duration: 3000 })
return
}
const routeParams = getRouteParams()
const viewPreviewInModal = [
FeedViewType.SocialMedia,
FeedViewType.Videos,
FeedViewType.Pictures,
].includes(routeParams.view)
if (viewPreviewInModal) {
showSourceContentModal({
title: entry.entries.title ?? undefined,
src: entry.entries.url,
})
return
}
const layoutEntryId = routeParams.entryId
if (layoutEntryId !== entry.entries.id) {
navigateEntry({ entryId: entry.entries.id })
}
}
setShowSourceContent(true)
},
},
{
id: COMMAND_ID.entry.viewEntryContent,
label: t("entry_actions.view_source_content"),
icon: <i className="i-mgc-world-2-cute-fi" />,
run: () => {
setShowSourceContent(false)
toggleShowSourceContent()
},
},
{
Expand Down Expand Up @@ -274,27 +269,20 @@ export const useRegisterEntryCommands = () => {
{
id: COMMAND_ID.entry.read,
label: t("entry_actions.mark_as_read"),
icon: <i className="i-mgc-round-cute-fi" />,
icon: (props) => (
<i className={cn(props?.isActive ? "i-mgc-round-cute-re" : "i-mgc-round-cute-fi")} />
),
run: ({ entryId }) => {
const entry = useEntryStore.getState().flatMapEntries[entryId]
if (!entry) {
toast.error("Failed to mark as unread: feed is not available", { duration: 3000 })
return
}
read.mutate({ entryId, feedId: entry.feedId })
},
},
{
id: COMMAND_ID.entry.unread,
label: t("entry_actions.mark_as_unread"),
icon: <i className="i-mgc-round-cute-re" />,
run: ({ entryId }) => {
const entry = useEntryStore.getState().flatMapEntries[entryId]
if (!entry) {
toast.error("Failed to mark as unread: feed is not available", { duration: 3000 })
return
if (entry.read) {
unread.mutate({ entryId, feedId: entry.feedId })
} else {
read.mutate({ entryId, feedId: entry.feedId })
}
unread.mutate({ entryId, feedId: entry.feedId })
},
},
])
Expand Down
3 changes: 0 additions & 3 deletions apps/renderer/src/modules/command/commands/id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ export const COMMAND_ID = {
entry: {
tip: "entry:tip",
star: "entry:star",
unstar: "entry:unstar",
delete: "entry:delete",
copyLink: "entry:copy-link",
copyTitle: "entry:copy-title",
openInBrowser: "entry:open-in-browser",
viewSourceContent: "entry:view-source-content",
viewEntryContent: "entry:view-entry-content",
share: "entry:share",
read: "entry:read",
unread: "entry:unread",
toggleAISummary: "entry:toggle-ai-summary",
toggleAITranslation: "entry:toggle-ai-translation",
},
Expand Down
16 changes: 0 additions & 16 deletions apps/renderer/src/modules/command/commands/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ export type StarCommand = Command<{
id: typeof COMMAND_ID.entry.star
fn: (data: { entryId: string; view?: FeedViewType }) => void
}>
export type UnStarCommand = Command<{
id: typeof COMMAND_ID.entry.unstar
fn: (data: { entryId: string }) => void
}>

export type DeleteCommand = Command<{
id: typeof COMMAND_ID.entry.delete
Expand All @@ -43,10 +39,6 @@ export type ViewSourceContentCommand = Command<{
id: typeof COMMAND_ID.entry.viewSourceContent
fn: (data: { entryId: string }) => void
}>
export type ViewEntryContentCommand = Command<{
id: typeof COMMAND_ID.entry.viewEntryContent
fn: () => void
}>

export type ShareCommand = Command<{
id: typeof COMMAND_ID.entry.share
Expand All @@ -58,11 +50,6 @@ export type ReadCommand = Command<{
fn: ({ entryId }) => void
}>

export type UnReadCommand = Command<{
id: typeof COMMAND_ID.entry.unread
fn: ({ entryId }) => void
}>

export type ToggleAISummaryCommand = Command<{
id: typeof COMMAND_ID.entry.toggleAISummary
fn: () => void
Expand All @@ -76,16 +63,13 @@ export type ToggleAITranslationCommand = Command<{
export type EntryCommand =
| TipCommand
| StarCommand
| UnStarCommand
| DeleteCommand
| CopyLinkCommand
| CopyTitleCommand
| OpenInBrowserCommand
| ViewSourceContentCommand
| ViewEntryContentCommand
| ShareCommand
| ReadCommand
| UnReadCommand
| ToggleAISummaryCommand
| ToggleAITranslationCommand

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,6 @@ test("useRegisterFollowCommand types", () => {
expectTypeOf(entryId).toEqualTypeOf<string>()
},
},
{
id: COMMAND_ID.entry.viewEntryContent,
label: "",
run: (...args) => {
expectTypeOf(args).toEqualTypeOf<[]>()
},
},
]),
)

Expand Down Expand Up @@ -71,13 +64,6 @@ test("useRegisterFollowCommand types", () => {
expectTypeOf(entryId).toEqualTypeOf<string>()
},
},
{
id: COMMAND_ID.entry.viewEntryContent,
label: "",
run: (...args) => {
expectTypeOf(args).toEqualTypeOf<[]>()
},
},
]),
)
})
10 changes: 0 additions & 10 deletions apps/renderer/src/modules/command/registry/command.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,6 @@ test("defineFollowCommand types", () => {
})

test("defineFollowCommand with keyBinding types", () => {
assertType(
defineFollowCommand({
id: COMMAND_ID.entry.viewEntryContent,
label: "",
when: true,
keyBinding: "",
run: () => {},
}),
)

assertType(
defineFollowCommand({
id: COMMAND_ID.entry.viewSourceContent,
Expand Down
4 changes: 2 additions & 2 deletions apps/renderer/src/modules/command/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export interface Command<
title: string
subTitle?: string
}
readonly icon?: ReactNode
readonly icon?: ReactNode | ((props?: { isActive?: boolean }) => ReactNode)
readonly category: CommandCategory
readonly run: T["fn"]

Expand All @@ -82,7 +82,7 @@ export interface CommandOptions<
| (() => string)
| { title: string; subTitle?: string }
| (() => { title: string; subTitle?: string })
icon?: ReactNode
icon?: ReactNode | ((props?: { isActive?: boolean }) => ReactNode)
category?: CommandCategory
run: T["fn"]

Expand Down
3 changes: 1 addition & 2 deletions apps/renderer/src/modules/customize-toolbar/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface ToolbarActionOrder {

export const DEFAULT_ACTION_ORDER: ToolbarActionOrder = {
main: Object.values(COMMAND_ID.entry)
.filter((id) => !([COMMAND_ID.entry.read, COMMAND_ID.entry.unread] as string[]).includes(id))
.filter((id) => !([COMMAND_ID.entry.read] as string[]).includes(id))
.filter(
(id) =>
!([COMMAND_ID.entry.copyLink, COMMAND_ID.entry.openInBrowser] as string[]).includes(id),
Expand All @@ -22,7 +22,6 @@ export const DEFAULT_ACTION_ORDER: ToolbarActionOrder = {
COMMAND_ID.entry.copyLink,
COMMAND_ID.entry.openInBrowser,
COMMAND_ID.entry.read,
COMMAND_ID.entry.unread,
] as string[]
).includes(id),
),
Expand Down
4 changes: 3 additions & 1 deletion apps/renderer/src/modules/customize-toolbar/dnd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export const SortableActionButton = ({ id }: { id: UniqueIdentifier }) => {
return (
<SortableItem id={id}>
<div className="flex flex-col items-center rounded-lg p-2 hover:bg-theme-button-hover">
<div className="flex size-8 items-center justify-center text-xl">{cmd.icon}</div>
<div className="flex size-8 items-center justify-center text-xl">
{typeof cmd.icon === "function" ? cmd.icon({ isActive: false }) : cmd.icon}
</div>
<div className="mt-1 text-center text-xs text-neutral-500 dark:text-neutral-400">
{cmd.label.title}
</div>
Expand Down
Loading

0 comments on commit fd1209a

Please sign in to comment.