- 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>
117 行
2.9 KiB
TypeScript
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',
|
|
},
|
|
})
|