Skip to content

Commit

Permalink
feat: support vxna node and fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
liaoxuan committed Jun 24, 2024
1 parent 548947c commit 495cbf9
Show file tree
Hide file tree
Showing 17 changed files with 2,483 additions and 2,256 deletions.
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
auto-install-peers=true
registry=https://registry.npmjs.org
24 changes: 22 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"expo": {
"name": "V2Fun",
"slug": "v2ex",
"version": "1.7.4",
"version": "1.7.5",
"scheme": "v2fun",
"jsEngine": "jsc",
"icon": "./assets/icon.png",
Expand All @@ -20,7 +20,27 @@
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.liaoliao666.v2ex",
"buildNumber": "1.7.4.3"
"buildNumber": "1.7.5.4",
"privacyManifests": {
"NSPrivacyAccessedAPITypes": [
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryUserDefaults",
"NSPrivacyAccessedAPITypeReasons": ["CA92.1"]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryFileTimestamp",
"NSPrivacyAccessedAPITypeReasons": ["DDA9.1"]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryDiskSpace",
"NSPrivacyAccessedAPITypeReasons": ["85F4.1"]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategorySystemBootTime",
"NSPrivacyAccessedAPITypeReasons": ["35F9.1"]
}
]
}
},
"android": {
"adaptiveIcon": {
Expand Down
10 changes: 5 additions & 5 deletions components/StyledImage/AnimatedImageOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import useLatest from '@/utils/useLatest'

let animatingImage = ''

const animatedImageListeners = new Set<() => void>()
const animatedListeners = new Set<() => void>()
export const isAnimatingImage = (uri: string) => uri === animatingImage
const setAnimatingImage = (nextAnimatedImage: string) => {
if (!isAnimatingImage(nextAnimatedImage)) {
animatingImage = nextAnimatedImage
animatedImageListeners.forEach(l => l())
animatedListeners.forEach(l => l())
}
}

Expand Down Expand Up @@ -42,11 +42,11 @@ export default function AnimatedImageOverlay({
}
}

animatedImageListeners.add(listener)
animatedListeners.add(listener)
return () => {
animatedImageListeners.delete(listener)
animatedListeners.delete(listener)

if (!animatedImageListeners.size) {
if (!animatedListeners.size) {
animatingImage = ''
}
}
Expand Down
24 changes: 16 additions & 8 deletions components/StyledImage/BaseImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ export function BaseImage({
...props,
source,
onLoad: ev => {
const nextImageResult = pick(ev.source, ['width', 'height', 'isAnimated'])
const nextImageResult = pick(ev.source, [
'width',
'height',
'isAnimated',
'mediaType',
])
if (!isEqual(result, nextImageResult)) {
imageResults.set(uri, nextImageResult)
if (!hasPassedSize) update()
Expand Down Expand Up @@ -91,13 +96,16 @@ export function BaseImage({
autoplay={isAnimating}
allowDownscaling={props.allowDownscaling ?? !isAnimating}
>
{isObject(result) && !isMiniImage && !!result?.isAnimated && (
<AnimatedImageOverlay
isAnimating={isAnimating}
update={update}
uri={uri}
/>
)}
{isObject(result) &&
!isMiniImage &&
!!result?.isAnimated &&
result?.mediaType === 'image/gif' && (
<AnimatedImageOverlay
isAnimating={isAnimating}
update={update}
uri={uri}
/>
)}
</ImageBackground>
)
}
Expand Down
7 changes: 6 additions & 1 deletion components/StyledImage/helper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { ViewStyle } from 'react-native'

type ImageResult =
| { width: number; height: number; isAnimated?: boolean }
| {
width: number
height: number
isAnimated?: boolean
mediaType: string | null
}
| 'error'
| 'refetching'

Expand Down
20 changes: 15 additions & 5 deletions components/placeholder/TopicDetailPlaceholder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Topic } from '@/servicies'
import tw from '@/utils/tw'

import NavBar from '../NavBar'
import StyledButton from '../StyledButton'
import StyledFade from './StyledFade'

function AvatarPlaceholder() {
Expand Down Expand Up @@ -53,11 +54,20 @@ export default function TopicDetailPlaceholder({
</View>

<View style={tw`flex-1`}>
<Text
style={tw`text-[${colors.foreground}] ${fontSize.large} font-semibold`}
>
{topic.member?.username}
</Text>
<View style={tw`flex flex-row gap-2`}>
<Text
key={'username'}
style={tw`text-[${colors.foreground}] ${fontSize.xlarge} font-semibold flex-shrink`}
numberOfLines={1}
>
{topic.member?.username}
</Text>
{!!topic.node?.title && (
<StyledButton size="mini" type="tag">
{topic.node?.title}
</StyledButton>
)}
</View>

<Text
key="reply_count"
Expand Down
27 changes: 20 additions & 7 deletions components/topic/TopicInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { getBaseURL } from '@/utils/url'
import Html from '../Html'
import IconButton from '../IconButton'
import Separator from '../Separator'
import StyledButton from '../StyledButton'
import StyledImage from '../StyledImage'

export default function TopicInfo({
Expand Down Expand Up @@ -75,13 +76,25 @@ export default function TopicInfo({
<View style={tw`flex-1`}>
<Separator style={tw`flex-nowrap`}>
{compact([
<Text
key={'username'}
style={tw`text-[${colors.foreground}] ${fontSize.xlarge} font-semibold flex-shrink`}
numberOfLines={1}
>
{topic.member?.username}
</Text>,
<View key={'username'} style={tw`flex flex-row gap-2`}>
<Text
style={tw`text-[${colors.foreground}] ${fontSize.xlarge} font-semibold flex-shrink`}
numberOfLines={1}
>
{topic.member?.username}
</Text>
{!!topic.node?.title && (
<StyledButton
size="mini"
type="tag"
onPress={() => {
navigation.push('NodeTopics', { name: topic.node?.name! })
}}
>
{topic.node?.title}
</StyledButton>
)}
</View>,
hasParsedText && (
<Text
key={'isParsed'}
Expand Down
135 changes: 135 additions & 0 deletions components/topic/XnaItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { useAtomValue } from 'jotai'
import { compact, isEqual } from 'lodash-es'
import { memo } from 'react'
import { Text, View } from 'react-native'

import { uiAtom } from '@/jotai/uiAtom'
import { getCurrentRouteName, navigation } from '@/navigation/navigationRef'
import { Xna } from '@/servicies'
import { isTablet } from '@/utils/tablet'
import tw from '@/utils/tw'
import useUpdate from '@/utils/useUpdate'

import DebouncedPressable from '../DebouncedPressable'
import Separator from '../Separator'
import StyledButton from '../StyledButton'
import StyledImage from '../StyledImage'

export interface XnaItemProps {
xna: Xna
hideAvatar?: boolean
}

export default memo(XnaItem, (prev, next) => isEqual(prev.xna, next.xna))

const readedXnaMap = new Map<string, boolean>()

function XnaItem({ xna, hideAvatar }: XnaItemProps) {
const update = useUpdate()
const { colors, fontSize } = useAtomValue(uiAtom)

return (
<DebouncedPressable
style={tw`px-4 py-3 flex-row bg-[${colors.base100}]`}
onPress={() => {
readedXnaMap.set(xna.id, true)
update()
if (isTablet() && getCurrentRouteName() === 'Webview') {
navigation.replace('Webview', { url: xna.id })
} else {
navigation.push('Webview', { url: xna.id })
}
}}
>
{!hideAvatar && (
<View>
<DebouncedPressable
onPress={() => {
navigation.push('MemberDetail', {
username: xna.member?.username!,
})
}}
style={tw`pr-3`}
>
<StyledImage
style={tw`w-6 h-6 rounded-full`}
source={xna.member?.avatar}
priority="high"
/>
</DebouncedPressable>
</View>
)}
<View style={tw`flex-1`}>
<View style={tw`flex-row gap-2`}>
<Text
style={tw`text-[${colors.foreground}] ${fontSize.medium} flex-shrink`}
numberOfLines={1}
onPress={() => {
navigation.push('MemberDetail', {
username: xna.member?.username!,
})
}}
>
{xna.member?.username}
</Text>

{!!xna.node?.title && (
<StyledButton
size="mini"
type="tag"
onPress={() => {
if (isTablet() && getCurrentRouteName() === 'Webview') {
navigation.replace('Webview', { url: xna.node?.name! })
} else {
navigation.push('Webview', { url: xna.node?.name! })
}
}}
>
{xna.node?.title}
</StyledButton>
)}

{xna.pin_to_top && (
<StyledButton size="mini" color={colors.danger} type="tag" ghost>
置顶
</StyledButton>
)}
</View>

<Text
style={tw.style(
`${fontSize.medium} pt-1 font-medium`,
readedXnaMap.has(xna.id)
? `text-[${colors.default}]`
: `text-[${colors.foreground}]`
)}
>
{xna.title}
</Text>

<Separator style={tw`mt-1`}>
{compact([
<Text
key="last_touched"
style={tw`text-[${colors.default}] ${fontSize.small}`}
>
{xna.last_touched}
</Text>,
!!xna.last_reply_by && (
<Text
key="last_reply_by"
style={tw`text-[${colors.foreground}] ${fontSize.small} flex-1`}
numberOfLines={1}
>
<Text style={tw`text-[${colors.default}] ${fontSize.small}`}>
最后回复于
</Text>
{xna.last_reply_by}
</Text>
),
])}
</Separator>
</View>
</DebouncedPressable>
)
}
14 changes: 8 additions & 6 deletions jotai/homeTabsAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { atom } from 'jotai'

import { atomWithAsyncStorage } from './utils/atomWithAsyncStorage'

export const RECENT_TAB_KEY = 'recent'
export const XNA_KEY = 'xna'

export type HomeTab = {
title: string
key: string
type: 'tab' | 'node'
type: 'tab' | 'node' | typeof RECENT_TAB_KEY | typeof XNA_KEY
}

export const RECENT_TAB_KEY = 'recent'

export const allTabs: HomeTab[] = [
{ title: '最近', key: RECENT_TAB_KEY },
export const allTabs = [
{ title: '最近', key: RECENT_TAB_KEY, type: RECENT_TAB_KEY },
{ title: '最热', key: 'hot' },
{ title: '技术', key: 'tech' },
{ title: '创意', key: 'creative' },
Expand All @@ -23,10 +24,11 @@ export const allTabs: HomeTab[] = [
{ title: '问与答', key: 'qna' },
{ title: '全部', key: 'all' },
{ title: 'R2', key: 'r2' },
{ title: 'VXNA', key: XNA_KEY, type: XNA_KEY },
{ title: '节点', key: 'nodes' },
{ title: '关注', key: 'members' },
{ title: '刚更新', key: 'changes' },
].map(item => ({ ...item, type: 'tab' }))
].map(item => ({ ...item, type: item.type ?? 'tab' })) as HomeTab[]

export const homeTabsAtom = atomWithAsyncStorage<HomeTab[]>('tabs', allTabs)

Expand Down
Loading

0 comments on commit 495cbf9

Please sign in to comment.