Skip to content

Commit 94d3eb6

Browse files
authored
feat(mobile): action page (#3019)
1 parent 8966a53 commit 94d3eb6

File tree

17 files changed

+1358
-13
lines changed

17 files changed

+1358
-13
lines changed

apps/mobile/src/components/layouts/header/NavigationHeader.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { cn } from "@follow/utils"
12
import { getDefaultHeaderHeight, HeaderTitle } from "@react-navigation/elements"
23
import { router, useNavigation } from "expo-router"
34
import type { FC, PropsWithChildren, ReactNode } from "react"
@@ -295,12 +296,21 @@ export const DefaultHeaderBackButton = ({ canGoBack }: { canGoBack: boolean }) =
295296
export const UINavigationHeaderActionButton = ({
296297
children,
297298
onPress,
299+
disabled,
300+
className,
298301
}: {
299302
children: ReactNode
300303
onPress?: () => void
304+
disabled?: boolean
305+
className?: string
301306
}) => {
302307
return (
303-
<TouchableOpacity hitSlop={5} className="p-2" onPress={onPress}>
308+
<TouchableOpacity
309+
hitSlop={5}
310+
className={cn("p-2", className)}
311+
onPress={onPress}
312+
disabled={disabled}
313+
>
304314
{children}
305315
</TouchableOpacity>
306316
)

apps/mobile/src/components/ui/grouped/GroupedList.tsx

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import type { PressableProps, ViewProps } from "react-native"
66
import { Pressable, StyleSheet, Text, View } from "react-native"
77
import Animated, { FadeIn, FadeOut } from "react-native-reanimated"
88

9+
import { CheckFilledIcon } from "@/src/icons/check_filled"
910
import { MingcuteRightLine } from "@/src/icons/mingcute_right_line"
10-
import { useColor } from "@/src/theme/colors"
11+
import { accentColor, useColor } from "@/src/theme/colors"
1112

1213
export enum GroupedInsetListCardItemStyle {
1314
NavigationLink = "NavigationLink",
@@ -150,7 +151,7 @@ export const GroupedInsetListCell: FC<
150151
} & BaseCellClassNames
151152
> = ({ label, description, children, leftClassName, rightClassName }) => {
152153
return (
153-
<GroupedInsetListBaseCell className="flex-1">
154+
<GroupedInsetListBaseCell className="bg-secondary-system-grouped-background flex-1">
154155
<View className={cn("flex-1", leftClassName)}>
155156
<Text className="text-label">{label}</Text>
156157
{!!description && (
@@ -163,15 +164,48 @@ export const GroupedInsetListCell: FC<
163164
)
164165
}
165166

167+
export const GroupedInsetListActionCellRadio: FC<{
168+
label: string
169+
description?: string
170+
onPress?: () => void
171+
disabled?: boolean
172+
selected?: boolean
173+
}> = ({ label, description, onPress, disabled, selected }) => {
174+
return (
175+
<Pressable onPress={onPress} disabled={disabled}>
176+
{({ pressed }) => (
177+
<GroupedInsetListBaseCell
178+
className={cn(pressed ? "bg-system-fill" : undefined, disabled && "opacity-40")}
179+
>
180+
<View className="flex-1">
181+
<Text className="text-label">{label}</Text>
182+
{!!description && (
183+
<Text className="text-secondary-label text-sm leading-tight">{description}</Text>
184+
)}
185+
</View>
186+
187+
<View className="ml-4 size-[18px]">
188+
{selected && <CheckFilledIcon height={18} width={18} color={accentColor} />}
189+
</View>
190+
</GroupedInsetListBaseCell>
191+
)}
192+
</Pressable>
193+
)
194+
}
195+
166196
export const GroupedInsetListActionCell: FC<{
167197
label: string
168198
description?: string
169-
onPress: () => void
199+
onPress?: () => void
170200
disabled?: boolean
171201
}> = ({ label, description, onPress, disabled }) => {
172202
const rightIconColor = useColor("tertiaryLabel")
173203
return (
174-
<Pressable onPress={onPress} disabled={disabled}>
204+
<Pressable
205+
onPress={onPress}
206+
disabled={disabled}
207+
className="bg-secondary-system-grouped-background"
208+
>
175209
{({ pressed }) => (
176210
<GroupedInsetListBaseCell
177211
className={cn(pressed ? "bg-system-fill" : undefined, disabled && "opacity-40")}
@@ -194,7 +228,7 @@ export const GroupedInsetListActionCell: FC<{
194228

195229
export const GroupedInsetButtonCell: FC<{
196230
label: string
197-
onPress: () => void
231+
onPress?: () => void
198232
disabled?: boolean
199233
style?: "destructive" | "primary"
200234
}> = ({ label, onPress, disabled, style = "primary" }) => {

apps/mobile/src/modules/settings/SettingsList.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ const DataGroupNavigationLinks: GroupNavigationLink[] = [
9797
navigation.navigate("Actions")
9898
},
9999
iconBackgroundColor: "#059669",
100-
todo: true,
101100
anonymous: false,
102101
},
103102

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Text } from "react-native"
2+
import * as DropdownMenu from "zeego/dropdown-menu"
3+
4+
import { GroupedInsetListCell } from "@/src/components/ui/grouped/GroupedList"
5+
import { actionActions } from "@/src/store/action/store"
6+
import type { ActionRule } from "@/src/store/action/types"
7+
8+
import { translationOptions } from "./constant"
9+
10+
export const ActionFormTranslation: React.FC<{ rule: ActionRule }> = ({ rule }) => {
11+
const currentTranslation = translationOptions.find(
12+
(translation) => translation.value === rule.result?.translation,
13+
)
14+
return (
15+
<GroupedInsetListCell
16+
label="Translate into"
17+
leftClassName="flex-none"
18+
rightClassName="flex-1 flex-row items-center gap-4 justify-end"
19+
>
20+
<DropdownMenu.Root>
21+
<DropdownMenu.Trigger>
22+
<Text className="text-label">{currentTranslation?.label || "Select"}</Text>
23+
</DropdownMenu.Trigger>
24+
<DropdownMenu.Content>
25+
{translationOptions.map((translation) => (
26+
<DropdownMenu.CheckboxItem
27+
value={translation.value === currentTranslation?.value}
28+
key={translation.value}
29+
onSelect={() => {
30+
actionActions.patchRule(rule.index, { result: { translation: translation.value } })
31+
}}
32+
>
33+
<DropdownMenu.ItemTitle>{translation.label}</DropdownMenu.ItemTitle>
34+
</DropdownMenu.CheckboxItem>
35+
))}
36+
</DropdownMenu.Content>
37+
</DropdownMenu.Root>
38+
</GroupedInsetListCell>
39+
)
40+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import type { SupportedLanguages } from "@/src/lib/language"
2+
import { actionActions } from "@/src/store/action/store"
3+
import type { ActionId, ActionRule } from "@/src/store/action/types"
4+
5+
import type { SettingsNavigation } from "../hooks"
6+
import { ActionFormTranslation } from "./components"
7+
8+
export const filterFieldOptions = [
9+
{
10+
label: "Subscription View",
11+
value: "view",
12+
type: "view",
13+
},
14+
{
15+
label: "Feed Title",
16+
value: "title",
17+
},
18+
{
19+
label: "Feed Category",
20+
value: "category",
21+
},
22+
{
23+
label: "Site URL",
24+
value: "site_url",
25+
},
26+
{
27+
label: "Feed URL",
28+
value: "feed_url",
29+
},
30+
{
31+
label: "Entry Title",
32+
value: "entry_title",
33+
},
34+
{
35+
label: "Entry Content",
36+
value: "entry_content",
37+
},
38+
{
39+
label: "Entry URL",
40+
value: "entry_url",
41+
},
42+
{
43+
label: "Entry Author",
44+
value: "entry_author",
45+
},
46+
{
47+
label: "Entry Media Length",
48+
value: "entry_media_length",
49+
type: "number",
50+
},
51+
]
52+
53+
export const filterOperatorOptions = [
54+
{
55+
label: "contains",
56+
value: "contains",
57+
types: ["text"],
58+
},
59+
{
60+
label: "does not contain",
61+
value: "not_contains",
62+
types: ["text"],
63+
},
64+
{
65+
label: "is equal to",
66+
value: "eq",
67+
types: ["number", "text", "view"],
68+
},
69+
{
70+
label: "is not equal to",
71+
value: "not_eq",
72+
types: ["number", "text", "view"],
73+
},
74+
{
75+
label: "is greater than",
76+
value: "gt",
77+
types: ["number"],
78+
},
79+
{
80+
label: "is less than",
81+
value: "lt",
82+
types: ["number"],
83+
},
84+
{
85+
label: "matches regex",
86+
value: "regex",
87+
types: ["text"],
88+
},
89+
]
90+
91+
export const availableActionList: Array<{
92+
value: ActionId
93+
label: string
94+
onEnable?: (index: number) => void
95+
onNavigate?: (router: SettingsNavigation, index: number) => void
96+
component?: React.FC<{ rule: ActionRule }>
97+
}> = [
98+
{
99+
value: "summary",
100+
label: "Generate summary using AI",
101+
},
102+
{
103+
value: "translation",
104+
label: "Translate into",
105+
onEnable: (index) => {
106+
actionActions.patchRule(index, { result: { translation: "zh-CN" } })
107+
},
108+
component: ActionFormTranslation,
109+
},
110+
{
111+
value: "readability",
112+
label: "Enable readability",
113+
},
114+
{
115+
value: "sourceContent",
116+
label: "View source content",
117+
},
118+
{
119+
value: "newEntryNotification",
120+
label: "Notification of new entry",
121+
},
122+
{
123+
value: "silence",
124+
label: "Silence",
125+
},
126+
{
127+
value: "block",
128+
label: "Block",
129+
},
130+
{
131+
value: "rewriteRules",
132+
label: "Rewrite Rules",
133+
onEnable: (index: number) => {
134+
actionActions.patchRule(index, {
135+
result: {
136+
rewriteRules: [
137+
{
138+
from: "",
139+
to: "",
140+
},
141+
],
142+
},
143+
})
144+
},
145+
onNavigate: (router, index) => {
146+
router.navigate("EditRewriteRules", { index })
147+
},
148+
},
149+
{
150+
value: "webhooks",
151+
label: "Webhooks",
152+
onEnable: (index) => {
153+
actionActions.patchRule(index, { result: { webhooks: [""] } })
154+
},
155+
onNavigate: (router, index) => {
156+
router.navigate("EditWebhooks", { index })
157+
},
158+
},
159+
]
160+
161+
export const translationOptions: {
162+
label: string
163+
value: SupportedLanguages
164+
}[] = [
165+
{
166+
label: "English",
167+
value: "en",
168+
},
169+
{
170+
label: "日本語",
171+
value: "ja",
172+
},
173+
{
174+
label: "简体中文",
175+
value: "zh-CN",
176+
},
177+
{
178+
label: "繁體中文",
179+
value: "zh-TW",
180+
},
181+
]

apps/mobile/src/modules/settings/hooks.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ import type { SettingsStackParamList } from "./types"
66
export const useSettingsNavigation = () => {
77
return useNavigation<NativeStackNavigationProp<SettingsStackParamList>>()
88
}
9+
10+
export type SettingsNavigation = ReturnType<typeof useSettingsNavigation>

0 commit comments

Comments
 (0)