From 810111291c4d5518960e6ecf1def6ccfe0cd1303 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Thu, 13 Jun 2024 02:29:49 +0800 Subject: [PATCH] feat: apply title and description ai translation --- .../components/entry-column/article-item.tsx | 6 +- .../components/entry-column/audio-item.tsx | 4 +- .../src/components/entry-column/index.tsx | 46 +--------------- .../components/entry-column/item-wrapper.tsx | 55 ++++++++++++++++++- .../entry-column/notification-item.tsx | 4 +- .../components/entry-column/picture-item.tsx | 4 +- .../entry-column/social-media-item.tsx | 4 +- .../src/components/entry-column/types.ts | 4 ++ .../components/entry-column/video-item.tsx | 4 +- src/renderer/src/hono.ts | 23 +++++++- src/renderer/src/lib/constants.tsx | 6 ++ src/renderer/src/queries/ai.ts | 27 +++++++++ src/renderer/src/queries/index.ts | 2 + 13 files changed, 127 insertions(+), 62 deletions(-) create mode 100644 src/renderer/src/queries/ai.ts diff --git a/src/renderer/src/components/entry-column/article-item.tsx b/src/renderer/src/components/entry-column/article-item.tsx index c4b875961..3dd3c1f08 100644 --- a/src/renderer/src/components/entry-column/article-item.tsx +++ b/src/renderer/src/components/entry-column/article-item.tsx @@ -8,7 +8,7 @@ import { useEntry } from "@renderer/store/entry" import { ReactVirtuosoItemPlaceholder } from "../ui/placeholder" import type { UniversalItemProps } from "./types" -export function ArticleItem({ entryId, entryPreview }: UniversalItemProps) { +export function ArticleItem({ entryId, entryPreview, translation }: UniversalItemProps) { const entry = useEntry(entryId) || entryPreview const asRead = useAsRead(entry) @@ -33,13 +33,13 @@ export function ArticleItem({ entryId, entryPreview }: UniversalItemProps) {
- {entry.entries.title || entry.entries.description} + {translation?.title || translation?.description || entry.entries.title || entry.entries.description} {!!entry.collections && ( )}
- {entry.entries.description} + {translation?.description || entry.entries.description}
{entry.entries.images?.[0] && ( diff --git a/src/renderer/src/components/entry-column/audio-item.tsx b/src/renderer/src/components/entry-column/audio-item.tsx index 65ce0a428..2f9bd2d3b 100644 --- a/src/renderer/src/components/entry-column/audio-item.tsx +++ b/src/renderer/src/components/entry-column/audio-item.tsx @@ -6,7 +6,7 @@ import { useEntry } from "@renderer/store/entry" import { ReactVirtuosoItemPlaceholder } from "../ui/placeholder" import type { UniversalItemProps } from "./types" -export function AudioItem({ entryId, entryPreview }: UniversalItemProps) { +export function AudioItem({ entryId, entryPreview, translation }: UniversalItemProps) { const entry = useEntry(entryId) || entryPreview if (!entry) return return ( @@ -26,7 +26,7 @@ export function AudioItem({ entryId, entryPreview }: UniversalItemProps) {
- {entry.entries.title} + {translation?.title || entry.entries.title} {!!entry.collections && ( )} diff --git a/src/renderer/src/components/entry-column/index.tsx b/src/renderer/src/components/entry-column/index.tsx index 5febdd889..a15964177 100644 --- a/src/renderer/src/components/entry-column/index.tsx +++ b/src/renderer/src/components/entry-column/index.tsx @@ -7,7 +7,6 @@ import { } from "@renderer/components/ui/popover" import { apiClient } from "@renderer/lib/api-fetch" import { views } from "@renderer/lib/constants" -import { FeedViewType } from "@renderer/lib/enum" import { buildStorageNS } from "@renderer/lib/ns" import { getEntriesParams } from "@renderer/lib/utils" import { useEntries } from "@renderer/queries/entries" @@ -29,14 +28,7 @@ import { useEventCallback } from "usehooks-ts" import { useShallow } from "zustand/react/shallow" import { EmptyIcon } from "../icons/empty" -import { ArticleItem } from "./article-item" -import { AudioItem } from "./audio-item" import { EntryItemWrapper } from "./item-wrapper" -import { NotificationItem } from "./notification-item" -import { PictureItem } from "./picture-item" -import { SocialMediaItem } from "./social-media-item" -import type { UniversalItemProps } from "./types" -import { VideoItem } from "./video-item" const unreadOnlyAtom = atomWithStorage( buildStorageNS("entry-unreadonly"), @@ -50,38 +42,6 @@ export function EntryColumn() { const entries = useEntriesByView() const { entriesIds } = entries - let Item: FC - - switch (activeList?.view) { - case FeedViewType.Articles: { - Item = ArticleItem - break - } - case FeedViewType.SocialMedia: { - Item = SocialMediaItem - break - } - case FeedViewType.Pictures: { - Item = PictureItem - break - } - case FeedViewType.Videos: { - Item = VideoItem - break - } - case FeedViewType.Audios: { - Item = AudioItem - break - } - case FeedViewType.Notifications: { - Item = NotificationItem - break - } - default: { - Item = ArticleItem - } - } - const handleRangeChange = useEventCallback( debounce( async ({ startIndex }: ListRange) => { @@ -134,12 +94,10 @@ export function EntryColumn() { key={entryId} entryId={entryId} view={activeList?.view} - > - - + /> ) }, - [Item, activeList?.view], + [activeList?.view], ), } diff --git a/src/renderer/src/components/entry-column/item-wrapper.tsx b/src/renderer/src/components/entry-column/item-wrapper.tsx index 3c04b0a92..ea404a7c7 100644 --- a/src/renderer/src/components/entry-column/item-wrapper.tsx +++ b/src/renderer/src/components/entry-column/item-wrapper.tsx @@ -1,20 +1,29 @@ import { useAsRead } from "@renderer/hooks/useAsRead" +import { useBizQuery } from "@renderer/hooks/useBizQuery" import { useEntryActions, useRead } from "@renderer/hooks/useEntryActions" import { views } from "@renderer/lib/constants" +import { FeedViewType } from "@renderer/lib/enum" import { showNativeMenu } from "@renderer/lib/native-menu" import { cn } from "@renderer/lib/utils" +import { Queries } from "@renderer/queries" import { feedActions, useFeedStore } from "@renderer/store" import { useEntry } from "@renderer/store/entry" +import type { FC } from "react" import { ReactVirtuosoItemPlaceholder } from "../ui/placeholder" +import { ArticleItem } from "./article-item" +import { AudioItem } from "./audio-item" +import { NotificationItem } from "./notification-item" +import { PictureItem } from "./picture-item" +import { SocialMediaItem } from "./social-media-item" +import type { UniversalItemProps } from "./types" +import { VideoItem } from "./video-item" export function EntryItemWrapper({ - children, entryId, view, }: { entryId: string - children: React.ReactNode view?: number }) { const entry = useEntry(entryId) @@ -23,6 +32,14 @@ export function EntryItemWrapper({ entry, }) + const translation = useBizQuery(Queries.ai.translation({ + id: entry.entries.id, + language: entry.settings?.translation, + fields: view ? views[view].translation : "", + }), { + enabled: !!entry.settings?.translation, + }) + const activeEntry = useFeedStore((state) => state.activeEntry) const asRead = useAsRead(entry) @@ -32,6 +49,38 @@ export function EntryItemWrapper({ // NOTE: prevent 0 height element, react virtuoso will not stop render any more if (!entry) return + let Item: FC + + switch (view) { + case FeedViewType.Articles: { + Item = ArticleItem + break + } + case FeedViewType.SocialMedia: { + Item = SocialMediaItem + break + } + case FeedViewType.Pictures: { + Item = PictureItem + break + } + case FeedViewType.Videos: { + Item = VideoItem + break + } + case FeedViewType.Audios: { + Item = AudioItem + break + } + case FeedViewType.Notifications: { + Item = NotificationItem + break + } + default: { + Item = ArticleItem + } + } + return (
- {children} +
) diff --git a/src/renderer/src/components/entry-column/notification-item.tsx b/src/renderer/src/components/entry-column/notification-item.tsx index 64864cee2..e683183e8 100644 --- a/src/renderer/src/components/entry-column/notification-item.tsx +++ b/src/renderer/src/components/entry-column/notification-item.tsx @@ -6,7 +6,7 @@ import { useEntry } from "@renderer/store/entry" import { ReactVirtuosoItemPlaceholder } from "../ui/placeholder" import type { UniversalItemProps } from "./types" -export function NotificationItem({ entryId, entryPreview }: UniversalItemProps) { +export function NotificationItem({ entryId, entryPreview, translation }: UniversalItemProps) { const entry = useEntry(entryId) || entryPreview if (!entry) return return ( @@ -26,7 +26,7 @@ export function NotificationItem({ entryId, entryPreview }: UniversalItemProps)
- {entry.entries.title} + {translation?.title || entry.entries.title} {!!entry.collections && ( )} diff --git a/src/renderer/src/components/entry-column/picture-item.tsx b/src/renderer/src/components/entry-column/picture-item.tsx index e92da5bce..952fcaba1 100644 --- a/src/renderer/src/components/entry-column/picture-item.tsx +++ b/src/renderer/src/components/entry-column/picture-item.tsx @@ -8,7 +8,7 @@ import { useEntry } from "@renderer/store/entry" import { usePreviewImages } from "../ui/image/hooks" import type { UniversalItemProps } from "./types" -export function PictureItem({ entryId, entryPreview }: UniversalItemProps) { +export function PictureItem({ entryId, entryPreview, translation }: UniversalItemProps) { const entry = useEntry(entryId) || entryPreview const previewImage = usePreviewImages() if (!entry) return @@ -37,7 +37,7 @@ export function PictureItem({ entryId, entryPreview }: UniversalItemProps) { !!entry.collections && "pr-4", )} > - {entry.entries.title} + {translation?.title || entry.entries.title} {!!entry.collections && ( )} diff --git a/src/renderer/src/components/entry-column/social-media-item.tsx b/src/renderer/src/components/entry-column/social-media-item.tsx index 77228d31c..4a0c775f0 100644 --- a/src/renderer/src/components/entry-column/social-media-item.tsx +++ b/src/renderer/src/components/entry-column/social-media-item.tsx @@ -7,7 +7,7 @@ import { useEntry } from "@renderer/store/entry" import { ReactVirtuosoItemPlaceholder } from "../ui/placeholder" import type { UniversalItemProps } from "./types" -export function SocialMediaItem({ entryId, entryPreview }: UniversalItemProps) { +export function SocialMediaItem({ entryId, entryPreview, translation }: UniversalItemProps) { const entry = useEntry(entryId) || entryPreview // NOTE: prevent 0 height element, react virtuoso will not stop render any more @@ -30,7 +30,7 @@ export function SocialMediaItem({ entryId, entryPreview }: UniversalItemProps) {
- {entry.entries.description} + {translation?.description || entry.entries.description} {!!entry.collections && ( )} diff --git a/src/renderer/src/components/entry-column/types.ts b/src/renderer/src/components/entry-column/types.ts index 2a19a362e..10e0aea49 100644 --- a/src/renderer/src/components/entry-column/types.ts +++ b/src/renderer/src/components/entry-column/types.ts @@ -5,4 +5,8 @@ export type UniversalItemProps = { entryPreview?: EntryModel | { feeds: FeedModel } + translation?: { + title?: string + description?: string + } | null } diff --git a/src/renderer/src/components/entry-column/video-item.tsx b/src/renderer/src/components/entry-column/video-item.tsx index db6d15af0..cf7c77f77 100644 --- a/src/renderer/src/components/entry-column/video-item.tsx +++ b/src/renderer/src/components/entry-column/video-item.tsx @@ -10,7 +10,7 @@ import { useMemo, useRef, useState } from "react" import { ReactVirtuosoItemPlaceholder } from "../ui/placeholder" import type { UniversalItemProps } from "./types" -export function VideoItem({ entryId, entryPreview }: UniversalItemProps) { +export function VideoItem({ entryId, entryPreview, translation }: UniversalItemProps) { const entry = useEntry(entryId) || entryPreview const iframeSrc = useMemo(() => urlToIframe(entry.entries.url), [entry.entries.url]) @@ -52,7 +52,7 @@ export function VideoItem({ entryId, entryPreview }: UniversalItemProps) {
- {entry.entries.title} + {translation?.title || entry.entries.title} {!!entry.collections && ( )} diff --git a/src/renderer/src/hono.ts b/src/renderer/src/hono.ts index c54391de1..9aa9cdbdf 100644 --- a/src/renderer/src/hono.ts +++ b/src/renderer/src/hono.ts @@ -3,6 +3,27 @@ import * as hono_utils_http_status from 'hono/utils/http-status'; import * as hono from 'hono'; declare const routes: hono_hono_base.HonoBase, className: "text-amber-700", + translation: "title,description", }, { name: "Social Media", icon: , className: "text-sky-600", wideMode: true, + translation: "description", }, { name: "Pictures", @@ -25,6 +27,7 @@ export const views = [ className: "text-green-600", gridMode: true, wideMode: true, + translation: "title", }, { name: "Videos", @@ -32,16 +35,19 @@ export const views = [ className: "text-red-600", gridMode: true, wideMode: true, + translation: "title", }, { name: "Audios", icon: , className: "text-purple-600", + translation: "title", }, { name: "Notifications", icon: , className: "text-yellow-600", + translation: "title", }, ] diff --git a/src/renderer/src/queries/ai.ts b/src/renderer/src/queries/ai.ts new file mode 100644 index 000000000..fe0d26b09 --- /dev/null +++ b/src/renderer/src/queries/ai.ts @@ -0,0 +1,27 @@ +import { apiClient } from "@renderer/lib/api-fetch" +import { defineQuery } from "@renderer/lib/defineQuery" + +export const ai = { + translation: ({ + id, + language, + fields, + }: { + id: string + language?: string + fields: string + }) => + defineQuery(["translation", id, language], async () => { + if (!language) { + return null + } + const res = await apiClient.ai.translation.$get({ + query: { + id, + language: language as "en" | "ja" | "zh-CN" | "zh-TW", + fields, + }, + }) + return res.data + }), +} diff --git a/src/renderer/src/queries/index.ts b/src/renderer/src/queries/index.ts index 7b6a296bb..20b21ebd5 100644 --- a/src/renderer/src/queries/index.ts +++ b/src/renderer/src/queries/index.ts @@ -1,4 +1,5 @@ import { action } from "./actions" +import { ai } from "./ai" import { auth } from "./auth" import { entries } from "./entries" import { feed } from "./feed" @@ -10,4 +11,5 @@ export const Queries = { feed, action, auth, + ai, }