diff --git a/apps/mobile/changelog/0.2.1.md b/apps/mobile/changelog/0.2.1.md
new file mode 100644
index 000000000..12c327974
--- /dev/null
+++ b/apps/mobile/changelog/0.2.1.md
@@ -0,0 +1,23 @@
+# What's New in v0.2.1
+
+## Shiny new things
+
+- Video playback is now supported in both the entry list and entry view (#3798 #3816)
+- AI summaries can be rendered in Markdown (bf7193d)
+
+## Improvements
+
+- Gradually rolling out an experimental unified local database for mobile and desktop (#3809)
+- Added a “Mark as read” option to the long-press menu in lists (7c4b896)
+- RSSHub routes list now shows popularity scores and sorts by them (9c3457a)
+- Polished the RSSHub route detail page and added “Top Feeds” (6a51c8f)
+- Feed-subscription page now displays feed analytics (2c27d91)
+- Trending list now shows subscriber counts (bdb6802)
+- AI will skip summarising extra-short articles to avoid trivial summaries (63388b4)
+
+## No longer broken
+
+- AI-summary component now correctly spans full width (2612f9)
+- Fixed icon-colour issues in dark mode (c7be63b)
+- Fixed misaligned icons (75c9f80)
+- Fixed occasional transparency on the bottom navigation bar (#3867)
diff --git a/apps/mobile/ios/Folo/Info.plist b/apps/mobile/ios/Folo/Info.plist
index 008493ad9..409ca3d33 100644
--- a/apps/mobile/ios/Folo/Info.plist
+++ b/apps/mobile/ios/Folo/Info.plist
@@ -32,7 +32,7 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 0.2.0
+ 0.2.1
CFBundleSignature
????
CFBundleURLTypes
@@ -52,7 +52,7 @@
CFBundleVersion
- 108
+ 109
ITSAppUsesNonExemptEncryption
LSApplicationCategoryType
diff --git a/apps/mobile/package.json b/apps/mobile/package.json
index f30104bca..a8de58cd5 100644
--- a/apps/mobile/package.json
+++ b/apps/mobile/package.json
@@ -1,6 +1,6 @@
{
"name": "@follow/mobile",
- "version": "0.2.0",
+ "version": "0.2.1",
"private": true,
"main": "src/main.tsx",
"scripts": {
diff --git a/apps/mobile/src/modules/discover/Recommendations.tsx b/apps/mobile/src/modules/discover/Recommendations.tsx
index aa0c1a455..3e1d0afba 100644
--- a/apps/mobile/src/modules/discover/Recommendations.tsx
+++ b/apps/mobile/src/modules/discover/Recommendations.tsx
@@ -129,7 +129,7 @@ export const RecommendationTab: TabComponent<{
const discoverLanguage = useUISettingKey("discoverLanguage")
const { data, isLoading } = useQuery({
- queryKey: ["rsshub-popular", "cache", tab.value],
+ queryKey: ["rsshub-popular", "cache", tab.value, discoverLanguage],
queryFn: () =>
fetchRsshubPopular(tab.value, LanguageMap[discoverLanguage]).then((res) => res.data),
staleTime: 1000 * 60 * 60 * 24, // 1 day
diff --git a/apps/mobile/src/modules/entry-content/EntryAISummary.tsx b/apps/mobile/src/modules/entry-content/EntryAISummary.tsx
index fc505b3ca..b7e8196cf 100644
--- a/apps/mobile/src/modules/entry-content/EntryAISummary.tsx
+++ b/apps/mobile/src/modules/entry-content/EntryAISummary.tsx
@@ -1,10 +1,11 @@
+import { parseHtml } from "@follow/components/ui/markdown/parse-html.ts"
import { useEntry } from "@follow/store/entry/hooks"
import { SummaryGeneratingStatus } from "@follow/store/summary/enum"
import { usePrefetchSummary, useSummary } from "@follow/store/summary/hooks"
import { useSummaryStore } from "@follow/store/summary/store"
import { useAtomValue } from "jotai"
import type { FC } from "react"
-import { useMemo } from "react"
+import { useCallback, useMemo } from "react"
import { Text } from "react-native"
import { useActionLanguage, useGeneralSettingKey } from "@/src/atoms/settings/general"
@@ -21,14 +22,30 @@ export const EntryAISummary: FC<{
const showReadability = useAtomValue(ctx.showReadabilityAtom)
const showAISummaryOnce = useAtomValue(ctx.showAISummaryAtom)
const showAISummary = useGeneralSettingKey("summary") || showAISummaryOnce
- const entryReadabilityContent = useEntry(entryId, (state) => state.readabilityContent)
+ const entry = useEntry(
+ entryId,
+ useCallback(
+ (state) => {
+ const content = showReadability ? state.readabilityContent : state.content
+ const target =
+ showReadability && state.readabilityContent ? "readabilityContent" : "content"
+ const textLength = content ? parseHtml(content).toText().length : 0
+
+ return {
+ target,
+ isShortContent: textLength < 100,
+ } as const
+ },
+ [showReadability],
+ ),
+ )
const summary = useSummary(entryId)
const actionLanguage = useActionLanguage()
usePrefetchSummary({
entryId,
- target: showReadability && entryReadabilityContent ? "readabilityContent" : "content",
+ target: entry?.target || "content",
actionLanguage,
- enabled: showAISummary,
+ enabled: showAISummary && !entry?.isShortContent,
})
const summaryToShow = useMemo(() => {
const maybeMarkdown = showReadability