refactor(Post, PostMapper, PostRepository, CreatePostScreen, HomeScreen): update communityId to channelId for improved clarity
- Renamed communityId to channelId across Post entity, PostMapper, and PostRepository for consistency and clarity. - Updated CreatePostScreen to include channel selection functionality, enhancing user experience when creating posts. - Adjusted HomeScreen to support filtering posts by channel, improving content organization. - Refactored related interfaces and services to align with the new channelId terminology, ensuring a cohesive codebase.
This commit is contained in:
@@ -19,14 +19,15 @@ import {
|
||||
Platform,
|
||||
Animated,
|
||||
Image,
|
||||
useWindowDimensions,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useRouter, useLocalSearchParams, useNavigation } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import * as ImagePicker from 'expo-image-picker';
|
||||
import { colors, spacing, fontSizes, borderRadius, shadows } from '../../theme';
|
||||
import { Text, Button, ResponsiveContainer, AppBackButton } from '../../components/common';
|
||||
import { postService, showPrompt, voteService } from '../../services';
|
||||
import { Text, ResponsiveContainer } from '../../components/common';
|
||||
import { channelService, postService, showPrompt, voteService } from '../../services';
|
||||
import { ApiError } from '../../services/api';
|
||||
import { uploadService } from '../../services/uploadService';
|
||||
import VoteEditor from '../../components/business/VoteEditor';
|
||||
@@ -40,6 +41,10 @@ interface CreatePostScreenProps {
|
||||
|
||||
const MAX_TITLE_LENGTH = 100;
|
||||
const MAX_CONTENT_LENGTH = 2000;
|
||||
type ChannelOption = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
// 表情面板高度
|
||||
const EMOJI_PANEL_HEIGHT = 280;
|
||||
@@ -88,13 +93,14 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
|
||||
// 响应式布局
|
||||
const { isWideScreen, width } = useResponsive();
|
||||
const { height: windowHeight } = useWindowDimensions();
|
||||
|
||||
const [title, setTitle] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
const [images, setImages] = useState<{ uri: string; uploading?: boolean }[]>([]);
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
const [tagInput, setTagInput] = useState('');
|
||||
const [showTagInput, setShowTagInput] = useState(false);
|
||||
const [channelOptions, setChannelOptions] = useState<ChannelOption[]>([]);
|
||||
const [selectedChannelId, setSelectedChannelId] = useState<string | null>(null);
|
||||
const [showChannelPicker, setShowChannelPicker] = useState(false);
|
||||
const [showEmojiPanel, setShowEmojiPanel] = useState(false);
|
||||
const [posting, setPosting] = useState(false);
|
||||
const [loadingPost, setLoadingPost] = useState(false);
|
||||
@@ -118,6 +124,11 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
? Math.min(width, 800) - spacing.lg * 2
|
||||
: width - spacing.lg * 2;
|
||||
const imageSize = (availableWidth - imageGap * (imagesPerRow - 1)) / imagesPerRow;
|
||||
const contentInputMinHeight = Math.max(
|
||||
isWideScreen ? 460 : 320,
|
||||
Math.floor(windowHeight * (isWideScreen ? 0.56 : 0.5))
|
||||
);
|
||||
const contentSectionMinHeight = Math.floor(windowHeight * (isWideScreen ? 0.68 : 0.62));
|
||||
|
||||
React.useEffect(() => {
|
||||
Animated.parallel([
|
||||
@@ -134,16 +145,19 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
]).start();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const loadChannels = async () => {
|
||||
const list = await channelService.list();
|
||||
setChannelOptions(list.map(item => ({ id: item.id, name: item.name })));
|
||||
};
|
||||
loadChannels();
|
||||
}, []);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerShown: true,
|
||||
title: isEditMode ? '编辑帖子' : '发布帖子',
|
||||
// 当作为 Modal 使用时,显示关闭按钮
|
||||
headerLeft: onClose ? () => (
|
||||
<AppBackButton onPress={onClose} icon="close" />
|
||||
) : undefined,
|
||||
headerShown: false,
|
||||
});
|
||||
}, [navigation, isEditMode, onClose]);
|
||||
}, [navigation]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isEditMode || !editPostID) {
|
||||
@@ -264,20 +278,11 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
setImages(images.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// 添加标签
|
||||
const handleAddTag = useCallback(() => {
|
||||
const tag = tagInput.trim().replace(/^#/, '');
|
||||
if (tag && !tags.includes(tag) && tags.length < 5) {
|
||||
setTags([...tags, tag]);
|
||||
setTagInput('');
|
||||
setShowTagInput(false);
|
||||
}
|
||||
}, [tagInput, tags]);
|
||||
|
||||
// 删除标签
|
||||
const handleRemoveTag = (tag: string) => {
|
||||
setTags(tags.filter(t => t !== tag));
|
||||
};
|
||||
// 选择频道(单选,可再次点击取消)
|
||||
const handleSelectChannel = useCallback((channelId: string) => {
|
||||
setSelectedChannelId(prev => (prev === channelId ? null : channelId));
|
||||
setShowChannelPicker(false);
|
||||
}, []);
|
||||
|
||||
// 插入表情
|
||||
const handleInsertEmoji = (emoji: string) => {
|
||||
@@ -290,7 +295,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
// 关闭所有面板
|
||||
const closeAllPanels = () => {
|
||||
setShowEmojiPanel(false);
|
||||
setShowTagInput(false);
|
||||
setShowChannelPicker(false);
|
||||
};
|
||||
|
||||
// 切换表情面板
|
||||
@@ -299,10 +304,10 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
setShowEmojiPanel(!showEmojiPanel);
|
||||
};
|
||||
|
||||
// 切换标签输入
|
||||
const handleToggleTagInput = () => {
|
||||
// 切换频道选择
|
||||
const handleToggleChannelPicker = () => {
|
||||
closeAllPanels();
|
||||
setShowTagInput(!showTagInput);
|
||||
setShowChannelPicker(!showChannelPicker);
|
||||
};
|
||||
|
||||
// 切换投票模式
|
||||
@@ -373,6 +378,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
title: title.trim(),
|
||||
content: content.trim(),
|
||||
images: imageUrls,
|
||||
channel_id: selectedChannelId || undefined,
|
||||
vote_options: validOptions,
|
||||
});
|
||||
showPrompt({
|
||||
@@ -405,6 +411,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
title: title.trim(),
|
||||
content: content.trim(),
|
||||
images: imageUrls,
|
||||
channel_id: selectedChannelId || undefined,
|
||||
});
|
||||
showPrompt({
|
||||
type: 'info',
|
||||
@@ -474,66 +481,80 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染标签
|
||||
const renderTags = () => {
|
||||
if (tags.length === 0 && !showTagInput) return null;
|
||||
// 渲染频道(单选)
|
||||
const renderChannelPicker = () => {
|
||||
if (!showChannelPicker) return null;
|
||||
|
||||
return (
|
||||
<View style={styles.tagsSection}>
|
||||
<View style={styles.tagsContainer}>
|
||||
{tags.map((tag, index) => (
|
||||
<View key={index} style={styles.tag}>
|
||||
<MaterialCommunityIcons name="pound" size={12} color={colors.primary.main} />
|
||||
<Text variant="caption" color={colors.primary.main} style={styles.tagText}>{tag}</Text>
|
||||
{/* 独立的删除按钮 */}
|
||||
{channelOptions.map((channel) => {
|
||||
const isSelected = selectedChannelId === channel.id;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.tagDeleteButton}
|
||||
onPress={() => handleRemoveTag(tag)}
|
||||
hitSlop={{ top: 5, right: 5, bottom: 5, left: 5 }}
|
||||
key={channel.id}
|
||||
style={[styles.tag, isSelected && styles.channelSelected]}
|
||||
onPress={() => handleSelectChannel(channel.id)}
|
||||
>
|
||||
<MaterialCommunityIcons name="close-circle" size={14} color={colors.primary.main} />
|
||||
<Text
|
||||
variant="caption"
|
||||
color={isSelected ? colors.primary.contrast : colors.text.secondary}
|
||||
style={styles.tagText}
|
||||
>
|
||||
{channel.name}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
))}
|
||||
{tags.length < 5 && showTagInput && (
|
||||
<View style={styles.tagInputWrapper}>
|
||||
<MaterialCommunityIcons name="pound" size={14} color={colors.primary.main} />
|
||||
<TextInput
|
||||
style={styles.tagInput}
|
||||
value={tagInput}
|
||||
onChangeText={setTagInput}
|
||||
placeholder="输入话题"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
onSubmitEditing={handleAddTag}
|
||||
returnKeyType="done"
|
||||
autoFocus
|
||||
maxLength={20}
|
||||
/>
|
||||
<TouchableOpacity onPress={() => { setTagInput(''); setShowTagInput(false); }} style={styles.tagCancelButton}>
|
||||
<MaterialCommunityIcons name="close" size={16} color={colors.text.secondary} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={handleAddTag} style={styles.tagConfirmButton}>
|
||||
<MaterialCommunityIcons name="check" size={16} color={colors.primary.main} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{tags.length < 5 && !showTagInput && (
|
||||
<TouchableOpacity style={styles.addTagButton} onPress={() => setShowTagInput(true)}>
|
||||
<MaterialCommunityIcons name="plus" size={14} color={colors.text.secondary} />
|
||||
<Text variant="caption" color={colors.text.secondary} style={styles.addTagText}>话题</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
{channelOptions.length === 0 && (
|
||||
<Text variant="caption" color={colors.text.hint}>暂无可用频道</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const selectedChannelName = React.useMemo(() => {
|
||||
if (!selectedChannelId) return '';
|
||||
return channelOptions.find(c => c.id === selectedChannelId)?.name || '';
|
||||
}, [selectedChannelId, channelOptions]);
|
||||
|
||||
const renderChannelEntryRow = () => (
|
||||
<TouchableOpacity
|
||||
style={styles.channelEntryRow}
|
||||
onPress={handleToggleChannelPicker}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text variant="body" color={colors.text.secondary} style={styles.channelEntryLabel}>
|
||||
发表至:
|
||||
</Text>
|
||||
<View style={styles.channelEntryValueWrap}>
|
||||
<Text
|
||||
variant="body"
|
||||
color={selectedChannelName ? colors.text.primary : colors.text.hint}
|
||||
style={styles.channelEntryValue}
|
||||
>
|
||||
{selectedChannelName || '选择频道'}
|
||||
</Text>
|
||||
<MaterialCommunityIcons name="chevron-right" size={18} color={colors.text.hint} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
// 渲染投票编辑器
|
||||
const renderVoteEditor = () => {
|
||||
if (!isVotePost) return null;
|
||||
|
||||
return (
|
||||
<View style={isWideScreen && styles.voteEditorWide}>
|
||||
<View style={styles.voteEditorHeaderRow}>
|
||||
<Text variant="caption" color={colors.text.secondary}>
|
||||
已插入投票
|
||||
</Text>
|
||||
<TouchableOpacity onPress={() => setIsVotePost(false)} activeOpacity={0.7}>
|
||||
<Text variant="caption" color={colors.primary.main}>移除投票</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<VoteEditor
|
||||
options={voteOptions}
|
||||
onAddOption={handleAddVoteOption}
|
||||
@@ -549,7 +570,12 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
|
||||
// 渲染内容输入区
|
||||
const renderContentSection = () => (
|
||||
<View style={styles.contentSection}>
|
||||
<View
|
||||
style={[
|
||||
styles.contentSection,
|
||||
contentSectionMinHeight ? { minHeight: contentSectionMinHeight } : null,
|
||||
]}
|
||||
>
|
||||
{/* 标题输入 */}
|
||||
<TextInput
|
||||
style={[
|
||||
@@ -569,6 +595,7 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
style={[
|
||||
styles.contentInput,
|
||||
isWideScreen && styles.contentInputWide,
|
||||
{ minHeight: contentInputMinHeight },
|
||||
]}
|
||||
value={content}
|
||||
onChangeText={setContent}
|
||||
@@ -579,6 +606,10 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
maxLength={MAX_CONTENT_LENGTH}
|
||||
onSelectionChange={(e) => setSelection(e.nativeEvent.selection)}
|
||||
/>
|
||||
|
||||
{/* 作为正文容器的子模块,追加在文本后面 */}
|
||||
{renderImageGrid()}
|
||||
{renderVoteEditor()}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -642,25 +673,6 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.toolbarButton}
|
||||
onPress={handleToggleTagInput}
|
||||
disabled={tags.length >= 5 || posting}
|
||||
>
|
||||
<View style={styles.toolbarButtonInner}>
|
||||
<MaterialCommunityIcons
|
||||
name="pound"
|
||||
size={24}
|
||||
color={tags.length >= 5 ? colors.text.disabled : (showTagInput ? colors.primary.main : colors.text.secondary)}
|
||||
/>
|
||||
{tags.length > 0 && (
|
||||
<View style={styles.tagBadge}>
|
||||
<Text style={styles.tagBadgeText}>{tags.length}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.toolbarButton}
|
||||
onPress={handleToggleEmojiPanel}
|
||||
@@ -690,36 +702,54 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
{/* 右侧发布按钮 */}
|
||||
<View style={styles.toolbarRight}>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.charCount}>
|
||||
const renderTopActionBar = () => (
|
||||
<View style={styles.topActionBar}>
|
||||
<TouchableOpacity
|
||||
onPress={() => (onClose ? onClose() : router.back())}
|
||||
style={styles.topActionButton}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text variant="body" color={colors.text.secondary}>取消</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.topActionCenter}>
|
||||
<Text variant="body" color={colors.text.primary} style={styles.topActionTitle}>
|
||||
{isEditMode ? '编辑帖子' : '发布帖子'}
|
||||
</Text>
|
||||
<Text variant="caption" color={colors.text.hint} style={styles.topActionCount}>
|
||||
{content.length}/{MAX_CONTENT_LENGTH}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.postButton,
|
||||
(!content.trim() || posting) && styles.postButtonDisabled
|
||||
]}
|
||||
onPress={handlePost}
|
||||
disabled={!content.trim() || posting}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{posting ? (
|
||||
<MaterialCommunityIcons name="loading" size={18} color={colors.primary.contrast} />
|
||||
) : (
|
||||
<Text variant="body" color={colors.primary.contrast} style={styles.postButtonText}>
|
||||
{isEditMode ? '保存' : '发布'}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={handlePost}
|
||||
disabled={!content.trim() || posting}
|
||||
style={[
|
||||
styles.topActionButton,
|
||||
styles.topActionButtonRight,
|
||||
(!content.trim() || posting) && styles.topActionButtonDisabled,
|
||||
]}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text
|
||||
variant="body"
|
||||
color={!content.trim() || posting ? colors.text.hint : colors.primary.main}
|
||||
style={styles.topActionPublishText}
|
||||
>
|
||||
{posting ? '发布中' : '发布'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
||||
// 渲染主内容
|
||||
const renderMainContent = () => (
|
||||
<>
|
||||
{renderTopActionBar()}
|
||||
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -727,17 +757,13 @@ export const CreatePostScreen: React.FC<CreatePostScreenProps> = (props) => {
|
||||
>
|
||||
{/* 内容输入区 */}
|
||||
{renderContentSection()}
|
||||
|
||||
{/* 投票编辑器 */}
|
||||
{renderVoteEditor()}
|
||||
|
||||
{/* 图片网格 */}
|
||||
{renderImageGrid()}
|
||||
|
||||
{/* 标签 */}
|
||||
{renderTags()}
|
||||
</ScrollView>
|
||||
|
||||
{renderChannelEntryRow()}
|
||||
|
||||
{/* 频道选择面板 */}
|
||||
{renderChannelPicker()}
|
||||
|
||||
{/* 表情面板 - 位于底部工具栏上方 */}
|
||||
{renderEmojiPanel()}
|
||||
|
||||
@@ -788,10 +814,48 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
paddingBottom: spacing.xl,
|
||||
},
|
||||
topActionBar: {
|
||||
height: 52,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: spacing.lg,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: colors.divider,
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
topActionButton: {
|
||||
minWidth: 44,
|
||||
height: 36,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
topActionButtonRight: {
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
topActionButtonDisabled: {
|
||||
opacity: 0.7,
|
||||
},
|
||||
topActionTitle: {
|
||||
fontSize: fontSizes.lg,
|
||||
fontWeight: '600',
|
||||
},
|
||||
topActionCenter: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
topActionCount: {
|
||||
marginTop: 2,
|
||||
fontSize: fontSizes.xs,
|
||||
},
|
||||
topActionPublishText: {
|
||||
fontWeight: '600',
|
||||
},
|
||||
// 内容输入区
|
||||
contentSection: {
|
||||
flexGrow: 1,
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingTop: spacing.md,
|
||||
},
|
||||
@@ -806,22 +870,21 @@ const styles = StyleSheet.create({
|
||||
fontSize: fontSizes.xl,
|
||||
},
|
||||
contentInput: {
|
||||
flexGrow: 1,
|
||||
fontSize: fontSizes.md,
|
||||
color: colors.text.primary,
|
||||
minHeight: 120,
|
||||
lineHeight: 22,
|
||||
paddingVertical: spacing.sm,
|
||||
},
|
||||
contentInputWide: {
|
||||
fontSize: fontSizes.lg,
|
||||
lineHeight: 26,
|
||||
minHeight: 150,
|
||||
},
|
||||
// 图片网格
|
||||
imageGrid: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingHorizontal: 0,
|
||||
paddingTop: spacing.md,
|
||||
gap: 8,
|
||||
},
|
||||
@@ -866,7 +929,11 @@ const styles = StyleSheet.create({
|
||||
// 标签
|
||||
tagsSection: {
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingTop: spacing.lg,
|
||||
paddingTop: spacing.md,
|
||||
paddingBottom: spacing.sm,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: colors.divider,
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
tagsContainer: {
|
||||
flexDirection: 'row',
|
||||
@@ -874,60 +941,19 @@ const styles = StyleSheet.create({
|
||||
gap: spacing.sm,
|
||||
},
|
||||
tag: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.primary.light + '15',
|
||||
borderRadius: borderRadius.full,
|
||||
paddingLeft: spacing.sm,
|
||||
paddingRight: spacing.xs,
|
||||
paddingVertical: spacing.xs,
|
||||
},
|
||||
tagText: {
|
||||
marginLeft: spacing.xs,
|
||||
fontWeight: '500',
|
||||
},
|
||||
tagDeleteButton: {
|
||||
marginLeft: spacing.xs,
|
||||
padding: 2,
|
||||
},
|
||||
tagInputWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: borderRadius.full,
|
||||
paddingLeft: spacing.sm,
|
||||
paddingRight: spacing.xs,
|
||||
paddingVertical: spacing.xs,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.main,
|
||||
},
|
||||
tagInput: {
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.primary,
|
||||
minWidth: 80,
|
||||
marginLeft: spacing.xs,
|
||||
padding: 0,
|
||||
},
|
||||
tagCancelButton: {
|
||||
padding: spacing.xs,
|
||||
marginRight: spacing.xs,
|
||||
},
|
||||
tagConfirmButton: {
|
||||
padding: spacing.xs,
|
||||
},
|
||||
addTagButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: borderRadius.full,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.xs,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.divider,
|
||||
borderStyle: 'dashed',
|
||||
},
|
||||
addTagText: {
|
||||
marginLeft: spacing.xs,
|
||||
channelSelected: {
|
||||
backgroundColor: colors.primary.main,
|
||||
borderColor: colors.primary.main,
|
||||
},
|
||||
tagText: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
// 投票编辑器宽屏样式
|
||||
voteEditorWide: {
|
||||
@@ -935,7 +961,36 @@ const styles = StyleSheet.create({
|
||||
alignSelf: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
voteEditorHeaderRow: {
|
||||
marginHorizontal: spacing.lg,
|
||||
marginTop: spacing.md,
|
||||
marginBottom: -spacing.sm,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
// 底部工具栏
|
||||
channelEntryRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingVertical: spacing.sm,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: colors.divider,
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
channelEntryLabel: {
|
||||
fontSize: fontSizes.md,
|
||||
},
|
||||
channelEntryValueWrap: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.xs,
|
||||
},
|
||||
channelEntryValue: {
|
||||
fontSize: fontSizes.md,
|
||||
},
|
||||
toolbar: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
@@ -951,11 +1006,6 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
gap: spacing.xs,
|
||||
},
|
||||
toolbarRight: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.md,
|
||||
},
|
||||
toolbarButton: {
|
||||
position: 'relative',
|
||||
},
|
||||
@@ -966,41 +1016,6 @@ const styles = StyleSheet.create({
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
tagBadge: {
|
||||
position: 'absolute',
|
||||
top: 4,
|
||||
right: 4,
|
||||
minWidth: 16,
|
||||
height: 16,
|
||||
borderRadius: 8,
|
||||
backgroundColor: colors.primary.main,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
tagBadgeText: {
|
||||
color: colors.primary.contrast,
|
||||
fontSize: fontSizes.xs,
|
||||
fontWeight: '600',
|
||||
},
|
||||
charCount: {
|
||||
fontSize: fontSizes.xs,
|
||||
},
|
||||
postButton: {
|
||||
backgroundColor: colors.primary.main,
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: borderRadius.full,
|
||||
minWidth: 64,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
postButtonDisabled: {
|
||||
backgroundColor: colors.text.disabled,
|
||||
},
|
||||
postButtonText: {
|
||||
fontWeight: '600',
|
||||
fontSize: fontSizes.sm,
|
||||
},
|
||||
// 表情面板
|
||||
emojiPanel: {
|
||||
height: EMOJI_PANEL_HEIGHT,
|
||||
|
||||
Reference in New Issue
Block a user