Compare commits
No commits in common. "92f57595f0167c02c5572548ed47faa0d5b865f8" and "3d741441e271980e230097d52c87aefb0b6f0689" have entirely different histories.
92f57595f0
...
3d741441e2
|
|
@ -52,7 +52,7 @@ const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
|
||||||
const imgProps = {
|
const imgProps = {
|
||||||
style: [style, imageStyle],
|
style: [style, imageStyle],
|
||||||
ref,
|
ref,
|
||||||
// placeholder: blurhash,
|
placeholder: blurhash,
|
||||||
source: imageSource,
|
source: imageSource,
|
||||||
cachePolicy: 'memory-disk' as const,
|
cachePolicy: 'memory-disk' as const,
|
||||||
errorSource,
|
errorSource,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Image } from 'expo-image'
|
import { Image } from 'expo-image'
|
||||||
import { memo, useEffect, useMemo, useState } from 'react'
|
import { memo, useEffect, useState } from 'react'
|
||||||
import { ViewStyle } from 'react-native'
|
import { ViewStyle } from 'react-native'
|
||||||
import Video from 'react-native-video'
|
import Video from 'react-native-video'
|
||||||
|
|
||||||
|
|
@ -7,58 +7,19 @@ type Props = {
|
||||||
url?: string
|
url?: string
|
||||||
poster?: string
|
poster?: string
|
||||||
style?: ViewStyle
|
style?: ViewStyle
|
||||||
width?: number
|
|
||||||
} & React.ComponentProps<typeof Video>
|
} & React.ComponentProps<typeof Video>
|
||||||
|
|
||||||
const VideoBox = ({ url, poster, width = 120, style, ...videoProps }: Props) => {
|
const VideoBox = ({ url, poster, style, ...videoProps }: Props) => {
|
||||||
const [urlFinal, setUrlFinal] = useState('')
|
const [paused, setPaused] = useState(true)
|
||||||
|
|
||||||
const createUrl = (url: string) => {
|
|
||||||
return `https://modal-dev.bowong.cc/api/custom/video/converter/${encodeURI(url)}?options=compression_level=3,quality=70,loop=true,resolution=${width}x${width},fps=24`
|
|
||||||
}
|
|
||||||
|
|
||||||
const isImg = (url: any) => {
|
|
||||||
if (!url) return false
|
|
||||||
const lowerUrl = url.toLowerCase()
|
|
||||||
return lowerUrl?.match(/\.(jpg|jpeg|png|gif|webp|bmp|tiff|svg)(\?.*)?$/i)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resolveRedirect(url: string) {
|
|
||||||
const res = await fetch(url, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
Range: 'bytes=0-0',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return res.url
|
|
||||||
}
|
|
||||||
|
|
||||||
const setRedirectUrl = async (url?: string) => {
|
|
||||||
const isImg2 = isImg(url)
|
|
||||||
if (isImg2) {
|
|
||||||
setUrlFinal(url!)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const webpUrl = createUrl(url!)
|
|
||||||
const finalUrl = await resolveRedirect(webpUrl)
|
|
||||||
// console.log('finalUrl-----------', finalUrl)
|
|
||||||
setUrlFinal(finalUrl!)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!url) return
|
console.log('url--------', url);
|
||||||
setRedirectUrl(url!)
|
|
||||||
// const finalUrl = createUrl(url)
|
|
||||||
// console.log('finalUrl-----------', finalUrl)
|
|
||||||
|
|
||||||
// setUrlFinal(finalUrl)
|
setPaused(!Boolean(url))
|
||||||
return
|
|
||||||
}, [url])
|
}, [url])
|
||||||
|
|
||||||
// console.log('urlFinal--------- ', urlFinal, url)
|
const createUrl = (url: string)=>`https://modal-dev.bowong.cc/api/custom/video/converter/${encodeURI(url)}?options=compression_level=3,quality=70,loop=true,resolution=120x120,fps=24`
|
||||||
|
|
||||||
if (!url) return null
|
|
||||||
return (
|
return (
|
||||||
// 当 url 变化时通过 key 强制重载,确保自动播放生效
|
// 当 url 变化时通过 key 强制重载,确保自动播放生效
|
||||||
// <Video
|
// <Video
|
||||||
|
|
@ -75,7 +36,7 @@ const VideoBox = ({ url, poster, width = 120, style, ...videoProps }: Props) =>
|
||||||
// style={style as any}
|
// style={style as any}
|
||||||
// {...videoProps}
|
// {...videoProps}
|
||||||
// />
|
// />
|
||||||
<Image key={urlFinal} cachePolicy="memory-disk" source={{ uri: urlFinal }} style={style as any} />
|
<Image source={{ uri: url ? createUrl(url) : '' }} style={style as any} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Block, ConfirmModal, Text, Toast, VideoBox } from '@share/components'
|
||||||
import Img from '@share/components/Img'
|
import Img from '@share/components/Img'
|
||||||
import { AntDesign, FontAwesome, Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'
|
import { AntDesign, FontAwesome, Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'
|
||||||
import * as ImagePicker from 'expo-image-picker'
|
import * as ImagePicker from 'expo-image-picker'
|
||||||
import { Dimensions, Platform, ScrollView, View } from 'react-native'
|
import { Dimensions, ScrollView, View } from 'react-native'
|
||||||
import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated'
|
import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated'
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
import { imgPicker } from '@/@share/apis'
|
import { imgPicker } from '@/@share/apis'
|
||||||
|
|
@ -17,7 +17,7 @@ import { useFileUpload } from '@/hooks/actions/use-file-upload'
|
||||||
import { useTemplateActions } from '@/hooks/actions/use-template-actions'
|
import { useTemplateActions } from '@/hooks/actions/use-template-actions'
|
||||||
import { useAuth } from '@/hooks/core/use-auth'
|
import { useAuth } from '@/hooks/core/use-auth'
|
||||||
import { aniStorage } from '@/utils/aniStorage'
|
import { aniStorage } from '@/utils/aniStorage'
|
||||||
import * as FileSystem from 'expo-file-system'
|
import { get } from 'react-native/Libraries/TurboModule/TurboModuleRegistry'
|
||||||
|
|
||||||
// ============ 常量定义 ============
|
// ============ 常量定义 ============
|
||||||
const BACKGROUND_VIDEOS = [
|
const BACKGROUND_VIDEOS = [
|
||||||
|
|
@ -115,6 +115,11 @@ const GridItem = memo(
|
||||||
style={{ transform: [{ skewX: '-6deg' }], height: itemWidth, width: itemWidth }}
|
style={{ transform: [{ skewX: '-6deg' }], height: itemWidth, width: itemWidth }}
|
||||||
>
|
>
|
||||||
<Block style={{ height: itemWidth, width: itemWidth }}>
|
<Block style={{ height: itemWidth, width: itemWidth }}>
|
||||||
|
{/* {isVideoUrl(post.imageUrl) ? (
|
||||||
|
<VideoBox url={post.imageUrl} style={{ height: itemWidth, width: itemWidth }} />
|
||||||
|
) : (
|
||||||
|
<Img src={post.imageUrl} className="h-full w-full" />
|
||||||
|
)} */}
|
||||||
<Img src={post.imageUrl} className="h-full w-full" />
|
<Img src={post.imageUrl} className="h-full w-full" />
|
||||||
</Block>
|
</Block>
|
||||||
{isSelected && <Block className="absolute inset-[0px] border-[3px] border-accent" />}
|
{isSelected && <Block className="absolute inset-[0px] border-[3px] border-accent" />}
|
||||||
|
|
@ -288,17 +293,23 @@ const TopCircleSection = memo(
|
||||||
)
|
)
|
||||||
|
|
||||||
const GalleryRenderer = memo(({ selectedItem }: { selectedItem: any }) => {
|
const GalleryRenderer = memo(({ selectedItem }: { selectedItem: any }) => {
|
||||||
const url = selectedItem?.url || selectedItem.imageUrl
|
const uri = selectedItem?.url || selectedItem.imageUrl
|
||||||
console.log('GalleryRenderer------------', url)
|
console.log('GalleryRenderer------------', uri)
|
||||||
|
|
||||||
if (!url) return null
|
if (!uri) return null
|
||||||
|
|
||||||
const Width = 256
|
const Width = 256
|
||||||
return (
|
if (isVideoUrl(uri)) {
|
||||||
<View style={{ width: Width, height: Width, borderRadius: Width, overflow: 'hidden' }} className="relative z-[10] border-[4px] border-black">
|
return (
|
||||||
<VideoBox width={Width} url={url} style={{ width: Width, height: Width }} />
|
<View style={{ width: Width, height: Width, borderRadius: Width, overflow: 'hidden' }} className="relative z-[10] border-[4px] border-black">
|
||||||
</View>
|
<VideoBox url={uri} style={{ width: Width, height: Width }} />
|
||||||
)
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (selectedItem?.type?.includes('video')) {
|
||||||
|
return <VideoBox url={uri} style={{ height: 256, width: 256 }} />
|
||||||
|
}
|
||||||
|
return <Img src={uri} className="h-full w-full" />
|
||||||
})
|
})
|
||||||
|
|
||||||
const ManagerView = memo(
|
const ManagerView = memo(
|
||||||
|
|
@ -335,11 +346,26 @@ const ManagerView = memo(
|
||||||
)
|
)
|
||||||
|
|
||||||
const BackgroundBanner = memo(({ selectedItem }: { selectedItem: any }) => {
|
const BackgroundBanner = memo(({ selectedItem }: { selectedItem: any }) => {
|
||||||
const url = selectedItem?.url || selectedItem.imageUrl
|
const uri = selectedItem?.url || selectedItem.imageUrl
|
||||||
console.log('BackgroundBanner------------', url)
|
// if (!uri) return null
|
||||||
|
console.log('BackgroundBanner----', uri)
|
||||||
|
|
||||||
|
// if (isVideoUrl(uri)) {
|
||||||
|
// return (
|
||||||
|
// <Block className="absolute inset-0 bottom-0 left-0 right-0 top-0 z-[0] bg-red-400">
|
||||||
|
// <VideoBox
|
||||||
|
// url={'https://cdn.roasmax.cn/upload/c6d501f816a24354ab652d2b9083a32b.mp4'}
|
||||||
|
// viewType={1}
|
||||||
|
// style={{ width: screenWidth, height: screenHeight }}
|
||||||
|
// />
|
||||||
|
// <Block className="absolute inset-0 bg-black/0" />
|
||||||
|
// </Block>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
return (
|
return (
|
||||||
<Block className="absolute inset-0 bottom-0 left-0 right-0 top-0 z-[10] overflow-hidden">
|
<Block className="absolute inset-0 bottom-0 left-0 right-0 top-0 z-[10] overflow-hidden">
|
||||||
<VideoBox width={512} url={url} style={{ width: screenWidth, height: screenHeight }} />
|
{/* <VideoBox url={BACKGROUND_VIDEOS[0]} style={{ width: screenWidth, height: screenHeight }} /> */}
|
||||||
|
<Img src={bgGif} style={{ width: screenWidth, height: screenHeight }} />
|
||||||
</Block>
|
</Block>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
@ -395,20 +421,6 @@ const Sync = () => {
|
||||||
}))
|
}))
|
||||||
}, [generationsData, user])
|
}, [generationsData, user])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!selectedItem?.id && posts.length > 0) {
|
|
||||||
const firstItem = posts[0]
|
|
||||||
|
|
||||||
const newItem = {
|
|
||||||
id: firstItem.id,
|
|
||||||
imageUrl: firstItem.imageUrl,
|
|
||||||
url: firstItem.imageUrl,
|
|
||||||
originalUrl: firstItem.originalUrl,
|
|
||||||
}
|
|
||||||
setSelectedItem(newItem)
|
|
||||||
}
|
|
||||||
}, [posts])
|
|
||||||
|
|
||||||
const viewableIds = useRef<Set<string>>(new Set(posts.map((p: any) => p.id)))
|
const viewableIds = useRef<Set<string>>(new Set(posts.map((p: any) => p.id)))
|
||||||
|
|
||||||
// 动画效果
|
// 动画效果
|
||||||
|
|
@ -493,23 +505,23 @@ const Sync = () => {
|
||||||
|
|
||||||
const handlePick = useCallback(async () => {
|
const handlePick = useCallback(async () => {
|
||||||
const assetList = await imgPicker({ maxImages: 1, type: ImagePicker.MediaTypeOptions.All, resultType: 'asset' })
|
const assetList = await imgPicker({ maxImages: 1, type: ImagePicker.MediaTypeOptions.All, resultType: 'asset' })
|
||||||
|
|
||||||
if (!assetList?.length) return
|
if (!assetList?.length) return
|
||||||
|
|
||||||
const result = assetList[0] as any
|
const asset = assetList[0]
|
||||||
|
const isVideo = typeof asset === 'object' && asset?.type === 'video'
|
||||||
|
const uri = typeof asset === 'object' ? asset?.uri : asset
|
||||||
|
const fileName = typeof asset === 'object' && asset?.fileName ? asset.fileName : `upload_${Date.now()}.${isVideo ? 'mp4' : 'jpg'}`
|
||||||
|
|
||||||
Toast.showLoading({ title: '上传中...', duration: 30e3 })
|
Toast.showLoading({ title: '上传中...', duration: 30e3 })
|
||||||
|
|
||||||
const file = {
|
// 上传到云端
|
||||||
name: result.fileName,
|
const fileBlob = await fetch(uri).then((r) => r.blob())
|
||||||
type: result.mimeType || 'image/jpeg',
|
const mimeType = fileBlob.type || (isVideo ? 'video/mp4' : 'image/jpeg')
|
||||||
uri: Platform.OS === 'android' ? result.uri : result.uri.replace('file://', ''),
|
|
||||||
}
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('file', file as any)
|
|
||||||
|
|
||||||
const { url, error } = await uploadFile(file as any)
|
const file = new File([fileBlob], fileName, { type: mimeType })
|
||||||
|
|
||||||
|
const { url, error } = await uploadFile(file)
|
||||||
|
console.log({ error })
|
||||||
Toast.hideLoading()
|
Toast.hideLoading()
|
||||||
|
|
||||||
if (error || !url) {
|
if (error || !url) {
|
||||||
|
|
@ -519,11 +531,11 @@ const Sync = () => {
|
||||||
|
|
||||||
const newItem = {
|
const newItem = {
|
||||||
id: `local-${Date.now()}`,
|
id: `local-${Date.now()}`,
|
||||||
// type: isVideo ? 'video' : 'image',
|
type: isVideo ? 'video' : 'image',
|
||||||
imageUrl: url,
|
imageUrl: url,
|
||||||
url,
|
url,
|
||||||
originalUrl: url,
|
originalUrl: url,
|
||||||
// ...(typeof asset === 'object' ? asset : {}),
|
...(typeof asset === 'object' ? asset : {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedItem(newItem)
|
setSelectedItem(newItem)
|
||||||
|
|
@ -675,7 +687,7 @@ const Sync = () => {
|
||||||
viewableIds.current = new Set(viewableItems.map((v: any) => v.item.id))
|
viewableIds.current = new Set(viewableItems.map((v: any) => v.item.id))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const renderHeader = useMemo(
|
const renderHeaderFlatList = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<Block className="z-10">
|
<Block className="z-10">
|
||||||
<HeaderBanner connectedDevice={connectedDevice} onPick={handlePick} />
|
<HeaderBanner connectedDevice={connectedDevice} onPick={handlePick} />
|
||||||
|
|
@ -721,7 +733,7 @@ const Sync = () => {
|
||||||
getItemType={() => 'row'}
|
getItemType={() => 'row'}
|
||||||
removeClippedSubviews
|
removeClippedSubviews
|
||||||
keyExtractor={(item: any) => item?.id}
|
keyExtractor={(item: any) => item?.id}
|
||||||
ListHeaderComponent={renderHeader}
|
ListHeaderComponent={renderHeaderFlatList}
|
||||||
renderItem={renderGridItem}
|
renderItem={renderGridItem}
|
||||||
ItemSeparatorComponent={() => <Block style={{ height: 6 }} />}
|
ItemSeparatorComponent={() => <Block style={{ height: 6 }} />}
|
||||||
contentContainerStyle={{ paddingHorizontal: 12, paddingBottom: 200 }}
|
contentContainerStyle={{ paddingHorizontal: 12, paddingBottom: 200 }}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { Stack, useRouter } from 'expo-router'
|
||||||
import Alipay from 'expo-native-alipay'
|
import Alipay from 'expo-native-alipay'
|
||||||
import ExpoWeChat from 'expo-wechat'
|
import ExpoWeChat from 'expo-wechat'
|
||||||
import { alipay } from '@/lib/auth'
|
import { alipay } from '@/lib/auth'
|
||||||
import { ANDROID_ID, IOS_UNIVERSAL_LINK, SCHEME } from '@/app.constants'
|
import { ANDROID_ID, IOS_UNIVERSAL_LINK } from '@/app.constants'
|
||||||
import { useUserBalance } from '@/hooks/core'
|
import { useUserBalance } from '@/hooks/core'
|
||||||
|
|
||||||
const { width: SCREEN_WIDTH } = Dimensions.get('window')
|
const { width: SCREEN_WIDTH } = Dimensions.get('window')
|
||||||
|
|
@ -44,7 +44,7 @@ export default function ChargePage() {
|
||||||
|
|
||||||
console.log('initpay ---------', Alipay)
|
console.log('initpay ---------', Alipay)
|
||||||
|
|
||||||
Alipay.setAlipayScheme(SCHEME)
|
Alipay.setAlipayScheme(ANDROID_ID)
|
||||||
// ⚠️ 目前不可用,设置支付宝沙箱环境,仅 Android 支持
|
// ⚠️ 目前不可用,设置支付宝沙箱环境,仅 Android 支持
|
||||||
Alipay.setAlipaySandbox(true)
|
Alipay.setAlipaySandbox(true)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue