feat(mobile): MessageActionsSheet bottom modal — react/reply/delete
This commit is contained in:
@@ -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 },
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user