feat(business): enhance block editor with segment conversion and data persistence
- Implement `segmentsToBlocks` utility to convert message segments into editable blocks. - Update `BlockEditor` and `useBlockEditor` to support initial block loading and improved image upload handling. - Add logic to `CreatePostScreen` to automatically switch to long-post mode when image segments are detected and preserve data when toggling modes. - Update `AboutScreen` to make the ICP filing number clickable. - Refactor mapper imports to use the updated data model paths.
This commit is contained in:
@@ -42,6 +42,8 @@ import { uploadService } from '@/services/upload';
|
||||
import VoteEditor from '../../components/business/VoteEditor';
|
||||
import PostMentionInput from '../../components/business/PostMentionInput';
|
||||
import BlockEditor, { BlockEditorHandle } from '../../components/business/BlockEditor';
|
||||
import { segmentsToBlocks } from '../../components/business/BlockEditor/blocksToSegments';
|
||||
import type { EditorBlock } from '../../components/business/BlockEditor/blockEditorTypes';
|
||||
import { useResponsive, useResponsiveValue } from '../../hooks';
|
||||
import * as hrefs from '../../navigation/hrefs';
|
||||
import { MessageSegment } from '../../types';
|
||||
@@ -135,9 +137,10 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
const [showEmojiPanel, setShowEmojiPanel] = useState(false);
|
||||
const [posting, setPosting] = useState(false);
|
||||
const [loadingPost, setLoadingPost] = useState(false);
|
||||
|
||||
|
||||
// 长文模式:图片作为 image segment 嵌入文本
|
||||
const [isLongPostMode, setIsLongPostMode] = useState(false);
|
||||
const [initialBlocks, setInitialBlocks] = useState<EditorBlock[] | undefined>(undefined);
|
||||
|
||||
// 投票相关状态
|
||||
const [isVotePost, setIsVotePost] = useState(false);
|
||||
@@ -211,6 +214,13 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
setTitle(existingPost.title || '');
|
||||
setContent(existingPost.content || '');
|
||||
setImages((existingPost.images || []).map((img) => ({ uri: img.url, uploading: false })));
|
||||
|
||||
// 自动判断长文模式:segments 中包含图片块即为长文帖子
|
||||
const hasImageSegments = (existingPost.segments || []).some(s => s.type === 'image');
|
||||
if (hasImageSegments) {
|
||||
setIsLongPostMode(true);
|
||||
setInitialBlocks(segmentsToBlocks(existingPost.segments || []));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载待编辑帖子失败:', error);
|
||||
Alert.alert('错误', '加载帖子失败,请稍后重试');
|
||||
@@ -337,6 +347,33 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
setShowChannelPicker(false);
|
||||
};
|
||||
|
||||
// 切换长文/非长文模式时保留数据
|
||||
const handleToggleLongPostMode = useCallback(() => {
|
||||
if (!isLongPostMode) {
|
||||
// 非长文 -> 长文:将当前文本和图片转为 blocks
|
||||
const segs: MessageSegment[] = [];
|
||||
if (content.trim()) {
|
||||
segs.push({ type: 'text', data: { text: content } });
|
||||
}
|
||||
for (const img of images) {
|
||||
if (img.uri && !img.uploading) {
|
||||
segs.push({ type: 'image', data: { url: img.uri } });
|
||||
}
|
||||
}
|
||||
setInitialBlocks(segs.length > 0 ? segmentsToBlocks(segs) : undefined);
|
||||
} else {
|
||||
// 长文 -> 非长文:从 BlockEditor 提取文本和图片
|
||||
const editorContent = blockEditorRef.current?.getContent() || '';
|
||||
const editorSegments = blockEditorRef.current?.getSegments() || [];
|
||||
if (editorContent) setContent(editorContent);
|
||||
const editorImages = editorSegments
|
||||
.filter((s): s is Extract<typeof s, { type: 'image' }> => s.type === 'image')
|
||||
.map(s => ({ uri: s.data.url, uploading: false }));
|
||||
if (editorImages.length > 0) setImages(editorImages);
|
||||
}
|
||||
setIsLongPostMode(prev => !prev);
|
||||
}, [isLongPostMode, content, images]);
|
||||
|
||||
// 切换表情面板
|
||||
const handleToggleEmojiPanel = () => {
|
||||
const willShow = !showEmojiPanel;
|
||||
@@ -666,6 +703,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
isWideScreen && styles.contentInputWide,
|
||||
]}
|
||||
onContentChange={setContent}
|
||||
initialBlocks={initialBlocks}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
@@ -751,21 +789,19 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
{!isLongPostMode && (
|
||||
<TouchableOpacity
|
||||
style={styles.toolbarButton}
|
||||
onPress={handleTakePhoto}
|
||||
disabled={posting}
|
||||
>
|
||||
<View style={styles.toolbarButtonInner}>
|
||||
<MaterialCommunityIcons
|
||||
name="camera-outline"
|
||||
size={24}
|
||||
color={colors.text.secondary}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
style={styles.toolbarButton}
|
||||
onPress={isLongPostMode ? () => blockEditorRef.current?.insertCameraPhoto() : handleTakePhoto}
|
||||
disabled={posting}
|
||||
>
|
||||
<View style={styles.toolbarButtonInner}>
|
||||
<MaterialCommunityIcons
|
||||
name="camera-outline"
|
||||
size={24}
|
||||
color={colors.text.secondary}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.toolbarButton}
|
||||
@@ -799,7 +835,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
{/* 长文模式开关 */}
|
||||
<TouchableOpacity
|
||||
style={styles.toolbarButton}
|
||||
onPress={() => setIsLongPostMode(prev => !prev)}
|
||||
onPress={handleToggleLongPostMode}
|
||||
disabled={posting}
|
||||
>
|
||||
<View style={styles.toolbarButtonInner}>
|
||||
|
||||
@@ -300,6 +300,7 @@ function createAboutStyles(colors: AppColors) {
|
||||
fontSize: 12,
|
||||
color: colors.text.hint,
|
||||
marginTop: spacing.xs,
|
||||
textDecorationLine: 'underline',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -571,7 +572,7 @@ export const AboutScreen: React.FC = () => {
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text style={styles.copyright}>© 2026 青春之旅电子信息科技(威海)有限公司</Text>
|
||||
<Text style={styles.companyName}>鲁ICP备XXXXXXXX号-1</Text>
|
||||
<Text style={styles.companyName} onPress={() => Linking.openURL('https://beian.miit.gov.cn/')}>鲁ICP备2025144569号-2A</Text>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
Reference in New Issue
Block a user