XuqmGroup-RNSDK/packages/common/src/components/ScaledImage.tsx
XuqmGroup d7e7cd8e2d feat: async remote init, file upload in SDK, device info, ScaledImage, DB v2
- XuqmSDK.initialize(appId, serverUrl): fetches config from tenant platform
- ImSDK: sendImageMessage/sendVideoMessage/sendAudioMessage/sendFileMessage
- upload.ts: FormData upload to file-service with Bearer auth
- device.ts: deviceId (UUID), brand→pushVendor detection, platform info
- ScaledImage component: aspect-ratio bounded image rendering
- WatermelonDB schema v2: is_muted, is_pinned on conversations
- subscribeConversations: reactive WatermelonDB observable
- searchMessages: keyword/date/chat-type/target filtering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 16:41:19 +08:00

117 行
2.9 KiB
TypeScript

import React, { useState, useEffect } from 'react'
import { Image, View, StyleSheet } from 'react-native'
interface Props {
uri: string
originalWidth?: number
originalHeight?: number
minWidth?: number
maxWidth?: number
minHeight?: number
maxHeight?: number
borderRadius?: number
style?: object
}
function computeDimensions(
srcWidth: number,
srcHeight: number,
minWidth: number,
maxWidth: number,
minHeight: number,
maxHeight: number,
): { width: number; height: number } {
if (srcWidth <= 0 || srcHeight <= 0) {
return { width: minWidth, height: minHeight }
}
const aspectRatio = srcWidth / srcHeight
// Start from maxWidth and compute height
let width = maxWidth
let height = width / aspectRatio
// If height exceeds maxHeight, clamp by height
if (height > maxHeight) {
height = maxHeight
width = height * aspectRatio
}
// Enforce minimums
if (width < minWidth) {
width = minWidth
height = width / aspectRatio
}
if (height < minHeight) {
height = minHeight
width = height * aspectRatio
}
// Final clamp to maximums after minimum adjustments
width = Math.min(width, maxWidth)
height = Math.min(height, maxHeight)
return { width: Math.round(width), height: Math.round(height) }
}
export function ScaledImage(props: Props): JSX.Element {
const {
uri,
originalWidth,
originalHeight,
minWidth = 120,
maxWidth = 240,
minHeight = 80,
maxHeight = 320,
borderRadius,
style,
} = props
const [dimensions, setDimensions] = useState<{ width: number; height: number } | null>(() => {
if (originalWidth !== undefined && originalHeight !== undefined) {
return computeDimensions(originalWidth, originalHeight, minWidth, maxWidth, minHeight, maxHeight)
}
return null
})
useEffect(() => {
if (originalWidth !== undefined && originalHeight !== undefined) {
setDimensions(
computeDimensions(originalWidth, originalHeight, minWidth, maxWidth, minHeight, maxHeight),
)
return
}
// Use Image.getSize to determine dimensions at runtime
Image.getSize(
uri,
(w, h) => {
setDimensions(computeDimensions(w, h, minWidth, maxWidth, minHeight, maxHeight))
},
() => {
// Fallback to min dimensions on error
setDimensions({ width: minWidth, height: minHeight })
},
)
}, [uri, originalWidth, originalHeight, minWidth, maxWidth, minHeight, maxHeight])
if (!dimensions) {
// Placeholder while size is being resolved
return <View style={[styles.placeholder, { width: minWidth, height: minHeight, borderRadius }, style]} />
}
return (
<Image
source={{ uri }}
style={[{ width: dimensions.width, height: dimensions.height, borderRadius }, style]}
resizeMode="cover"
/>
)
}
const styles = StyleSheet.create({
placeholder: {
backgroundColor: '#e0e0e0',
},
})