Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/mobile/assets/icons/compose.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!-- Tabler Icons edit (MIT), matching AppSymbol's square.and.pencil.
Strokes expanded to fills: Expo UI's vector parser does not support strokes. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="#000000"
android:pathData="M7,6 L6,6 C4.3431,6 3,7.3431 3,9 L3,18 C3,19.6569 4.3431,21 6,21 L15,21 C16.6569,21 18,19.6569 18,18 L18,17 C18,16.4477 17.5523,16 17,16 C16.4477,16 16,16.4477 16,17 L16,18 C16,18.5523 15.5523,19 15,19 L6,19 C5.4477,19 5,18.5523 5,18 L5,9 C5,8.4477 5.4477,8 6,8 L7,8 C7.5523,8 8,7.5523 8,7 C8,6.4477 7.5523,6 7,6 Z M21.0921,5.8779 L21.092,5.8778 L20.3849,6.5849 L21.092,7.292 C22.3027,6.0814 22.3027,4.1186 21.092,2.908 C19.8814,1.6973 17.9186,1.6973 16.708,2.908 L8.2942,11.2916 C8.1059,11.4793 8,11.7342 8,12 L8,15 C8,15.5523 8.4477,16 9,16 L12,16 C12.2658,16 12.5207,15.8942 12.7084,15.7058 L21.0934,7.2908 C21.4832,6.8996 21.4821,6.2665 21.0908,5.8766 C20.6996,5.4868 20.0665,5.4879 19.6766,5.8792 L11.2916,14.2942 L12,14 L9,14 L10,15 L10,12 L9.7058,12.7084 L18.1209,4.3235 C18.5518,3.8926 19.2482,3.8926 19.6778,4.3222 C20.1074,4.7518 20.1074,5.4482 19.6778,5.8778 C19.2873,6.2683 19.2873,6.9015 19.6778,7.292 L19.6779,7.2921 C20.0684,7.6826 20.7016,7.6826 21.0921,7.2921 C21.4826,6.9016 21.4826,6.2684 21.0921,5.8779 Z M15.2929,5.7071 L18.2929,8.7071 C18.6834,9.0976 19.3166,9.0976 19.7071,8.7071 C20.0976,8.3166 20.0976,7.6834 19.7071,7.2929 L16.7071,4.2929 C16.3166,3.9024 15.6834,3.9024 15.2929,4.2929 C14.9024,4.6834 14.9024,5.3166 15.2929,5.7071 Z" />
</vector>
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { ComponentProps } from "react";
import { MaterialFloatingActionButton } from "./MaterialFloatingActionButton.android";
import { MaterialScrollComposeButton } from "./MaterialScrollComposeButton.android";
import type { MaterialNewThreadButton as SharedMaterialNewThreadButton } from "./MaterialNewThreadButton.shared";

export function MaterialNewThreadButton(
props: ComponentProps<typeof SharedMaterialNewThreadButton>,
) {
if (props.extended && props.expanded !== undefined) {
return <MaterialScrollComposeButton {...props} expanded={props.expanded} />;
}
return (
<MaterialFloatingActionButton
{...props}
Expand Down
75 changes: 75 additions & 0 deletions apps/mobile/src/components/MaterialScrollComposeButton.android.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Box, ExtendedFloatingActionButton, Host, Icon, Text } from "@expo/ui/jetpack-compose";
import { fillMaxWidth, onSizeChanged, size } from "@expo/ui/jetpack-compose/modifiers";
import { useCallback, useState } from "react";
import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
import { useAppearancePreferences } from "../features/settings/appearance/AppearancePreferencesProvider";
import { useScaledTextRole } from "../features/settings/appearance/useScaledTextRole";

/** Keep the animated width and icon positioning entirely inside Compose, not Yoga. */
export function MaterialScrollComposeButton(props: {
readonly expanded: boolean;
readonly onPress: () => void;
readonly className?: string;
readonly style?: StyleProp<ViewStyle>;
}) {
const { themeAppearance, themeVariables: colors } = useAppearancePreferences();
const typography = useScaledTextRole("footnote");
const [expandedWidth, setExpandedWidth] = useState(56);
const rememberWidth = useCallback(({ width }: { width: number }) => {
setExpandedWidth((previous) => Math.max(previous, width));
}, []);
return (
<View pointerEvents="box-none" className={props.className} style={[props.style, { left: 20 }]}>
<View pointerEvents="none" importantForAccessibility="no-hide-descendants">
<Host
matchContents={{ vertical: true }}
colorScheme={themeAppearance}
ignoreSafeAreaKeyboardInsets
style={{ width: "100%" }}
>
<Box modifiers={[fillMaxWidth()]} contentAlignment="centerEnd">
<ExtendedFloatingActionButton
expanded={props.expanded}
containerColor={colors["--color-primary"]}
modifiers={[onSizeChanged(rememberWidth)]}
>
<ExtendedFloatingActionButton.Icon>
<Box modifiers={[size(24, 24)]}>
<Icon
source={require("../../assets/icons/compose.xml")}
size={24}
tint={colors["--color-primary-foreground"]}
/>
</Box>
</ExtendedFloatingActionButton.Icon>
<ExtendedFloatingActionButton.Text>
<Text
color={colors["--color-primary-foreground"]}
style={{ ...typography, fontWeight: "500" }}
>
New thread
</Text>
</ExtendedFloatingActionButton.Text>
</ExtendedFloatingActionButton>
</Box>
</Host>
</View>
{/* The visual host is wider than the button; only this target intercepts list touches. */}
<Pressable
onPress={props.onPress}
accessibilityRole="button"
accessibilityLabel="New thread"
android_ripple={{ foreground: true }}
style={{
position: "absolute",
right: 0,
top: 0,
bottom: 0,
width: props.expanded ? expandedWidth : 56,
borderRadius: 16,
overflow: "hidden",
}}
/>
</View>
);
}
Loading