feat(mobile): MessageActionsSheet bottom modal — react/reply/delete

This commit is contained in:
byGalax
2026-05-14 00:20:08 +02:00
parent 8d7ed592b8
commit 3b62fbc243
@@ -0,0 +1,103 @@
import { Modal, Pressable, StyleSheet, Text } from 'react-native';
import { ReactionStrip } from './ReactionStrip';
import { colors } from '../theme/colors';
interface Props {
visible: boolean;
mine: boolean;
onClose: () => void;
onReact: (emoji: string) => void;
onReply: () => void;
onDelete: () => void;
}
// Bottom-sheet-style modal opened on long-press of a message bubble.
// Contains the reaction quick-strip plus the action rows. The "Löschen"
// row is hidden for messages not authored by the current user — the
// server-side trigger would refuse anyway, but trimming UI keeps the
// surface honest.
export function MessageActionsSheet({
visible,
mine,
onClose,
onReact,
onReply,
onDelete,
}: Props) {
return (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={onClose}
>
<Pressable style={styles.backdrop} onPress={onClose}>
<Pressable style={styles.sheet} onPress={() => undefined}>
<ReactionStrip
onReact={(e) => {
onReact(e);
onClose();
}}
/>
<Action label="Antworten" onPress={() => { onReply(); onClose(); }} />
{mine && (
<Action
label="Löschen"
danger
onPress={() => { onDelete(); onClose(); }}
/>
)}
<Action label="Abbrechen" muted onPress={onClose} />
</Pressable>
</Pressable>
</Modal>
);
}
function Action({
label,
danger,
muted,
onPress,
}: {
label: string;
danger?: boolean;
muted?: boolean;
onPress: () => void;
}) {
return (
<Pressable
onPress={onPress}
style={({ pressed }) => [styles.action, pressed && styles.actionPressed]}
>
<Text style={[styles.actionText, danger && styles.actionDanger, muted && styles.actionMuted]}>
{label}
</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
backdrop: {
flex: 1,
justifyContent: 'flex-end',
backgroundColor: 'rgba(0,0,0,0.5)',
},
sheet: {
backgroundColor: colors.surface,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
paddingBottom: 24,
},
action: {
paddingVertical: 14,
paddingHorizontal: 20,
borderBottomColor: colors.border,
borderBottomWidth: 1,
},
actionPressed: { backgroundColor: colors.bg },
actionText: { color: colors.text, fontSize: 15, fontWeight: '500' },
actionDanger: { color: colors.danger },
actionMuted: { color: colors.textMuted },
});