fix(actions): reset the operator and value when a condition changes field type

Changing a rule condition's field only merged the new field, so a
"title contains foo" condition switched to "Subscription view" kept the
text operator and value. The operator select showed nothing and the rule
saved an invalid condition. Desktop and mobile now go through
changeConditionField, which keeps the operator and value only when the
new field has the same type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DIYgod
2026-10-12 00:37:05 +08:00
parent 5c1cf61c3c
commit 3f29a8f57f
5 changed files with 44 additions and 5 deletions
@@ -90,7 +90,12 @@ export const WhenSection = ({ index }: WhenSectionProps) => {
placeholder={t("actions.action_card.select_field")}
disabled={disabled}
value={item.field}
onValueChange={(value) => change("field", value as ActionFeedField)}
onValueChange={(value) =>
actionActions.changeConditionField(
actionConditionIndex,
value as ActionFeedField,
)
}
items={filterFieldOptions.map((option) => ({
...option,
label: t(option.label),
@@ -1,7 +1,7 @@
import { filterFieldOptions, filterOperatorOptions } from "@follow/store/action/constant"
import { useActionRuleCondition } from "@follow/store/action/hooks"
import { actionActions } from "@follow/store/action/store"
import type { ActionConditionIndex } from "@follow-app/client-sdk"
import type { ActionConditionIndex, ActionFeedField } from "@follow-app/client-sdk"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { View } from "react-native"
@@ -72,9 +72,7 @@ function ConditionForm({ index }: { index: ActionConditionIndex }) {
}))}
value={currentField?.value}
onValueChange={(value) => {
actionActions.pathCondition(index, {
field: value as any,
})
actionActions.changeConditionField(index, value as ActionFeedField)
}}
wrapperClassName="min-w-48"
/>
@@ -72,6 +72,9 @@ const filterFieldOptionsMap: {
export const filterFieldOptions = Object.values(filterFieldOptionsMap)
export const getFilterFieldType = (field: ActionFeedField | undefined) =>
(field && filterFieldOptionsMap[field]?.type) || "text"
const filterOperatorOptionsMap: {
[K in ActionOperation]: {
label: Extract<ParseKeys<"settings">, `actions.action_card.operation_options.${string}`>
@@ -30,4 +30,21 @@ describe("actionActions", () => {
expect(useActionStore.getState().rules.map((rule) => rule.index)).toEqual([0, 1])
})
test("clears the operator and value when the field type changes", () => {
actionActions.addRule((index) => `Action ${index}`)
actionActions.toggleRuleFilter(0)
const index = { ruleIndex: 0, groupIndex: 0, conditionIndex: 0 }
actionActions.pathCondition(index, { field: "entry_title", operator: "contains", value: "foo" })
actionActions.changeConditionField(index, "entry_content")
expect(useActionStore.getState().rules[0]!.condition[0]![0]).toEqual({
field: "entry_content",
operator: "contains",
value: "foo",
})
actionActions.changeConditionField(index, "view")
expect(useActionStore.getState().rules[0]!.condition[0]![0]).toEqual({ field: "view" })
})
})
@@ -1,6 +1,7 @@
import { SyncMetaService } from "@follow/database/services/sync-meta"
import type {
ActionConditionIndex,
ActionFeedField,
ActionFilterItem,
ActionId,
ActionItem as ActionItemRes,
@@ -13,6 +14,7 @@ import type { Hydratable, Resetable } from "../../lib/base"
import { createImmerSetter, createZustandStore } from "../../lib/helper"
import { registerSyncModel } from "../../sync/model-registry"
import { ensureSyncedThroughEngine } from "../../sync/sync-status"
import { getFilterFieldType } from "./constant"
export type ActionItem = Omit<ActionItemRes, "condition"> & {
condition: ActionFilterItem[][]
@@ -183,6 +185,20 @@ class ActionActions implements Hydratable, Resetable {
})
}
changeConditionField(index: ActionConditionIndex, field: ActionFeedField) {
immerSet((state) => {
const group = state.rules[index.ruleIndex]?.condition[index.groupIndex]
const item = group?.[index.conditionIndex]
if (!group || !item) return
// The operator and value only make sense for the previous field type
group[index.conditionIndex] =
getFilterFieldType(item.field) === getFilterFieldType(field)
? { ...item, field }
: { field }
state.isDirty = true
})
}
addConditionItem(index: Omit<ActionConditionIndex, "conditionIndex">) {
immerSet((state) => {
const rule = state.rules[index.ruleIndex]