diff --git a/apps/renderer/src/modules/entry-content/components/EntryTitle.tsx b/apps/renderer/src/modules/entry-content/components/EntryTitle.tsx
new file mode 100644
index 000000000..b8ee7d0a8
--- /dev/null
+++ b/apps/renderer/src/modules/entry-content/components/EntryTitle.tsx
@@ -0,0 +1,85 @@
+import { useMemo } from "react"
+
+import { useWhoami } from "~/atoms/user"
+import { useAuthQuery } from "~/hooks/common"
+import type { FeedModel } from "~/models"
+import { Queries } from "~/queries"
+import { useEntry, useEntryReadHistory } from "~/store/entry"
+import { getPreferredTitle, useFeedById } from "~/store/feed"
+
+import { EntryTranslation } from "../../entry-column/translation"
+
+interface EntryLinkProps {
+ entryId: string
+}
+
+const safeUrl = (url: string, baseUrl: string) => {
+ try {
+ return new URL(url, baseUrl).href
+ } catch {
+ return url
+ }
+}
+
+export const EntryTitle = ({ entryId }: EntryLinkProps) => {
+ const user = useWhoami()
+ const entry = useEntry(entryId)
+ const feed = useFeedById(entry?.feedId) as FeedModel
+ const entryHistory = useEntryReadHistory(entryId)
+
+ const populatedFullHref = useMemo(() => {
+ const href = entry?.entries.url
+ if (!href) return "#"
+
+ if (href.startsWith("http")) return href
+ const feedSiteUrl = feed?.siteUrl
+ if (href.startsWith("/") && feedSiteUrl) return safeUrl(href, feedSiteUrl)
+ return href
+ }, [entry?.entries.url, feed?.siteUrl])
+
+ const translation = useAuthQuery(
+ Queries.ai.translation({
+ entry: entry!,
+ language: entry?.settings?.translation,
+ extraFields: ["title"],
+ }),
+ {
+ enabled: !!entry?.settings?.translation,
+ refetchOnMount: false,
+ refetchOnWindowFocus: false,
+ meta: {
+ persist: true,
+ },
+ },
+ )
+
+ if (!entry) return null
+
+ return (
+
+
+
+
+ {getPreferredTitle(feed)}
+
+ {entry.entries.publishedAt && new Date(entry.entries.publishedAt).toLocaleString()}
+
+
+
+
+ {(
+ (entryHistory?.readCount ?? 0) +
+ (entryHistory?.userIds?.every((id) => id !== user?.id) ? 1 : 0)
+ ) // if no me, +1
+ .toLocaleString()}
+
+
+
+
+ )
+}
diff --git a/apps/renderer/src/modules/entry-content/components/SupportCreator.tsx b/apps/renderer/src/modules/entry-content/components/SupportCreator.tsx
new file mode 100644
index 000000000..55cd337f9
--- /dev/null
+++ b/apps/renderer/src/modules/entry-content/components/SupportCreator.tsx
@@ -0,0 +1,62 @@
+import { useTranslation } from "react-i18next"
+
+import { Button } from "~/components/ui/button"
+import { Divider } from "~/components/ui/divider"
+import { UserAvatar } from "~/components/user-button"
+import { useEntry } from "~/store/entry"
+import { useFeedById } from "~/store/feed"
+
+import { useTipModal } from "../../wallet/hooks"
+
+export const SupportCreator = ({ entryId }: { entryId: string }) => {
+ const { t } = useTranslation()
+ const entry = useEntry(entryId)
+ const feed = useFeedById(entry?.feedId)
+
+ const openTipModal = useTipModal({
+ userId: feed?.ownerUserId ?? undefined,
+ feedId: entry?.feedId,
+ entryId,
+ })
+
+ if (!feed || !feed.ownerUserId || feed.type !== "feed") return null
+
+ return (
+ <>
+
+
+
+
+
+ {!!feed.tipUsers?.length && (
+ <>
+
+ {t("entry_content.support_amount", { amount: feed.tipUsers.length })}
+
+
+ {feed.tipUsers?.map((user) => (
+
+
+
+ ))}
+
+ >
+ )}
+
+ >
+ )
+}
diff --git a/apps/renderer/src/modules/entry-content/index.tsx b/apps/renderer/src/modules/entry-content/index.tsx
index 6f4ebfff5..783f4e777 100644
--- a/apps/renderer/src/modules/entry-content/index.tsx
+++ b/apps/renderer/src/modules/entry-content/index.tsx
@@ -13,18 +13,14 @@ import {
useEntryReadabilityContent,
} from "~/atoms/readability"
import { useUISettingKey } from "~/atoms/settings/ui"
-import { useWhoami } from "~/atoms/user"
import { m } from "~/components/common/Motion"
import { ShadowDOM } from "~/components/common/ShadowDOM"
import { AutoResizeHeight } from "~/components/ui/auto-resize-height"
-import { Button } from "~/components/ui/button"
-import { Divider } from "~/components/ui/divider"
import { HTML } from "~/components/ui/markdown"
import { Toc } from "~/components/ui/markdown/components/Toc"
import { useInPeekModal } from "~/components/ui/modal/inspire/PeekModal"
import { RootPortal } from "~/components/ui/portal"
import { ScrollArea } from "~/components/ui/scroll-area"
-import { UserAvatar } from "~/components/user-button"
import { isWebBuild, ROUTE_FEED_PENDING } from "~/constants"
import { shortcuts } from "~/constants/shortcuts"
import { useEntryReadabilityToggle } from "~/hooks/biz/useEntryActions"
@@ -41,27 +37,19 @@ import {
WrappedElementProvider,
} from "~/providers/wrapped-element-provider"
import { Queries } from "~/queries"
-import { useEntry, useEntryReadHistory } from "~/store/entry"
-import { getPreferredTitle, useFeedById, useFeedHeaderTitle } from "~/store/feed"
+import { useEntry } from "~/store/entry"
+import { useFeedById, useFeedHeaderTitle } from "~/store/feed"
import { LoadingWithIcon } from "../../components/ui/loading"
import { EntryPlaceholderDaily } from "../ai/ai-daily/EntryPlaceholderDaily"
-import { EntryTranslation } from "../entry-column/translation"
-import { useTipModal } from "../wallet/hooks"
import { setEntryContentScrollToTop, setEntryTitleMeta } from "./atoms"
import { EntryPlaceholderLogo } from "./components/EntryPlaceholderLogo"
+import { EntryTitle } from "./components/EntryTitle"
+import { SupportCreator } from "./components/SupportCreator"
import { EntryHeader } from "./header"
import { EntryContentLoading } from "./loading"
import { EntryContentProvider } from "./provider"
-const safeUrl = (url: string, baseUrl: string) => {
- try {
- return new URL(url, baseUrl).href
- } catch {
- return url
- }
-}
-
export const EntryContent = ({ entryId }: { entryId: ActiveEntryId }) => {
const title = useFeedHeaderTitle()
const { feedId, view } = useRouteParams()
@@ -87,7 +75,6 @@ export const EntryContent = ({ entryId }: { entryId: ActiveEntryId }) => {
export const EntryContentRender: Component<{ entryId: string }> = ({ entryId, className }) => {
const { t } = useTranslation()
- const user = useWhoami()
const { error, data, isPending } = useAuthQuery(Queries.entries.byId(entryId), {
staleTime: 300_000,
@@ -97,27 +84,7 @@ export const EntryContentRender: Component<{ entryId: string }> = ({ entryId, cl
useTitle(entry?.entries.title)
const feed = useFeedById(entry?.feedId) as FeedModel
-
- const entryHistory = useEntryReadHistory(entryId)
-
const readerRenderInlineStyle = useUISettingKey("readerRenderInlineStyle")
-
- const translation = useAuthQuery(
- Queries.ai.translation({
- entry: entry!,
- language: entry?.settings?.translation,
- extraFields: ["title"],
- }),
- {
- enabled: !!entry?.settings?.translation,
- refetchOnMount: false,
- refetchOnWindowFocus: false,
- meta: {
- persist: true,
- },
- },
- )
-
const summary = useAuthQuery(
Queries.ai.summary({
entryId,
@@ -166,22 +133,6 @@ export const EntryContentRender: Component<{ entryId: string }> = ({ entryId, cl
[readerFontFamily],
)
- const populatedFullHref = useMemo(() => {
- const href = entry?.entries.url
- if (!href) return "#"
-
- if (href.startsWith("http")) return href
- const feedSiteUrl = feed?.siteUrl
- if (href.startsWith("/") && feedSiteUrl) return safeUrl(href, feedSiteUrl)
- return href
- }, [entry?.entries.url, feed?.siteUrl])
-
- const openTipModal = useTipModal({
- userId: feed?.ownerUserId ?? undefined,
- feedId: entry?.feedId,
- entryId,
- })
-
if (!entry) return null
const content = entry?.entries.content ?? data?.entries.content
@@ -216,33 +167,7 @@ export const EntryContentRender: Component<{ entryId: string }> = ({ entryId, cl
onContextMenu={stopPropagation}
className="relative m-auto min-w-0 max-w-[550px] @3xl:max-w-[70ch]"
>
-
-
-
-
-
- {getPreferredTitle(feed)}
-
-
- {entry.entries.publishedAt && new Date(entry.entries.publishedAt).toLocaleString()}
-
-
-
-
- {(
- (entryHistory?.readCount ?? 0) +
- (entryHistory?.userIds?.every((id) => id !== user?.id) ? 1 : 0)
- ) // if no me, +1
- .toLocaleString()}
-
-
-
-
+
@@ -301,44 +226,7 @@ export const EntryContentRender: Component<{ entryId: string }> = ({ entryId, cl
)}
- {feed?.ownerUserId && (
- <>
-
-
-
-
-
- {!!feed.tipUsers?.length && (
- <>
-
- {t("entry_content.support_amount", { amount: feed.tipUsers.length })}
-
-
- {feed.tipUsers?.map((user) => (
-
-
-
- ))}
-
- >
- )}
-
- >
- )}
+ {feed?.ownerUserId && }