refactor(message): improve message synchronization and hook reliability
Refactor the message management system to address synchronization issues and improve performance through request deduplication and enhanced lifecycle management. - Implement in-flight promise tracking in `MessageSyncService` to prevent redundant network requests for conversations and messages. - Enhance `useMessages` hook with `useFocusEffect` to trigger automatic synchronization when a chat screen regains focus. - Add `forceSync` capability to `MessageManager` and `MessageSyncService` to allow manual overrides of loading states during re-entry. - Consolidate WebSocket synchronization logic in `WSMessageHandler` using a throttled and deduplicated trigger mechanism. - Clean up unused styles and deprecated hooks (`baseHooks.ts`, `bubbleStyles.ts`, `inputStyles.ts`). - Update `metro.config.js` and `package.json` to include `@expo/ui` and optimize resolver settings.
This commit is contained in:
@@ -1,317 +0,0 @@
|
||||
/**
|
||||
* 消息气泡样式工具
|
||||
* 参考 Element X 设计,实现动态圆角和现代化气泡样式
|
||||
*/
|
||||
|
||||
import { StyleSheet, ViewStyle, TextStyle } from 'react-native';
|
||||
import { spacing, type AppColors } from '../../../../theme';
|
||||
import { useChatSettingsStore } from '../../../../stores/settings';
|
||||
|
||||
// 默认圆角,但会从 store 读取
|
||||
export const BUBBLE_RADIUS = 16;
|
||||
|
||||
// 获取动态圆角值
|
||||
export function useBubbleRadius(): number {
|
||||
return useChatSettingsStore((s) => s.messageRadius);
|
||||
}
|
||||
|
||||
export function getBubbleColors(colors: AppColors, outgoingBubbleColor?: string) {
|
||||
return {
|
||||
outgoing: {
|
||||
background: outgoingBubbleColor || colors.chat.bubbleOutgoing,
|
||||
text: colors.chat.textPrimary,
|
||||
},
|
||||
incoming: {
|
||||
background: colors.chat.bubbleIncoming,
|
||||
text: colors.chat.textPrimary,
|
||||
},
|
||||
replyHighlight: {
|
||||
background: colors.chat.replyTint,
|
||||
borderLeft: colors.chat.replyBorder,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export type MessageGroupPosition = 'single' | 'first' | 'middle' | 'last';
|
||||
|
||||
// 使用动态圆角的 hook
|
||||
export function useBubbleBorderRadius(isMe: boolean, position: MessageGroupPosition): ViewStyle {
|
||||
const radius = useBubbleRadius();
|
||||
|
||||
if (isMe) {
|
||||
switch (position) {
|
||||
case 'single':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: 4,
|
||||
};
|
||||
case 'first':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: 4,
|
||||
};
|
||||
case 'middle':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: 4,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: 4,
|
||||
};
|
||||
case 'last':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: 4,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
switch (position) {
|
||||
case 'single':
|
||||
return {
|
||||
borderTopLeftRadius: 4,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
case 'first':
|
||||
return {
|
||||
borderTopLeftRadius: 4,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: 4,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
case 'middle':
|
||||
return {
|
||||
borderTopLeftRadius: 4,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: 4,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
case 'last':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保持原有的静态函数用于兼容
|
||||
export const getBubbleBorderRadius = (isMe: boolean, position: MessageGroupPosition): ViewStyle => {
|
||||
const radius = BUBBLE_RADIUS;
|
||||
|
||||
if (isMe) {
|
||||
switch (position) {
|
||||
case 'single':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: 4,
|
||||
};
|
||||
case 'first':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: 4,
|
||||
};
|
||||
case 'middle':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: 4,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: 4,
|
||||
};
|
||||
case 'last':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: 4,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
switch (position) {
|
||||
case 'single':
|
||||
return {
|
||||
borderTopLeftRadius: 4,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
case 'first':
|
||||
return {
|
||||
borderTopLeftRadius: 4,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: 4,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
case 'middle':
|
||||
return {
|
||||
borderTopLeftRadius: 4,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: 4,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
case 'last':
|
||||
return {
|
||||
borderTopLeftRadius: radius,
|
||||
borderTopRightRadius: radius,
|
||||
borderBottomLeftRadius: radius,
|
||||
borderBottomRightRadius: radius,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getMessageGroupPosition = (
|
||||
index: number,
|
||||
messages: Array<{ sender_id: string }>,
|
||||
currentUserId: string
|
||||
): MessageGroupPosition => {
|
||||
const currentMessage = messages[index];
|
||||
if (!currentMessage) return 'single';
|
||||
|
||||
const prevMessage = index > 0 ? messages[index - 1] : null;
|
||||
const nextMessage = index < messages.length - 1 ? messages[index + 1] : null;
|
||||
|
||||
const isSameSenderAsPrev = prevMessage && prevMessage.sender_id === currentMessage.sender_id;
|
||||
const isSameSenderAsNext = nextMessage && nextMessage.sender_id === currentMessage.sender_id;
|
||||
|
||||
if (!isSameSenderAsPrev && !isSameSenderAsNext) {
|
||||
return 'single';
|
||||
} else if (!isSameSenderAsPrev && isSameSenderAsNext) {
|
||||
return 'first';
|
||||
} else if (isSameSenderAsPrev && isSameSenderAsNext) {
|
||||
return 'middle';
|
||||
} else {
|
||||
return 'last';
|
||||
}
|
||||
};
|
||||
|
||||
export const shouldShowSenderInfo = (
|
||||
index: number,
|
||||
messages: Array<{ sender_id: string }>
|
||||
): boolean => {
|
||||
if (index === 0) return true;
|
||||
|
||||
const currentMessage = messages[index];
|
||||
const prevMessage = messages[index - 1];
|
||||
|
||||
return prevMessage.sender_id !== currentMessage.sender_id;
|
||||
};
|
||||
|
||||
export const getBubbleBaseStyle = (isMe: boolean, colors: AppColors, outgoingBubbleColor?: string): ViewStyle => {
|
||||
const bc = getBubbleColors(colors, outgoingBubbleColor);
|
||||
return {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm + 4,
|
||||
minWidth: 60,
|
||||
maxWidth: '75%',
|
||||
backgroundColor: isMe ? bc.outgoing.background : bc.incoming.background,
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.06,
|
||||
shadowRadius: 2,
|
||||
elevation: 2,
|
||||
};
|
||||
};
|
||||
|
||||
// 使用动态字号的 hook
|
||||
export function useChatFontSize(): number {
|
||||
return useChatSettingsStore((s) => s.fontSize);
|
||||
}
|
||||
|
||||
export const getBubbleTextStyle = (isMe: boolean, colors: AppColors, customFontSize?: number): TextStyle => {
|
||||
const bc = getBubbleColors(colors);
|
||||
const fontSize = customFontSize ?? 16;
|
||||
return {
|
||||
color: isMe ? bc.outgoing.text : bc.incoming.text,
|
||||
fontSize,
|
||||
lineHeight: Math.round(fontSize * 1.4),
|
||||
letterSpacing: 0.2,
|
||||
fontWeight: '400',
|
||||
};
|
||||
};
|
||||
|
||||
export function createBubbleStyles(colors: AppColors, customFontSize?: number, outgoingBubbleColor?: string) {
|
||||
const bc = getBubbleColors(colors, outgoingBubbleColor);
|
||||
const fontSize = customFontSize ?? 16;
|
||||
return StyleSheet.create({
|
||||
bubble: {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm + 4,
|
||||
minWidth: 60,
|
||||
},
|
||||
outgoing: {
|
||||
backgroundColor: bc.outgoing.background,
|
||||
},
|
||||
incoming: {
|
||||
backgroundColor: bc.incoming.background,
|
||||
},
|
||||
text: {
|
||||
fontSize,
|
||||
lineHeight: Math.round(fontSize * 1.4),
|
||||
letterSpacing: 0.2,
|
||||
fontWeight: '400',
|
||||
},
|
||||
shadow: {
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.06,
|
||||
shadowRadius: 2,
|
||||
elevation: 2,
|
||||
},
|
||||
longPressShadow: {
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: 3 },
|
||||
shadowOpacity: 0.12,
|
||||
shadowRadius: 8,
|
||||
elevation: 6,
|
||||
},
|
||||
replyHighlight: {
|
||||
borderLeftWidth: 3,
|
||||
borderLeftColor: colors.primary.main,
|
||||
backgroundColor: bc.replyHighlight.background,
|
||||
},
|
||||
recalled: {
|
||||
backgroundColor: colors.chat.surfaceMuted,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.chat.border,
|
||||
borderStyle: 'dashed',
|
||||
borderRadius: 12,
|
||||
},
|
||||
systemNotice: {
|
||||
alignItems: 'center',
|
||||
marginVertical: spacing.sm,
|
||||
},
|
||||
systemNoticeText: {
|
||||
fontSize: 12,
|
||||
color: colors.chat.textSecondary,
|
||||
backgroundColor: colors.chat.surfaceMuted,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.xs,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
BUBBLE_RADIUS,
|
||||
getBubbleColors,
|
||||
getBubbleBorderRadius,
|
||||
getMessageGroupPosition,
|
||||
shouldShowSenderInfo,
|
||||
getBubbleBaseStyle,
|
||||
getBubbleTextStyle,
|
||||
createBubbleStyles,
|
||||
};
|
||||
Reference in New Issue
Block a user