mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 00:25:38 -04:00
remove zeego
This commit is contained in:
@@ -8,6 +8,7 @@ node_modules
|
|||||||
# Build outputs
|
# Build outputs
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
|
.build
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
.tanstack
|
.tanstack
|
||||||
|
|
||||||
|
|||||||
+1
-18
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||||
"plugins": ["oxc", "eslint", "typescript", "react", "import", "unicorn", "vitest", "jsx-a11y"],
|
"plugins": ["oxc", "eslint", "typescript", "react", "import", "unicorn", "vitest", "jsx-a11y"],
|
||||||
"jsPlugins": ["@e18e/eslint-plugin"],
|
|
||||||
"env": {
|
"env": {
|
||||||
"builtin": true
|
"builtin": true
|
||||||
},
|
},
|
||||||
@@ -26,23 +25,7 @@
|
|||||||
"no-await-in-loop": "off",
|
"no-await-in-loop": "off",
|
||||||
"react/no-array-index-key": "off",
|
"react/no-array-index-key": "off",
|
||||||
"react/jsx-no-useless-fragment": "error",
|
"react/jsx-no-useless-fragment": "error",
|
||||||
"react/rules-of-hooks": "error",
|
"react/rules-of-hooks": "error"
|
||||||
"e18e/prefer-array-at": "error",
|
|
||||||
"e18e/prefer-array-fill": "error",
|
|
||||||
"e18e/prefer-array-from-map": "error",
|
|
||||||
"e18e/prefer-array-some": "error",
|
|
||||||
"e18e/prefer-array-to-reversed": "error",
|
|
||||||
"e18e/prefer-array-to-sorted": "error",
|
|
||||||
"e18e/prefer-array-to-spliced": "error",
|
|
||||||
"e18e/prefer-date-now": "error",
|
|
||||||
"e18e/prefer-includes": "error",
|
|
||||||
"e18e/prefer-nullish-coalescing": "error",
|
|
||||||
"e18e/prefer-object-has-own": "error",
|
|
||||||
"e18e/prefer-regex-test": "error",
|
|
||||||
"e18e/prefer-spread-syntax": "off",
|
|
||||||
"e18e/prefer-static-regex": "error",
|
|
||||||
"e18e/prefer-timer-args": "error",
|
|
||||||
"e18e/prefer-url-canparse": "error"
|
|
||||||
},
|
},
|
||||||
"ignorePatterns": ["node_modules", "dist", "build", "docs", "**/routeTree.gen.ts"]
|
"ignorePatterns": ["node_modules", "dist", "build", "docs", "**/routeTree.gen.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@better-auth/expo": "catalog:",
|
"@better-auth/expo": "catalog:",
|
||||||
|
"@expo/material-symbols": "0.1.1",
|
||||||
"@expo/metro-runtime": "~57.0.3",
|
"@expo/metro-runtime": "~57.0.3",
|
||||||
"@expo/ui": "~57.0.4",
|
"@expo/ui": "~57.0.4",
|
||||||
"@formatjs/intl-datetimeformat": "7.4.9",
|
"@formatjs/intl-datetimeformat": "7.4.9",
|
||||||
@@ -30,7 +31,6 @@
|
|||||||
"@orpc/client": "catalog:",
|
"@orpc/client": "catalog:",
|
||||||
"@orpc/contract": "catalog:",
|
"@orpc/contract": "catalog:",
|
||||||
"@orpc/tanstack-query": "catalog:",
|
"@orpc/tanstack-query": "catalog:",
|
||||||
"@react-native-menu/menu": "2.0.0",
|
|
||||||
"@sentry/react-native": "~8.18.0",
|
"@sentry/react-native": "~8.18.0",
|
||||||
"@shopify/flash-list": "2.0.2",
|
"@shopify/flash-list": "2.0.2",
|
||||||
"@sofa/api": "workspace:*",
|
"@sofa/api": "workspace:*",
|
||||||
@@ -70,11 +70,9 @@
|
|||||||
"react-dom": "catalog:",
|
"react-dom": "catalog:",
|
||||||
"react-native": "0.86.0",
|
"react-native": "0.86.0",
|
||||||
"react-native-gesture-handler": "~2.32.0",
|
"react-native-gesture-handler": "~2.32.0",
|
||||||
"react-native-ios-context-menu": "3.2.0",
|
|
||||||
"react-native-ios-utilities": "5.2.0",
|
|
||||||
"react-native-keyboard-controller": "1.21.9",
|
"react-native-keyboard-controller": "1.21.9",
|
||||||
"react-native-mmkv": "4.3.2",
|
"react-native-mmkv": "~4.3.2",
|
||||||
"react-native-nitro-modules": "0.36.1",
|
"react-native-nitro-modules": "~0.36.1",
|
||||||
"react-native-reanimated": "4.5.0",
|
"react-native-reanimated": "4.5.0",
|
||||||
"react-native-safe-area-context": "~5.7.0",
|
"react-native-safe-area-context": "~5.7.0",
|
||||||
"react-native-screens": "4.25.2",
|
"react-native-screens": "4.25.2",
|
||||||
@@ -83,7 +81,6 @@
|
|||||||
"tailwind-merge": "catalog:",
|
"tailwind-merge": "catalog:",
|
||||||
"tailwindcss": "catalog:",
|
"tailwindcss": "catalog:",
|
||||||
"uniwind": "1.10.0",
|
"uniwind": "1.10.0",
|
||||||
"zeego": "3.0.6",
|
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Icon } from "@expo/ui";
|
||||||
|
import { MenuView } from "@expo/ui/community/menu";
|
||||||
import { plural } from "@lingui/core/macro";
|
import { plural } from "@lingui/core/macro";
|
||||||
import { Trans, useLingui } from "@lingui/react/macro";
|
import { Trans, useLingui } from "@lingui/react/macro";
|
||||||
import {
|
import {
|
||||||
@@ -38,7 +40,6 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import Animated, { FadeInDown } from "react-native-reanimated";
|
import Animated, { FadeInDown } from "react-native-reanimated";
|
||||||
import { useCSSVariable } from "uniwind";
|
import { useCSSVariable } from "uniwind";
|
||||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
|
||||||
|
|
||||||
import { IntegrationsSection } from "@/components/settings/integrations-section";
|
import { IntegrationsSection } from "@/components/settings/integrations-section";
|
||||||
import { SettingsRow } from "@/components/settings/settings-row";
|
import { SettingsRow } from "@/components/settings/settings-row";
|
||||||
@@ -66,6 +67,15 @@ const settingsContentContainerStyle = {
|
|||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changePhotoIcon = Icon.select({
|
||||||
|
ios: "photo.on.rectangle.angled",
|
||||||
|
android: import("@expo/material-symbols/photo_library.xml"),
|
||||||
|
});
|
||||||
|
const removePhotoIcon = Icon.select({
|
||||||
|
ios: "trash",
|
||||||
|
android: import("@expo/material-symbols/delete.xml"),
|
||||||
|
});
|
||||||
|
|
||||||
export default function SettingsScreen() {
|
export default function SettingsScreen() {
|
||||||
const { t, i18n } = useLingui();
|
const { t, i18n } = useLingui();
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
@@ -241,52 +251,49 @@ export default function SettingsScreen() {
|
|||||||
<SettingsSection title={t`Account`} icon={IconUser}>
|
<SettingsSection title={t`Account`} icon={IconUser}>
|
||||||
<View className="flex-row items-center py-3.5">
|
<View className="flex-row items-center py-3.5">
|
||||||
{hasAvatarImage ? (
|
{hasAvatarImage ? (
|
||||||
<DropdownMenu.Root>
|
<MenuView
|
||||||
<DropdownMenu.Trigger asChild>
|
actions={[
|
||||||
<Pressable
|
{ id: "change", title: t`Change Photo`, image: changePhotoIcon },
|
||||||
accessibilityRole="button"
|
{
|
||||||
accessibilityLabel={t`Edit profile photo`}
|
id: "remove",
|
||||||
accessibilityHint={t`Opens options to change or remove your photo`}
|
title: t`Remove Photo`,
|
||||||
className="mr-3"
|
image: removePhotoIcon,
|
||||||
hitSlop={8}
|
attributes: { destructive: true },
|
||||||
>
|
},
|
||||||
<View className="bg-secondary size-11 overflow-hidden rounded-full">
|
]}
|
||||||
{isUploadingAvatar ? (
|
onPressAction={({ nativeEvent }) => {
|
||||||
<View className="flex-1 items-center justify-center">
|
if (nativeEvent.event === "change") {
|
||||||
<Spinner size="sm" colorClassName="accent-primary" />
|
pickAvatar();
|
||||||
</View>
|
} else if (nativeEvent.event === "remove") {
|
||||||
) : (
|
removeAvatar.mutate();
|
||||||
<Image
|
}
|
||||||
source={{ uri: session.user.image ?? undefined }}
|
}}
|
||||||
style={{ width: "100%", height: "100%" }}
|
>
|
||||||
contentFit="cover"
|
<Pressable
|
||||||
/>
|
accessibilityRole="button"
|
||||||
)}
|
accessibilityLabel={t`Edit profile photo`}
|
||||||
</View>
|
accessibilityHint={t`Opens options to change or remove your photo`}
|
||||||
<View className="bg-primary absolute right-0 bottom-0 size-[18px] items-center justify-center rounded-full">
|
className="mr-3"
|
||||||
<IconCamera size={10} color={primaryFgColor} />
|
hitSlop={8}
|
||||||
</View>
|
>
|
||||||
</Pressable>
|
<View className="bg-secondary size-11 overflow-hidden rounded-full">
|
||||||
</DropdownMenu.Trigger>
|
{isUploadingAvatar ? (
|
||||||
<DropdownMenu.Content>
|
<View className="flex-1 items-center justify-center">
|
||||||
<DropdownMenu.Item key="change" onSelect={() => pickAvatar()}>
|
<Spinner size="sm" colorClassName="accent-primary" />
|
||||||
<DropdownMenu.ItemIcon ios={{ name: "photo.on.rectangle.angled" }} />
|
</View>
|
||||||
<DropdownMenu.ItemTitle>
|
) : (
|
||||||
<Trans>Change Photo</Trans>
|
<Image
|
||||||
</DropdownMenu.ItemTitle>
|
source={{ uri: session.user.image ?? undefined }}
|
||||||
</DropdownMenu.Item>
|
style={{ width: "100%", height: "100%" }}
|
||||||
<DropdownMenu.Item
|
contentFit="cover"
|
||||||
key="remove"
|
/>
|
||||||
destructive
|
)}
|
||||||
onSelect={() => removeAvatar.mutate()}
|
</View>
|
||||||
>
|
<View className="bg-primary absolute right-0 bottom-0 size-[18px] items-center justify-center rounded-full">
|
||||||
<DropdownMenu.ItemIcon ios={{ name: "trash" }} />
|
<IconCamera size={10} color={primaryFgColor} />
|
||||||
<DropdownMenu.ItemTitle>
|
</View>
|
||||||
<Trans>Remove Photo</Trans>
|
</Pressable>
|
||||||
</DropdownMenu.ItemTitle>
|
</MenuView>
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
) : (
|
) : (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={pickAvatar}
|
onPress={pickAvatar}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import { MenuView } from "@expo/ui/community/menu";
|
||||||
import { useLingui } from "@lingui/react/macro";
|
import { useLingui } from "@lingui/react/macro";
|
||||||
import type { Icon } from "@tabler/icons-react-native";
|
import type { Icon } from "@tabler/icons-react-native";
|
||||||
import { Pressable, View } from "react-native";
|
import { Pressable, View } from "react-native";
|
||||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
|
||||||
|
|
||||||
import { ScaledIcon } from "@/components/ui/scaled-icon";
|
import { ScaledIcon } from "@/components/ui/scaled-icon";
|
||||||
import { Sparkline } from "@/components/ui/sparkline";
|
import { Sparkline } from "@/components/ui/sparkline";
|
||||||
@@ -86,29 +86,25 @@ export function StatsCard(props: StatsCardProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={cardStyle}>
|
<View style={cardStyle}>
|
||||||
<DropdownMenu.Root>
|
<MenuView
|
||||||
<DropdownMenu.Trigger asChild>
|
actions={periods.map((value) => ({
|
||||||
<Pressable
|
id: value,
|
||||||
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
title: periodLabels[value],
|
||||||
accessibilityRole="button"
|
state: value === period ? "on" : "off",
|
||||||
>
|
}))}
|
||||||
<CardInner {...props} />
|
onOpenMenu={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
||||||
</Pressable>
|
onPressAction={({ nativeEvent }) => {
|
||||||
</DropdownMenu.Trigger>
|
const nextPeriod = periods.find((value) => value === nativeEvent.event);
|
||||||
|
if (nextPeriod) onPeriodChange(nextPeriod);
|
||||||
<DropdownMenu.Content>
|
}}
|
||||||
{periods.map((p) => (
|
>
|
||||||
<DropdownMenu.CheckboxItem
|
<Pressable
|
||||||
key={p}
|
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
||||||
value={p === period ? "on" : "off"}
|
accessibilityRole="button"
|
||||||
onValueChange={() => onPeriodChange(p)}
|
>
|
||||||
>
|
<CardInner {...props} />
|
||||||
<DropdownMenu.ItemIndicator />
|
</Pressable>
|
||||||
<DropdownMenu.ItemTitle>{periodLabels[p]}</DropdownMenu.ItemTitle>
|
</MenuView>
|
||||||
</DropdownMenu.CheckboxItem>
|
|
||||||
))}
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import { Icon } from "@expo/ui";
|
||||||
|
import { MenuView } from "@expo/ui/community/menu";
|
||||||
import { useLingui } from "@lingui/react/macro";
|
import { useLingui } from "@lingui/react/macro";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import { Alert, Pressable, View } from "react-native";
|
import { Alert, Pressable, View } from "react-native";
|
||||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
|
||||||
|
|
||||||
import { Image } from "@/components/ui/image";
|
import { Image } from "@/components/ui/image";
|
||||||
import { Text } from "@/components/ui/text";
|
import { Text } from "@/components/ui/text";
|
||||||
@@ -9,6 +10,15 @@ import { queryClient } from "@/lib/query-client";
|
|||||||
import { authClient } from "@/lib/server";
|
import { authClient } from "@/lib/server";
|
||||||
import * as Haptics from "@/utils/haptics";
|
import * as Haptics from "@/utils/haptics";
|
||||||
|
|
||||||
|
const settingsIcon = Icon.select({
|
||||||
|
ios: "gear",
|
||||||
|
android: import("@expo/material-symbols/settings.xml"),
|
||||||
|
});
|
||||||
|
const signOutIcon = Icon.select({
|
||||||
|
ios: "rectangle.portrait.and.arrow.right",
|
||||||
|
android: import("@expo/material-symbols/logout.xml"),
|
||||||
|
});
|
||||||
|
|
||||||
export function HeaderAvatar() {
|
export function HeaderAvatar() {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const { data: session } = authClient.useSession();
|
const { data: session } = authClient.useSession();
|
||||||
@@ -19,66 +29,61 @@ export function HeaderAvatar() {
|
|||||||
const { user } = session;
|
const { user } = session;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Root>
|
<MenuView
|
||||||
<DropdownMenu.Trigger asChild>
|
actions={[
|
||||||
<Pressable
|
{ id: "settings", title: t`Settings`, image: settingsIcon },
|
||||||
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
{
|
||||||
accessibilityRole="button"
|
id: "sign-out",
|
||||||
accessibilityLabel={t`User menu`}
|
title: t`Sign out`,
|
||||||
hitSlop={8}
|
image: signOutIcon,
|
||||||
>
|
attributes: { destructive: true },
|
||||||
<View className="size-8 overflow-hidden rounded-full" accessible={false}>
|
},
|
||||||
{user.image ? (
|
]}
|
||||||
<Image
|
onOpenMenu={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
||||||
source={{ uri: user.image }}
|
onPressAction={({ nativeEvent }) => {
|
||||||
style={{ width: "100%", height: "100%" }}
|
if (nativeEvent.event === "settings") {
|
||||||
contentFit="cover"
|
navigate("/(tabs)/(settings)");
|
||||||
accessible={false}
|
} else if (nativeEvent.event === "sign-out") {
|
||||||
/>
|
Alert.alert(t`Sign out`, t`Are you sure you want to sign out?`, [
|
||||||
) : (
|
{ text: t`Cancel`, style: "cancel" },
|
||||||
<View className="bg-primary/[0.08] flex-1 items-center justify-center">
|
{
|
||||||
<Text
|
text: t`Sign out`,
|
||||||
maxFontSizeMultiplier={1.0}
|
style: "destructive",
|
||||||
className="font-display text-primary text-sm font-medium"
|
onPress: () => {
|
||||||
>
|
authClient.signOut();
|
||||||
{user.name?.charAt(0)?.toUpperCase() ?? "?"}
|
queryClient.clear();
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</Pressable>
|
|
||||||
</DropdownMenu.Trigger>
|
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
|
||||||
<DropdownMenu.Item key="settings" onSelect={() => navigate("/(tabs)/(settings)")}>
|
|
||||||
<DropdownMenu.ItemIcon ios={{ name: "gear" }} androidIconName="ic_menu_preferences" />
|
|
||||||
<DropdownMenu.ItemTitle>{t`Settings`}</DropdownMenu.ItemTitle>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
|
|
||||||
<DropdownMenu.Item
|
|
||||||
key="sign-out"
|
|
||||||
destructive
|
|
||||||
onSelect={() => {
|
|
||||||
Alert.alert(t`Sign out`, t`Are you sure you want to sign out?`, [
|
|
||||||
{ text: t`Cancel`, style: "cancel" },
|
|
||||||
{
|
|
||||||
text: t`Sign out`,
|
|
||||||
style: "destructive",
|
|
||||||
onPress: () => {
|
|
||||||
authClient.signOut();
|
|
||||||
queryClient.clear();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]);
|
},
|
||||||
}}
|
]);
|
||||||
>
|
}
|
||||||
<DropdownMenu.ItemIcon
|
}}
|
||||||
ios={{ name: "rectangle.portrait.and.arrow.right" }}
|
>
|
||||||
androidIconName="ic_menu_close_clear_cancel"
|
<Pressable
|
||||||
/>
|
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
||||||
<DropdownMenu.ItemTitle>{t`Sign out`}</DropdownMenu.ItemTitle>
|
accessibilityRole="button"
|
||||||
</DropdownMenu.Item>
|
accessibilityLabel={t`User menu`}
|
||||||
</DropdownMenu.Content>
|
hitSlop={8}
|
||||||
</DropdownMenu.Root>
|
>
|
||||||
|
<View className="size-8 overflow-hidden rounded-full" accessible={false}>
|
||||||
|
{user.image ? (
|
||||||
|
<Image
|
||||||
|
source={{ uri: user.image }}
|
||||||
|
style={{ width: "100%", height: "100%" }}
|
||||||
|
contentFit="cover"
|
||||||
|
accessible={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View className="bg-primary/[0.08] flex-1 items-center justify-center">
|
||||||
|
<Text
|
||||||
|
maxFontSizeMultiplier={1.0}
|
||||||
|
className="font-display text-primary text-sm font-medium"
|
||||||
|
>
|
||||||
|
{user.name?.charAt(0)?.toUpperCase() ?? "?"}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Pressable>
|
||||||
|
</MenuView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { MenuView } from "@expo/ui/community/menu";
|
||||||
import { useLingui } from "@lingui/react/macro";
|
import { useLingui } from "@lingui/react/macro";
|
||||||
import { IconArrowsSort } from "@tabler/icons-react-native";
|
import { IconArrowsSort } from "@tabler/icons-react-native";
|
||||||
import { Pressable } from "react-native";
|
import { Pressable } from "react-native";
|
||||||
import { useCSSVariable } from "uniwind";
|
import { useCSSVariable } from "uniwind";
|
||||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
|
||||||
|
|
||||||
import { ScaledIcon } from "@/components/ui/scaled-icon";
|
import { ScaledIcon } from "@/components/ui/scaled-icon";
|
||||||
import * as Haptics from "@/utils/haptics";
|
import * as Haptics from "@/utils/haptics";
|
||||||
@@ -37,33 +37,28 @@ export function SortMenu({ sortBy, sortDirection, onSortChange }: SortMenuProps)
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Root>
|
<MenuView
|
||||||
<DropdownMenu.Trigger asChild>
|
actions={sortOptions.map((option) => ({
|
||||||
<Pressable
|
id: `${option.sortBy}-${option.sortDirection}`,
|
||||||
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
title: option.label,
|
||||||
accessibilityRole="button"
|
state: option.sortBy === sortBy && option.sortDirection === sortDirection ? "on" : "off",
|
||||||
accessibilityLabel={t`Sort`}
|
}))}
|
||||||
hitSlop={8}
|
onOpenMenu={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
||||||
>
|
onPressAction={({ nativeEvent }) => {
|
||||||
<ScaledIcon icon={IconArrowsSort} size={22} color={foregroundColor} />
|
const option = sortOptions.find(
|
||||||
</Pressable>
|
(candidate) => `${candidate.sortBy}-${candidate.sortDirection}` === nativeEvent.event,
|
||||||
</DropdownMenu.Trigger>
|
);
|
||||||
|
if (option) onSortChange(option.sortBy, option.sortDirection);
|
||||||
<DropdownMenu.Content>
|
}}
|
||||||
{sortOptions.map((option) => {
|
>
|
||||||
const isSelected = option.sortBy === sortBy && option.sortDirection === sortDirection;
|
<Pressable
|
||||||
return (
|
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
|
||||||
<DropdownMenu.CheckboxItem
|
accessibilityRole="button"
|
||||||
key={`${option.sortBy}-${option.sortDirection}`}
|
accessibilityLabel={t`Sort`}
|
||||||
value={isSelected ? "on" : "off"}
|
hitSlop={8}
|
||||||
onValueChange={() => onSortChange(option.sortBy, option.sortDirection)}
|
>
|
||||||
>
|
<ScaledIcon icon={IconArrowsSort} size={22} color={foregroundColor} />
|
||||||
<DropdownMenu.ItemIndicator />
|
</Pressable>
|
||||||
<DropdownMenu.ItemTitle>{option.label}</DropdownMenu.ItemTitle>
|
</MenuView>
|
||||||
</DropdownMenu.CheckboxItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,6 @@
|
|||||||
"sync:providers": "bun scripts/sync-tmdb-providers.ts"
|
"sync:providers": "bun scripts/sync-tmdb-providers.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@e18e/eslint-plugin": "0.5.1",
|
|
||||||
"@lingui/cli": "catalog:",
|
"@lingui/cli": "catalog:",
|
||||||
"@lingui/conf": "catalog:",
|
"@lingui/conf": "catalog:",
|
||||||
"@lingui/format-po": "catalog:",
|
"@lingui/format-po": "catalog:",
|
||||||
|
|||||||
Reference in New Issue
Block a user