feat(mobile): support anonymous timeline reading (#4911)
This commit is contained in:
parent
565793fceb
commit
f78d7e766b
|
|
@ -11,6 +11,13 @@ appId: is.follow
|
|||
commands:
|
||||
- tapOn:
|
||||
id: no-login-timeline
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
id: home-avatar-trigger
|
||||
commands:
|
||||
- tapOn:
|
||||
id: home-avatar-trigger
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: login-screen
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useIsEntryStarred } from "@follow/store/collection/hooks"
|
|||
import { collectionSyncService } from "@follow/store/collection/store"
|
||||
import { useEntry } from "@follow/store/entry/hooks"
|
||||
import { unreadSyncService } from "@follow/store/unread/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import { requireNativeModule } from "expo"
|
||||
import type { PropsWithChildren } from "react"
|
||||
import { useRef } from "react"
|
||||
|
|
@ -35,6 +36,7 @@ const getIOSNativeImageActions = () => {
|
|||
|
||||
export const ImageContextMenu = ({ imageUrl, entryId, children, view }: ImageContextMenuProps) => {
|
||||
const { t } = useTranslation()
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const entry = useEntry(entryId, (state) => ({
|
||||
read: state.read,
|
||||
feedId: state.feedId,
|
||||
|
|
@ -58,7 +60,7 @@ export const ImageContextMenu = ({ imageUrl, entryId, children, view }: ImageCon
|
|||
</ContextMenu.Trigger>
|
||||
|
||||
<ContextMenu.Content>
|
||||
{entryId && feedId && view !== undefined && (
|
||||
{isLoggedIn && entryId && feedId && view !== undefined && (
|
||||
<>
|
||||
<ContextMenu.Item
|
||||
key="MarkAsRead"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { collectionSyncService } from "@follow/store/collection/store"
|
|||
import { getEntry } from "@follow/store/entry/getter"
|
||||
import { useEntry } from "@follow/store/entry/hooks"
|
||||
import { unreadSyncService } from "@follow/store/unread/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import { PortalProvider } from "@gorhom/portal"
|
||||
import type { PropsWithChildren } from "react"
|
||||
import { useCallback } from "react"
|
||||
|
|
@ -32,6 +33,7 @@ export const EntryItemContextMenu = ({
|
|||
const { t } = useTranslation()
|
||||
const selectedView = useSelectedView()
|
||||
const selectedFeed = useSelectedFeed()
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const entry = useEntry(id, (state) => ({
|
||||
read: state.read,
|
||||
feedId: state.feedId,
|
||||
|
|
@ -73,81 +75,85 @@ export const EntryItemContextMenu = ({
|
|||
)}
|
||||
</ContextMenu.Preview>
|
||||
|
||||
<ContextMenu.Item
|
||||
key="MarkAsReadAbove"
|
||||
onSelect={() => {
|
||||
const payload = getFetchEntryPayload(selectedFeed, selectedView)
|
||||
const { publishedAt } = entry
|
||||
unreadSyncService.markBatchAsRead({
|
||||
view: selectedView,
|
||||
filter: payload,
|
||||
time: {
|
||||
startTime: new Date(publishedAt).getTime() + 1,
|
||||
endTime: Date.now(),
|
||||
},
|
||||
excludePrivate: getHideAllReadSubscriptions(),
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: "arrow.up",
|
||||
}}
|
||||
/>
|
||||
<ContextMenu.ItemTitle>
|
||||
{t("operation.mark_all_as_read_which", {
|
||||
which: t("operation.mark_all_as_read_which_above"),
|
||||
})}
|
||||
</ContextMenu.ItemTitle>
|
||||
</ContextMenu.Item>
|
||||
{isLoggedIn && (
|
||||
<>
|
||||
<ContextMenu.Item
|
||||
key="MarkAsReadAbove"
|
||||
onSelect={() => {
|
||||
const payload = getFetchEntryPayload(selectedFeed, selectedView)
|
||||
const { publishedAt } = entry
|
||||
unreadSyncService.markBatchAsRead({
|
||||
view: selectedView,
|
||||
filter: payload,
|
||||
time: {
|
||||
startTime: new Date(publishedAt).getTime() + 1,
|
||||
endTime: Date.now(),
|
||||
},
|
||||
excludePrivate: getHideAllReadSubscriptions(),
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: "arrow.up",
|
||||
}}
|
||||
/>
|
||||
<ContextMenu.ItemTitle>
|
||||
{t("operation.mark_all_as_read_which", {
|
||||
which: t("operation.mark_all_as_read_which_above"),
|
||||
})}
|
||||
</ContextMenu.ItemTitle>
|
||||
</ContextMenu.Item>
|
||||
|
||||
<ContextMenu.Item
|
||||
key="MarkAsRead"
|
||||
onSelect={() => {
|
||||
entry.read
|
||||
? unreadSyncService.markEntryAsUnread(id)
|
||||
: unreadSyncService.markEntryAsRead(id)
|
||||
}}
|
||||
>
|
||||
<ContextMenu.ItemTitle>
|
||||
{entry.read ? t("operation.mark_as_unread") : t("operation.mark_as_read")}
|
||||
</ContextMenu.ItemTitle>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: entry.read ? "circle.fill" : "checkmark.circle",
|
||||
}}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
key="MarkAsRead"
|
||||
onSelect={() => {
|
||||
entry.read
|
||||
? unreadSyncService.markEntryAsUnread(id)
|
||||
: unreadSyncService.markEntryAsRead(id)
|
||||
}}
|
||||
>
|
||||
<ContextMenu.ItemTitle>
|
||||
{entry.read ? t("operation.mark_as_unread") : t("operation.mark_as_read")}
|
||||
</ContextMenu.ItemTitle>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: entry.read ? "circle.fill" : "checkmark.circle",
|
||||
}}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
|
||||
<ContextMenu.Item
|
||||
key="MarkAsReadBelow"
|
||||
onSelect={() => {
|
||||
const payload = getFetchEntryPayload(selectedFeed, selectedView)
|
||||
const { publishedAt } = entry
|
||||
unreadSyncService.markBatchAsRead({
|
||||
view: selectedView,
|
||||
filter: payload,
|
||||
time: {
|
||||
startTime: 1,
|
||||
endTime: new Date(publishedAt).getTime() - 1,
|
||||
},
|
||||
excludePrivate: getHideAllReadSubscriptions(),
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: "arrow.down",
|
||||
}}
|
||||
/>
|
||||
<ContextMenu.ItemTitle>
|
||||
{t("operation.mark_all_as_read_which", {
|
||||
which: t("operation.mark_all_as_read_which_below"),
|
||||
})}
|
||||
</ContextMenu.ItemTitle>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
key="MarkAsReadBelow"
|
||||
onSelect={() => {
|
||||
const payload = getFetchEntryPayload(selectedFeed, selectedView)
|
||||
const { publishedAt } = entry
|
||||
unreadSyncService.markBatchAsRead({
|
||||
view: selectedView,
|
||||
filter: payload,
|
||||
time: {
|
||||
startTime: 1,
|
||||
endTime: new Date(publishedAt).getTime() - 1,
|
||||
},
|
||||
excludePrivate: getHideAllReadSubscriptions(),
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: "arrow.down",
|
||||
}}
|
||||
/>
|
||||
<ContextMenu.ItemTitle>
|
||||
{t("operation.mark_all_as_read_which", {
|
||||
which: t("operation.mark_all_as_read_which_below"),
|
||||
})}
|
||||
</ContextMenu.ItemTitle>
|
||||
</ContextMenu.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
{feedId && view !== undefined && (
|
||||
{isLoggedIn && feedId && view !== undefined && (
|
||||
<ContextMenu.Item
|
||||
key="Star"
|
||||
onSelect={() => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useIsEntryStarred } from "@follow/store/collection/hooks"
|
|||
import { collectionSyncService } from "@follow/store/collection/store"
|
||||
import { useEntry } from "@follow/store/entry/hooks"
|
||||
import { unreadSyncService } from "@follow/store/unread/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import type { PropsWithChildren } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Share } from "react-native"
|
||||
|
|
@ -16,6 +17,7 @@ type VideoContextMenuProps = PropsWithChildren<{
|
|||
|
||||
export const VideoContextMenu = ({ entryId, children }: VideoContextMenuProps) => {
|
||||
const { t } = useTranslation()
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const entry = useEntry(entryId, (state) => ({
|
||||
read: state.read,
|
||||
feedId: state.feedId,
|
||||
|
|
@ -35,24 +37,26 @@ export const VideoContextMenu = ({ entryId, children }: VideoContextMenuProps) =
|
|||
<ContextMenu.Trigger>{children}</ContextMenu.Trigger>
|
||||
|
||||
<ContextMenu.Content>
|
||||
<ContextMenu.Item
|
||||
key="MarkAsRead"
|
||||
onSelect={() => {
|
||||
entry.read
|
||||
? unreadSyncService.markEntryAsUnread(entryId)
|
||||
: unreadSyncService.markEntryAsRead(entryId)
|
||||
}}
|
||||
>
|
||||
<ContextMenu.ItemTitle>
|
||||
{entry.read ? t("operation.mark_as_unread") : t("operation.mark_as_read")}
|
||||
</ContextMenu.ItemTitle>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: entry.read ? "circle.fill" : "checkmark.circle",
|
||||
{isLoggedIn && (
|
||||
<ContextMenu.Item
|
||||
key="MarkAsRead"
|
||||
onSelect={() => {
|
||||
entry.read
|
||||
? unreadSyncService.markEntryAsUnread(entryId)
|
||||
: unreadSyncService.markEntryAsRead(entryId)
|
||||
}}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
{feedId && (
|
||||
>
|
||||
<ContextMenu.ItemTitle>
|
||||
{entry.read ? t("operation.mark_as_unread") : t("operation.mark_as_read")}
|
||||
</ContextMenu.ItemTitle>
|
||||
<ContextMenu.ItemIcon
|
||||
ios={{
|
||||
name: entry.read ? "circle.fill" : "checkmark.circle",
|
||||
}}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
)}
|
||||
{isLoggedIn && feedId && (
|
||||
<ContextMenu.Item
|
||||
key="Star"
|
||||
onSelect={() => {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { entrySyncServices } from "@follow/store/entry/store"
|
|||
import { useFeedById } from "@follow/store/feed/hooks"
|
||||
import { useSubscriptionById } from "@follow/store/subscription/hooks"
|
||||
import { translationSyncService } from "@follow/store/translation/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import { setStringAsync } from "expo-clipboard"
|
||||
import { useAtom } from "jotai"
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
|
|
@ -58,6 +59,7 @@ const HeaderRightActionsImpl = ({
|
|||
}: HeaderRightActionsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const labelColor = useColor("label")
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const isStarred = useIsEntryStarred(entryId)
|
||||
const [extraActionContainerWidth, setExtraActionContainerWidth] = useState(0)
|
||||
|
||||
|
|
@ -136,18 +138,19 @@ const HeaderRightActionsImpl = ({
|
|||
|
||||
// Define action items for reuse
|
||||
const actionItems = [
|
||||
subscription && {
|
||||
key: "Star",
|
||||
title: isStarred ? t("operation.unstar") : t("operation.star"),
|
||||
icon: isStarred ? <StarCuteFiIcon /> : <StarCuteReIcon />,
|
||||
iconIOS: {
|
||||
name: isStarred ? "star.fill" : "star",
|
||||
paletteColors: isStarred ? ["#facc15"] : undefined,
|
||||
isLoggedIn &&
|
||||
subscription && {
|
||||
key: "Star",
|
||||
title: isStarred ? t("operation.unstar") : t("operation.star"),
|
||||
icon: isStarred ? <StarCuteFiIcon /> : <StarCuteReIcon />,
|
||||
iconIOS: {
|
||||
name: isStarred ? "star.fill" : "star",
|
||||
paletteColors: isStarred ? ["#facc15"] : undefined,
|
||||
},
|
||||
onPress: handleToggleStar,
|
||||
active: isStarred,
|
||||
iconColor: isStarred ? "#facc15" : undefined,
|
||||
},
|
||||
onPress: handleToggleStar,
|
||||
active: isStarred,
|
||||
iconColor: isStarred ? "#facc15" : undefined,
|
||||
},
|
||||
!showReadabilitySetting && {
|
||||
key: "ShowReadability",
|
||||
title: "Show Readability",
|
||||
|
|
@ -158,16 +161,17 @@ const HeaderRightActionsImpl = ({
|
|||
isCheckbox: true,
|
||||
// inMenu: true,
|
||||
},
|
||||
!showAITranslationSetting && {
|
||||
key: "ShowTranslation",
|
||||
title: "Show Translation",
|
||||
icon: <Translate2CuteReIcon />,
|
||||
iconIOS: { name: "globe" },
|
||||
onPress: toggleAITranslation,
|
||||
active: showTranslation,
|
||||
isCheckbox: true,
|
||||
inMenu: true,
|
||||
},
|
||||
isLoggedIn &&
|
||||
!showAITranslationSetting && {
|
||||
key: "ShowTranslation",
|
||||
title: "Show Translation",
|
||||
icon: <Translate2CuteReIcon />,
|
||||
iconIOS: { name: "globe" },
|
||||
onPress: toggleAITranslation,
|
||||
active: showTranslation,
|
||||
isCheckbox: true,
|
||||
inMenu: true,
|
||||
},
|
||||
{
|
||||
key: "Share",
|
||||
title: t("operation.share"),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useEntryReadHistory } from "@follow/store/entry/hooks"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import { View } from "react-native"
|
||||
|
||||
import { UserAvatar } from "@/src/components/ui/avatar/UserAvatar"
|
||||
|
|
@ -7,9 +8,10 @@ import { useNavigation } from "@/src/lib/navigation/hooks"
|
|||
import { ProfileScreen } from "@/src/screens/(modal)/ProfileScreen"
|
||||
|
||||
export const EntryReadHistory = ({ entryId }: { entryId: string }) => {
|
||||
const data = useEntryReadHistory(entryId, 6)
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const data = useEntryReadHistory(entryId, 6, isLoggedIn)
|
||||
const navigation = useNavigation()
|
||||
if (!data?.entryReadHistories) return null
|
||||
if (!isLoggedIn || !data?.entryReadHistories) return null
|
||||
return (
|
||||
<View className="flex-row items-center justify-center">
|
||||
{data?.entryReadHistories.userIds.map((userId, index) => {
|
||||
|
|
|
|||
|
|
@ -13,14 +13,20 @@ import { useNavigation } from "@/src/lib/navigation/hooks"
|
|||
import { EntryListContentPicture } from "@/src/modules/entry-list/EntryListContentPicture"
|
||||
import { EntryDetailScreen } from "@/src/screens/(stack)/entries/[entryId]/EntryDetailScreen"
|
||||
|
||||
import { useEntries } from "../screen/atoms"
|
||||
import { useEntries, useEntryListContext } from "../screen/atoms"
|
||||
import { EntryListContentArticle } from "./EntryListContentArticle"
|
||||
import { EntryListContentSocial } from "./EntryListContentSocial"
|
||||
import { EntryListContentVideo } from "./EntryListContentVideo"
|
||||
|
||||
const NoLoginGuard = ({ children }: { children: React.ReactNode }) => {
|
||||
const whoami = useWhoami()
|
||||
return whoami ? children : <NoLoginInfo target="timeline" />
|
||||
const screenType = useEntryListContext().type
|
||||
|
||||
if (whoami || screenType !== "subscriptions") {
|
||||
return children
|
||||
}
|
||||
|
||||
return <NoLoginInfo target="subscriptions" />
|
||||
}
|
||||
|
||||
type EntryListSelectorProps = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { debouncedFetchEntryContentByStream } from "@follow/store/entry/store"
|
||||
import { unreadSyncService } from "@follow/store/unread/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import type { ViewToken } from "@shopify/flash-list"
|
||||
import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from "react"
|
||||
import type { NativeScrollEvent, NativeSyntheticEvent } from "react-native"
|
||||
|
|
@ -18,6 +19,7 @@ export function useOnViewableItemsChanged({
|
|||
} = {}) {
|
||||
const orientation = useRef<"down" | "up">("down")
|
||||
const lastOffset = useRef(0)
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
|
||||
const markAsReadWhenScrolling = useGeneralSettingKey("scrollMarkUnread")
|
||||
const markAsReadWhenRendering = useGeneralSettingKey("renderMarkUnread")
|
||||
|
|
@ -58,7 +60,7 @@ export function useOnViewableItemsChanged({
|
|||
useEffect(() => {
|
||||
if (disabled) return
|
||||
|
||||
if (markAsReadWhenScrolling && lastRemovedItems) {
|
||||
if (isLoggedIn && markAsReadWhenScrolling && lastRemovedItems) {
|
||||
lastRemovedItems.forEach((item) => {
|
||||
unreadSyncService.markEntryAsRead(stableIdExtractor(item)).then(() => {
|
||||
setLastRemovedItems((prev) => {
|
||||
|
|
@ -72,13 +74,14 @@ export function useOnViewableItemsChanged({
|
|||
})
|
||||
}
|
||||
|
||||
if (markAsReadWhenRendering && lastViewableItems) {
|
||||
if (isLoggedIn && markAsReadWhenRendering && lastViewableItems) {
|
||||
lastViewableItems.forEach((item) => {
|
||||
unreadSyncService.markEntryAsRead(stableIdExtractor(item))
|
||||
})
|
||||
}
|
||||
}, [
|
||||
disabled,
|
||||
isLoggedIn,
|
||||
lastRemovedItems,
|
||||
lastViewableItems,
|
||||
markAsReadWhenRendering,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { MediaModel } from "@follow/database/schemas/types"
|
|||
import { useEntry } from "@follow/store/entry/hooks"
|
||||
import { getFeedById } from "@follow/store/feed/getter"
|
||||
import { unreadSyncService } from "@follow/store/unread/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import { uniqBy } from "es-toolkit/compat"
|
||||
import type { ImageSource } from "expo-image"
|
||||
|
|
@ -19,6 +20,7 @@ import { Text } from "@/src/components/ui/typography/Text"
|
|||
export function EntryPictureItem({ id }: { id: string }) {
|
||||
const { t } = useTranslation()
|
||||
const { openLightbox } = useLightboxControls()
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const aviRef = useAnimatedRef<View>()
|
||||
const item = useEntry(id, (state) => ({
|
||||
media: state.media,
|
||||
|
|
@ -81,7 +83,9 @@ export function EntryPictureItem({ id }: { id: string }) {
|
|||
})
|
||||
})()
|
||||
|
||||
unreadSyncService.markEntryAsRead(id)
|
||||
if (isLoggedIn) {
|
||||
unreadSyncService.markEntryAsRead(id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useEntry } from "@follow/store/entry/hooks"
|
|||
import { useFeedById } from "@follow/store/feed/hooks"
|
||||
import { useEntryTranslation } from "@follow/store/translation/hooks"
|
||||
import { unreadSyncService } from "@follow/store/unread/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import type { ImageSource } from "expo-image"
|
||||
import { memo, useCallback } from "react"
|
||||
|
|
@ -54,9 +55,12 @@ export const EntrySocialItem = memo(
|
|||
})
|
||||
const { openLightbox } = useLightboxControls()
|
||||
const feed = useFeedById(entry?.feedId || "")
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const navigation = useNavigation()
|
||||
const handlePress = useCallback(() => {
|
||||
unreadSyncService.markEntryAsRead(entryId)
|
||||
if (isLoggedIn) {
|
||||
unreadSyncService.markEntryAsRead(entryId)
|
||||
}
|
||||
tracker.navigateEntry({
|
||||
feedId: entry?.feedId ?? "",
|
||||
entryId,
|
||||
|
|
@ -66,7 +70,7 @@ export const EntrySocialItem = memo(
|
|||
entryIds: extraData.entryIds ?? [],
|
||||
view: FeedViewType.SocialMedia,
|
||||
})
|
||||
}, [entry?.feedId, entryId, extraData.entryIds, navigation])
|
||||
}, [entry?.feedId, entryId, extraData.entryIds, isLoggedIn, navigation])
|
||||
const autoExpandLongSocialMedia = useGeneralSettingKey("autoExpandLongSocialMedia")
|
||||
const navigationToFeedEntryList = useCallback(() => {
|
||||
if (!entry?.feedId) return
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { FeedViewType } from "@follow/constants"
|
||||
import { useEntry } from "@follow/store/entry/hooks"
|
||||
import { unreadSyncService } from "@follow/store/unread/store"
|
||||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import { formatDuration, transformVideoUrl } from "@follow/utils"
|
||||
import { memo, useMemo } from "react"
|
||||
|
|
@ -20,6 +21,7 @@ import { EntryGridFooter } from "../../entry-content/EntryGridFooter"
|
|||
|
||||
export const EntryVideoItem = memo(({ id }: { id: string }) => {
|
||||
const { t } = useTranslation()
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const item = useEntry(id, (state) => ({
|
||||
attachments: state.attachments,
|
||||
media: state.media,
|
||||
|
|
@ -45,7 +47,9 @@ export const EntryVideoItem = memo(({ id }: { id: string }) => {
|
|||
<ItemPressable
|
||||
itemStyle={ItemPressableStyle.Plain}
|
||||
onPress={() => {
|
||||
unreadSyncService.markEntryAsRead(id)
|
||||
if (isLoggedIn) {
|
||||
unreadSyncService.markEntryAsRead(id)
|
||||
}
|
||||
tracker.navigateEntry({
|
||||
feedId: item.feedId!,
|
||||
entryId: id,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { useIsLoggedIn } from "@follow/store/user/hooks"
|
||||
import type { FC } from "react"
|
||||
import { useEffect, useMemo, useRef, useState } from "react"
|
||||
import { View } from "react-native"
|
||||
|
|
@ -27,6 +28,7 @@ import { useEntries, useEntryListContext, useSelectedFeedTitle } from "./atoms"
|
|||
export function TimelineHeader({ feedId }: { feedId?: string }) {
|
||||
const viewTitle = useSelectedFeedTitle()
|
||||
const screenType = useEntryListContext().type
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
|
||||
const isFeed = screenType === "feed"
|
||||
const isTimeline = screenType === "timeline"
|
||||
|
|
@ -49,13 +51,13 @@ export function TimelineHeader({ feedId }: { feedId?: string }) {
|
|||
return () => (
|
||||
<View className="flex-row items-center justify-end">
|
||||
<ActionGroup>
|
||||
<UnreadOnlyActionButton />
|
||||
<MarkAllAsReadActionButton />
|
||||
{isLoggedIn && <UnreadOnlyActionButton />}
|
||||
{isLoggedIn && <MarkAllAsReadActionButton />}
|
||||
<FeedShareActionButton feedId={feedId} />
|
||||
</ActionGroup>
|
||||
</View>
|
||||
)
|
||||
}, [feedId])}
|
||||
}, [feedId, isLoggedIn])}
|
||||
headerHideableBottom={isTimeline || isSubscriptions ? TimelineViewSelector : undefined}
|
||||
headerHideableBottomHeight={TIMELINE_VIEW_SELECTOR_HEIGHT}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -199,9 +199,10 @@ const EntryInfo = ({ entryId }: { entryId: string }) => {
|
|||
publishedAt: state.publishedAt,
|
||||
feedId: state.feedId,
|
||||
}))
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const feed = useFeedById(entry?.feedId)
|
||||
const secondaryLabelColor = useColor("secondaryLabel")
|
||||
const readCount = useEntryReadHistory(entryId)?.entryReadHistories?.readCount ?? 0
|
||||
const readCount = useEntryReadHistory(entryId, 20, isLoggedIn)?.entryReadHistories?.readCount ?? 0
|
||||
const hideRecentReader = useUISettingKey("hideRecentReader")
|
||||
if (!entry) return null
|
||||
const { publishedAt } = entry
|
||||
|
|
@ -222,7 +223,7 @@ const EntryInfo = ({ entryId }: { entryId: string }) => {
|
|||
className="text-xs leading-tight text-secondary-label"
|
||||
/>
|
||||
</View>
|
||||
{!hideRecentReader && (
|
||||
{isLoggedIn && !hideRecentReader && (
|
||||
<View className="flex flex-row items-center gap-1">
|
||||
<Eye2CuteReIcon width={16} height={16} color={secondaryLabelColor} />
|
||||
<Text className="text-xs leading-tight text-secondary-label">{readCount}</Text>
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ export const useEntryIsInbox = (entryId: string) => {
|
|||
return useEntryStore(useCallback((state) => getEntryIsInboxSelector(state)(entryId), [entryId]))
|
||||
}
|
||||
|
||||
export const useEntryReadHistory = (entryId: string, size = 20) => {
|
||||
export const useEntryReadHistory = (entryId: string, size = 20, enabled = true) => {
|
||||
const isInboxEntry = useEntryIsInbox(entryId)
|
||||
const { data } = useQuery({
|
||||
queryKey: ["entry-read-history", entryId],
|
||||
|
|
@ -231,7 +231,7 @@ export const useEntryReadHistory = (entryId: string, size = 20) => {
|
|||
return entrySyncServices.fetchEntryReadHistory(entryId, size)
|
||||
},
|
||||
staleTime: 1000 * 60 * 5,
|
||||
enabled: !isInboxEntry,
|
||||
enabled: enabled && !isInboxEntry,
|
||||
})
|
||||
|
||||
return data
|
||||
|
|
|
|||
Loading…
Reference in New Issue