Skip to content

Commit

Permalink
Feature/refactoring context menus (#836)
Browse files Browse the repository at this point in the history
Co-authored-by: Sean Griffin <sgriffin6697@gmail.com>
  • Loading branch information
flx-sta and sgriff96 authored Jul 23, 2023
1 parent e5c9c54 commit 7305409
Show file tree
Hide file tree
Showing 56 changed files with 1,119 additions and 824 deletions.
12 changes: 8 additions & 4 deletions Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ function Tabs() {
options={{
headerShown: false,
tabBarIcon: ({ color }) => (
<SFIcon icon="doc.text.image" color={color} />
<SFIcon icon={ICON_MAP.FEED} color={color} />
),
tabBarLabel: t("Feed"),
freezeOnBlur: false,
Expand All @@ -611,7 +611,9 @@ function Tabs() {
component={InboxStackScreen}
options={{
headerShown: false,
tabBarIcon: ({ color }) => <SFIcon icon="envelope" color={color} />,
tabBarIcon: ({ color }) => (
<SFIcon icon={ICON_MAP.INBOX} color={color} />
),
tabBarLabel: t("Inbox"),

tabBarBadge:
Expand Down Expand Up @@ -642,7 +644,7 @@ function Tabs() {
options={{
headerShown: false,
tabBarIcon: ({ color }) => (
<SFIcon icon="magnifyingglass" color={color} />
<SFIcon icon={ICON_MAP.SEARCH} color={color} />
),
tabBarLabel: t("Search"),
freezeOnBlur: false,
Expand All @@ -654,7 +656,9 @@ function Tabs() {
options={{
headerShown: false,
// tabBarIcon: ({ color }) => <IconSettings color={color} />,
tabBarIcon: ({ color }) => <SFIcon icon="gear" color={color} />,
tabBarIcon: ({ color }) => (
<SFIcon icon={ICON_MAP.SETTINGS} color={color} />
),
tabBarLabel: t("Settings"),
freezeOnBlur: false,
}}
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/AvatarUsername.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function AvatarUsername({
))}
{creator.bot_account && (
<SFIcon
icon="server.rack"
icon={ICON_MAP.BOT_ACCOUNT}
size={12}
boxSize={20}
color={theme.colors.app.info}
Expand Down
3 changes: 2 additions & 1 deletion src/components/common/Buttons/HideReadFAB.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Fab, useTheme } from "native-base";
import React from "react";
import SFIcon from "../icons/SFIcon";
import { ICON_MAP } from "../../../constants/IconMap";

interface IProps {
onPress: () => void;
Expand All @@ -15,7 +16,7 @@ function HideReadFAB({ onPress }: IProps) {
shadow={2}
fontSize="md"
backgroundColor={theme.colors.app.accent}
icon={<SFIcon icon="eye.slash" color="#fff" size={14} />}
icon={<SFIcon icon={ICON_MAP.HIDE} color="#fff" size={14} />}
p={2}
onPress={onPress}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/components/common/Buttons/LinkButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { openLink } from "../../../helpers/LinkHelper";
import { truncateLink } from "../../../helpers/TextHelper";
import SFIcon from "../icons/SFIcon";
import { ICON_MAP } from "../../../constants/IconMap";

interface LinkButtonProps {
link: string;
Expand Down Expand Up @@ -45,7 +46,7 @@ function LinkButton({ link, thumbnail }: LinkButtonProps) {

<HStack flexDirection="row" alignItems="center" space={3} mx={4} my={2}>
<SFIcon
icon="link"
icon={ICON_MAP.LINK}
color={theme.colors.app.textSecondary}
size={14}
/>
Expand Down
45 changes: 7 additions & 38 deletions src/components/common/Comments/CommentContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import React from "react";
import {
ContextMenuButton,
ContextMenuView,
MenuElementConfig,
OnPressMenuItemEvent,
} from "react-native-ios-context-menu";
import { OnPressMenuItemEvent } from "react-native-ios-context-menu";
import { ContextMenuOptions } from "../../../types/ContextMenuOptions";
import { AppContextMenuButton } from "../ContextMenu/App/AppContextMenuButton";
import { AppContextMenuView } from "../ContextMenu/App/AppContextMenuView";

interface IProps {
children: React.ReactNode;
Expand All @@ -20,45 +17,17 @@ export function CommentContextMenu({
isButton = false,
options,
}: IProps) {
// @ts-expect-error Types for menuItems are wrong for this library
const menuItems: MenuElementConfig[] = [
...Object.entries(options).map(([key, value]) => ({
actionKey: key,
actionTitle: value.display,
...(value.destructive ? { menuAttributes: ["destructive"] } : {}),
icon: {
type: "IMAGE_SYSTEM",
imageValue: {
systemName: value.icon,
},
},
})),
];

if (isButton) {
return (
<ContextMenuButton
isMenuPrimaryAction
onPressMenuItem={onPress}
menuConfig={{
menuTitle: "",
menuItems,
}}
>
<AppContextMenuButton options={options} onPressMenuItem={onPress}>
{children}
</ContextMenuButton>
</AppContextMenuButton>
);
}

return (
<ContextMenuView
onPressMenuItem={onPress}
menuConfig={{
menuTitle: "",
menuItems,
}}
>
<AppContextMenuView options={options} onPressMenuItem={onPress}>
{children}
</ContextMenuView>
</AppContextMenuView>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import IconButtonWithText from "../../IconButtonWithText";
import SFIcon from "../../icons/SFIcon";
import { CommentContextMenu } from "../CommentContextMenu";
import { ContextMenuOptions } from "../../../../types/ContextMenuOptions";
import { ICON_MAP } from "../../../../constants/IconMap";

interface IProps {
onPress: (key: string) => void;
Expand All @@ -24,7 +25,7 @@ function CommentHeaderRight({
if (collapsed) {
return (
<SFIcon
icon="chevron.down"
icon={ICON_MAP.CHEVRON.DOWN}
size={12}
color={theme.colors.app.textSecondary}
/>
Expand All @@ -49,7 +50,7 @@ function CommentHeaderRight({
<IconButtonWithText
icon={
<SFIcon
icon="ellipsis"
icon={ICON_MAP.MORE_OPTIONS}
size={12}
color={theme.colors.app.textSecondary}
/>
Expand Down
97 changes: 48 additions & 49 deletions src/components/common/ContextMenu/AccountsContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from "react";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { ContextMenuButton, MenuState } from "react-native-ios-context-menu";
import { OnPressMenuItemEventObject } from "react-native-ios-context-menu";
import { useAppDispatch, useAppSelector } from "../../../../store";
import { setCurrentAccount } from "../../../slices/accounts/accountsActions";
import {
selectAccounts,
selectCurrentAccount,
} from "../../../slices/accounts/accountsSlice";
import { ICON_MAP } from "../../../constants/IconMap";
import { ContextMenuOption } from "../../../types/ContextMenuOptions";
import { AppContextMenuButton } from "./App/AppContextMenuButton";

interface IProps {
children: React.ReactNode;
Expand All @@ -24,58 +27,54 @@ export function AccountsContextMenu({
const currentAccount = useAppSelector(selectCurrentAccount);

const dispatch = useAppDispatch();
return (
<ContextMenuButton
style={{
flex: 1,
}}
onPressMenuItem={({ nativeEvent }) => {
if (nativeEvent.actionKey === "manage_accounts") {
navigation.navigate("FeedStack", { screen: "ViewAccounts" });
} else {
const account = accounts.find(
(a) => a.username + a.instance === nativeEvent.actionKey
);
dispatch(setCurrentAccount(account));
navigation.navigate("FeedStack", { screen: "FeedScreen" });
}
}}
isMenuPrimaryAction={isShortPress}
menuConfig={{
menuTitle: t("Accounts"),
menuItems: [

const options = useMemo<ContextMenuOption[]>(
() => [
{
key: "manage_accounts_menu",
title: "",
inline: true,
options: [
{
type: "menu",
menuTitle: "",
menuOptions: ["displayInline"],
menuItems: [
{
type: "action",
actionKey: "manage_accounts",
actionTitle: t("Manage Accounts"),
icon: {
type: "IMAGE_SYSTEM",
imageValue: {
systemName: "chevron.right",
},
},
},
],
key: "manage_accounts",
title: "Manage Accounts",
icon: ICON_MAP.CHEVRON.RIGHT,
},
...accounts.map((account) => ({
actionKey: account.username + account.instance,
actionTitle: account.username,
actionSubtitle: account.instance,
menuState:
currentAccount.username + currentAccount.instance ===
account.username + account.instance
? "on"
: ("off" as MenuState),
})),
],
},
...accounts.map((account) => ({
key: account.username + account.instance,
title: account.username,
subtitle: account.instance,
})),
],
[t, accounts]
);

const onPressMenuItem = ({ nativeEvent }: OnPressMenuItemEventObject) => {
if (nativeEvent.actionKey === "manage_accounts") {
navigation.navigate("FeedStack", { screen: "ViewAccounts" });
} else {
const account = accounts.find(
(a) => a.username + a.instance === nativeEvent.actionKey
);
dispatch(setCurrentAccount(account));
navigation.navigate("FeedStack", { screen: "FeedScreen" });
}
};

return (
<AppContextMenuButton
isPrimaryAction={isShortPress}
options={options}
selection={currentAccount.username + currentAccount.instance}
title={t("Accounts")}
style={{
flex: 1,
}}
onPressMenuItem={onPressMenuItem}
>
{children}
</ContextMenuButton>
</AppContextMenuButton>
);
}
84 changes: 84 additions & 0 deletions src/components/common/ContextMenu/App/AppContextMenuButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
ContextMenuButton,
OnPressMenuItemEvent,
} from "react-native-ios-context-menu";
import React, { PropsWithChildren, useCallback, useMemo } from "react";
import { MenuConfig } from "react-native-ios-context-menu/lib/typescript/types/MenuConfig";
import { StyleProp, ViewStyle } from "react-native";
import { MenuElementConfig } from "react-native-ios-context-menu/src/types/MenuConfig";
import { ContextMenuOption } from "../../../../types/ContextMenuOptions";

interface AppContextMenuButtonProps<S = string> extends PropsWithChildren {
options: ContextMenuOption[];
selection?: S;
title?: string;
isPrimaryAction?: boolean;
style?: StyleProp<ViewStyle>;
onPressMenuItem: OnPressMenuItemEvent;
}

export function AppContextMenuButton<S = string>(
props: AppContextMenuButtonProps<S>
) {
const {
children,
selection,
title = "",
options,
isPrimaryAction = true,
style,
onPressMenuItem,
} = props;

const transformOption = useCallback(
(option: ContextMenuOption): MenuElementConfig => {
if (option.options) {
return {
type: "menu",
menuOptions: option.inline ? ["displayInline"] : [],
menuTitle: option.title,
icon: {
type: "IMAGE_SYSTEM",
imageValue: {
systemName: option.icon,
},
},
menuItems: [...option.options.map(transformOption)],
};
}
return {
actionKey: option.key,
actionTitle: option.title,
actionSubtitle: option.subtitle,
menuState: selection === option.key ? "on" : "off",
menuAttributes: option.destructive ? ["destructive"] : [],
icon: {
type: "IMAGE_SYSTEM",
imageValue: {
systemName: option.icon,
},
},
};
},
[selection]
);

const menuConfig = useMemo<MenuConfig>(
() => ({
menuTitle: title,
menuItems: [...options.map(transformOption)],
}),
[options, title, transformOption]
);

return (
<ContextMenuButton
isMenuPrimaryAction={isPrimaryAction}
menuConfig={menuConfig}
style={style}
onPressMenuItem={onPressMenuItem}
>
{children}
</ContextMenuButton>
);
}
Loading

0 comments on commit 7305409

Please sign in to comment.