mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-04 16:06:41 +00:00
feat: open the composer as a centred panel on tablets
This commit is contained in:
@@ -394,6 +394,7 @@ export default function App() {
|
|||||||
// cached mail list instead of the "Restoring session" spinner; the real
|
// cached mail list instead of the "Restoring session" spinner; the real
|
||||||
// JMAP session comes up in the background.
|
// JMAP session comes up in the background.
|
||||||
const hasPersistedAccount = useAccountStore((state) => state.activeAccountId != null);
|
const hasPersistedAccount = useAccountStore((state) => state.activeAccountId != null);
|
||||||
|
const { isSplit } = useLayout();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!hasRestoredSession) {
|
if (!hasRestoredSession) {
|
||||||
@@ -824,10 +825,11 @@ export default function App() {
|
|||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="Compose"
|
name="Compose"
|
||||||
component={ComposeScreen}
|
component={ComposeScreen}
|
||||||
options={{
|
// A tablet shows the composer as a panel over the screen it came
|
||||||
presentation: 'modal',
|
// from, so that screen stays drawn behind the dimmed backdrop.
|
||||||
animation: 'slide_from_bottom',
|
options={isSplit
|
||||||
}}
|
? { presentation: 'transparentModal', animation: 'fade' }
|
||||||
|
: { presentation: 'modal', animation: 'slide_from_bottom' }}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen name="ContactDetail" component={ContactDetailScreen} />
|
<Stack.Screen name="ContactDetail" component={ContactDetailScreen} />
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
@@ -399,11 +399,15 @@ export default function ComposeScreen({ route, navigation }: Props) {
|
|||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
// Track the visible keyboard obstruction so the format bar stays above it.
|
// Track the visible keyboard obstruction so the format bar stays above it.
|
||||||
const kbObstruction = useKeyboardObstruction(insets.bottom, () => editorRef.current?.blur());
|
const kbObstruction = useKeyboardObstruction(insets.bottom, () => editorRef.current?.blur());
|
||||||
const { shortHeight } = useLayout();
|
const { shortHeight, isSplit } = useLayout();
|
||||||
const typingInShortWindow = shortHeight && kbObstruction > 0;
|
const typingInShortWindow = shortHeight && kbObstruction > 0;
|
||||||
// When the keyboard is up it covers the bottom safe area, so we don't
|
// When the keyboard is up it covers the bottom safe area, so we don't
|
||||||
// need to add it on top — pad by whichever is larger.
|
// need to add it on top — pad by whichever is larger.
|
||||||
const bottomPad = Math.max(kbObstruction, insets.bottom);
|
// On a tablet the composer is a centred panel that stops PANEL_MARGIN
|
||||||
|
// short of the screen bottom; the keyboard covers that much less of it.
|
||||||
|
const bottomPad = isSplit
|
||||||
|
? Math.max(0, kbObstruction - PANEL_MARGIN)
|
||||||
|
: Math.max(kbObstruction, insets.bottom);
|
||||||
const replyTo = route.params?.replyTo;
|
const replyTo = route.params?.replyTo;
|
||||||
const draft = route.params?.draft;
|
const draft = route.params?.draft;
|
||||||
const mode = route.params?.mode ?? 'compose';
|
const mode = route.params?.mode ?? 'compose';
|
||||||
@@ -2263,8 +2267,8 @@ export default function ComposeScreen({ route, navigation }: Props) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const composer = (
|
||||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
<SafeAreaView style={styles.container} edges={isSplit ? [] : ['top', 'left', 'right']}>
|
||||||
<View style={[styles.header, typingInShortWindow && styles.headerCompact]}>
|
<View style={[styles.header, typingInShortWindow && styles.headerCompact]}>
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={onClose}
|
onPress={onClose}
|
||||||
@@ -2851,12 +2855,38 @@ export default function ComposeScreen({ route, navigation }: Props) {
|
|||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
|
if (!isSplit) return composer;
|
||||||
|
// Tablet: a centred panel over the dimmed screen it was opened from.
|
||||||
|
return (
|
||||||
|
<View style={[styles.panelBackdrop, { paddingTop: insets.top + PANEL_MARGIN, paddingBottom: insets.bottom + PANEL_MARGIN }]}>
|
||||||
|
<View style={styles.panel}>{composer}</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gap between the tablet composer panel and the screen edges. */
|
||||||
|
const PANEL_MARGIN = 24;
|
||||||
|
|
||||||
function makeStyles(c: ThemePalette) {
|
function makeStyles(c: ThemePalette) {
|
||||||
return StyleSheet.create({
|
return StyleSheet.create({
|
||||||
container: { flex: 1, backgroundColor: c.background },
|
container: { flex: 1, backgroundColor: c.background },
|
||||||
flex: { flex: 1 },
|
flex: { flex: 1 },
|
||||||
|
panelBackdrop: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: PANEL_MARGIN,
|
||||||
|
},
|
||||||
|
panel: {
|
||||||
|
flex: 1,
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 760,
|
||||||
|
borderRadius: radius.lg,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: c.border,
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: c.background,
|
||||||
|
},
|
||||||
scrollContent: { flexGrow: 1 },
|
scrollContent: { flexGrow: 1 },
|
||||||
|
|
||||||
header: {
|
header: {
|
||||||
|
|||||||
Reference in New Issue
Block a user