Skip to content

Commit

Permalink
fix: Android Menu selector (#1527)
Browse files Browse the repository at this point in the history
Added design system menu
Fixed splash screen
for dark mode
  • Loading branch information
alexrisch authored Jan 16, 2025
1 parent d404973 commit cf7cccb
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 103 deletions.
1 change: 1 addition & 0 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ LogBox.ignoreLogs([
'event="noNetwork', // ethers
"[Reanimated] Reading from `value` during component render. Please ensure that you do not access the `value` property or use `get` method of a shared value while React is rendering a component.",
"Attempted to import the module",
"Couldn't find real values for `KeyboardContext`. Please make sure you're inside of `KeyboardProvider` - otherwise functionality of `react-native-keyboard-controller` will not work. [Component Stack]",
]);

// This is the default configuration
Expand Down
38 changes: 23 additions & 15 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ const isProduction = !isDev && !isPreview;
const scheme = isDev
? "converse-dev"
: isPreview
? "converse-preview"
: "converse";
? "converse-preview"
: "converse";
const androidPackage = isDev
? "com.converse.dev"
: isPreview
? "com.converse.preview"
: "com.converse.prod";
? "com.converse.preview"
: "com.converse.prod";
const appDomainConverse = isDev
? "dev.converse.xyz"
: isPreview
? "preview.converse.xyz"
: "converse.xyz";
? "preview.converse.xyz"
: "converse.xyz";
const appDomainGetConverse = isDev
? "dev.getconverse.app"
: isPreview
? "preview.getconverse.app"
: "getconverse.app";
? "preview.getconverse.app"
: "getconverse.app";

export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
Expand All @@ -44,11 +44,6 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
orientation: "portrait",
icon: isProduction ? "./assets/icon.png" : "./assets/icon-preview.png",
userInterfaceStyle: "automatic",
splash: {
image: "./assets/splash.png",
resizeMode: "contain",
backgroundColor: "#ffffff",
},
updates: {
fallbackToCacheTimeout: 0,
url: "https://u.expo.dev/49a65fae-3895-4487-8e8a-5bd8bee3a401",
Expand Down Expand Up @@ -77,8 +72,8 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
googleServicesFile: isDev
? "./scripts/build/android/google-services/dev.json"
: isPreview
? "./scripts/build/android/google-services/preview.json"
: "./scripts/build/android/google-services/production.json",
? "./scripts/build/android/google-services/preview.json"
: "./scripts/build/android/google-services/production.json",
permissions: [
"INTERNET",
"READ_EXTERNAL_STORAGE",
Expand Down Expand Up @@ -225,6 +220,19 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
},
},
],
[
"expo-splash-screen",
{
image: "./assets/splash.png",
backgroundColor: "#ffffff",
dark: {
image: "./assets/splash-dark.png",
backgroundColor: "#000000",
},
imageWidth: 300,
},
],
"expo-secure-store",
[
"@sentry/react-native/expo",
{
Expand Down
66 changes: 66 additions & 0 deletions design-system/Menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useCallback, useMemo } from "react";
import {
MenuView,
NativeActionEvent,
MenuAction as RNMenuAction,
} from "@react-native-menu/menu";
import { Platform, StyleProp, ViewStyle } from "react-native";
import { Pressable } from "../Pressable";
import { useAppTheme } from "@/theme/useAppTheme";
import { Haptics } from "@/utils/haptics";
import { getInlinedItem } from "./Menu.utils";

export type IMenuAction = Omit<RNMenuAction, "titleColor" | "imageColor"> & {
color?: string;
};

type MenuProps = {
actions: IMenuAction[];
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
onPress?: (actionId: string) => void;
};

export const Menu = ({ children, actions, style, onPress }: MenuProps) => {
const { theme } = useAppTheme();

const themedActions: RNMenuAction[] = useMemo(() => {
return actions.map((action) => {
const themedAction = {
...action,
// AR: Right now Android will only show a background color of white, so don't set a titleColor which is android specific anyways
// This may be only an android 13 issue though, will either need to fix the library or wait for a fix
titleColor: action.color ?? theme.colors.global.primary,
imageColor:
action.color ??
(Platform.OS === "android" ? theme.colors.global.primary : undefined),
displayInline: action.displayInline,
};
return action.displayInline ? getInlinedItem(themedAction) : themedAction;
});
}, [actions, theme.colors.global.primary]);

const handlePress = useCallback(
({ nativeEvent }: NativeActionEvent) => {
Haptics.selectionAsync();
onPress?.(nativeEvent.event);
},
[onPress]
);

return (
<MenuView
onPressAction={handlePress}
actions={themedActions}
style={style}
shouldOpenOnLongPress={false}
>
<Pressable style={$pressable}>{children}</Pressable>
</MenuView>
);
};

const $pressable: ViewStyle = {
alignItems: "center",
justifyContent: "center",
};
19 changes: 19 additions & 0 deletions design-system/Menu/Menu.utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Platform } from "react-native";
import { MenuAction as RNMenuAction } from "@react-native-menu/menu";

/**
* Android will always make a sub menu item, so instead this will just return the item
* iOS seems to work fine with the item, so this will just return the item
* @param item RN Menu Action
* @returns RN Menu Action
*/
export const getInlinedItem = (item: RNMenuAction): RNMenuAction => {
if (Platform.OS === "ios") {
return {
title: "",
displayInline: true,
subactions: [item],
};
}
return item;
};
96 changes: 29 additions & 67 deletions features/conversation-list/conversation-list.screen-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ import { usePreferredName } from "@/hooks/usePreferredName";
import { translate } from "@/i18n";
import { useHeader } from "@/navigation/use-header";
import { useAppTheme } from "@/theme/useAppTheme";
import { Haptics } from "@/utils/haptics";
import { shortDisplayName } from "@/utils/str";
import { useAccountsProfiles } from "@/utils/useAccountsProfiles";
import { useNavigation } from "@react-navigation/native";
import React from "react";
import { Alert, Platform } from "react-native";
import {
ContextMenuButton,
MenuActionConfig,
} from "react-native-ios-context-menu";
import { Menu } from "@/design-system/Menu/Menu";

export function useHeaderWrapper() {
const { theme } = useAppTheme();
Expand Down Expand Up @@ -84,80 +80,46 @@ export function useHeaderWrapper() {
<Avatar size={theme.avatarSize.sm} />
</Center>
</Pressable>
<ContextMenuButton
// hitSlop={theme.spacing.sm} // Not working...
<Menu
style={{
paddingVertical: theme.spacing.sm, // TMP solution for the hitSlop not working
paddingRight: theme.spacing.sm, // TMP solution for the hitSlop not working
}}
isMenuPrimaryAction
onPressMenuItem={({ nativeEvent }) => {
Haptics.selectionAsync();
if (nativeEvent.actionKey === "all-chats") {
onPress={(actionId: string) => {
if (actionId === "all-chats") {
Alert.alert("Coming soon");
} else if (nativeEvent.actionKey === "new-account") {
} else if (actionId === "new-account") {
navigation.navigate("NewAccountNavigator");
} else if (nativeEvent.actionKey === "app-settings") {
} else if (actionId === "app-settings") {
Alert.alert("Coming soon");
}

// Pressed on an account
else {
setCurrentAccount(nativeEvent.actionKey, false);
setCurrentAccount(actionId, false);
}
}}
menuConfig={{
menuTitle: "",
menuItems: [
...accountsProfiles.map((profilePreferedName, index) => {
return {
actionKey: accounts[index],
actionTitle: shortDisplayName(profilePreferedName),
icon: {
iconType: "SYSTEM",
iconValue:
currentAccount === accounts[index]
? Platform.select({
default: "checkmark",
ios: "checkmark",
android: "checkmark",
})
: "",
},
} as MenuActionConfig;
}),
{
type: "menu",
menuTitle: "",
menuOptions: ["displayInline"],
menuItems: [
{
actionKey: "new-account",
actionTitle: translate("new_account"),
icon: {
iconType: "SYSTEM",
iconValue: iconRegistry["new-account-card"],
},
},
],
},
{
type: "menu",
menuTitle: "",
menuOptions: ["displayInline"],
menuItems: [
{
actionKey: "app-settings",
actionTitle: translate("App settings"),
icon: {
iconType: "SYSTEM",
iconValue: iconRegistry["settings"],
},
},
],
},
],
}}
actions={[
...accountsProfiles.map((profilePreferedName, index) => {
return {
id: accounts[index],
title: shortDisplayName(profilePreferedName),
image: currentAccount === accounts[index] ? "checkmark" : "",
};
}),
{
displayInline: true,
id: "new-account",
title: translate("new_account"),
image: iconRegistry["new-account-card"],
},
{
displayInline: true,
id: "app-settings",
title: translate("App settings"),
image: iconRegistry["settings"],
},
]}
>
<HStack
// {...debugBorder()}
Expand All @@ -181,7 +143,7 @@ export function useHeaderWrapper() {
/>
</Center>
</HStack>
</ContextMenuButton>
</Menu>
</HStack>
),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Center } from "@/design-system/Center";
import { Menu } from "@/design-system/Menu/Menu";
import { useConversationComposerStore } from "@/features/conversation/conversation-composer/conversation-composer.store-context";
import { getCurrentAccount } from "@data/store/accountsStore";
import { Icon } from "@design-system/Icon/Icon";
import { Pressable } from "@design-system/Pressable";
import { translate } from "@i18n";
import { MenuView } from "@react-native-menu/menu";
import { useAppTheme } from "@theme/useAppTheme";
import { uploadRemoteAttachment } from "@utils/attachment/uploadRemoteAttachment";
import {
Expand Down Expand Up @@ -97,13 +97,13 @@ export function AddAttachmentButton() {
}, [handleAttachmentSelected]);

return (
<MenuView
<Menu
style={{
margin: theme.spacing.xxxs,
alignSelf: "flex-end",
}}
onPressAction={async ({ nativeEvent }) => {
switch (nativeEvent.event) {
onPress={(actionId) => {
switch (actionId) {
case "camera":
openCamera();
break;
Expand All @@ -118,11 +118,6 @@ export function AddAttachmentButton() {
{
id: "mediaLibrary",
title: translate("photo_library"),
titleColor: theme.colors.text.primary,
imageColor: Platform.select({
ios: undefined,
android: theme.colors.text.primary,
}),
image: Platform.select({
ios: "square.and.arrow.up",
android: "ic_menu_share",
Expand All @@ -131,23 +126,15 @@ export function AddAttachmentButton() {
{
id: "camera",
title: translate("camera"),
titleColor: theme.colors.text.primary,
imageColor: Platform.select({
ios: undefined,
android: theme.colors.text.primary,
}),
image: Platform.select({
ios: "camera",
android: "ic_menu_camera",
}),
},
]}
shouldOpenOnLongPress={false}
>
<Pressable
<Center
style={{
alignItems: "center",
justifyContent: "center",
height: 36, // Value from Figma
width: 36, // Value from Figma
backgroundColor: theme.colors.fill.minimal,
Expand All @@ -159,7 +146,7 @@ export function AddAttachmentButton() {
icon="plus"
size={20} // Value from figma
/>
</Pressable>
</MenuView>
</Center>
</Menu>
);
}

0 comments on commit cf7cccb

Please sign in to comment.