Skip to content

Conversation

@appflowy
Copy link
Contributor

@appflowy appflowy commented Dec 17, 2025

Description


Checklist

General

  • I've included relevant documentation or comments for the changes introduced.
  • I've tested the changes in multiple environments (e.g., different browsers, operating systems).

Testing

  • I've added or updated tests to validate the changes introduced for AppFlowy Web.

Feature-Specific

  • For feature additions, I've added a preview (video, screenshot, or demo) in the "Feature Preview" section.
  • I've verified that this feature integrates seamlessly with existing functionality.

Summary by Sourcery

Align filter type semantics with desktop and harden filter creation and updates with validation and logging.

Enhancements:

  • Add validation and detailed logging around filter creation and update flows to avoid creating invalid filters and aid debugging.
  • Reorder filter type enum values so data filters are correctly classified relative to logical AND/OR filters.

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 17, 2025

Reviewer's Guide

Adjusts filter creation/update logic to be more robust and align filter type values with desktop by reordering the FilterType enum and adding guardrails and logging in the Yjs-based filter hooks.

Sequence diagram for Yjs filter creation via useAddFilter

sequenceDiagram
  actor User
  participant WebUI
  participant useAddFilterHook
  participant Logger
  participant OperationExecutor
  participant YjsView
  participant YjsFields
  participant YjsFiltersArray

  User->>WebUI: Click add filter for fieldId
  WebUI->>useAddFilterHook: invoke(fieldId)
  useAddFilterHook->>Logger: debug [useAddFilter] Creating filter
  alt Missing view or invalid fieldId
    useAddFilterHook->>Logger: warn [useAddFilter] Skipping filter creation
    useAddFilterHook-->>WebUI: return
  else Valid view and fieldId
    useAddFilterHook->>useAddFilterHook: generate filterId
    useAddFilterHook->>Logger: debug [useAddFilter] Generated filter id
    useAddFilterHook->>OperationExecutor: executeOperations(addFilter)
    OperationExecutor->>YjsFields: get(fieldId)
    alt Field not found
      OperationExecutor->>Logger: warn [useAddFilter] Field not found
      OperationExecutor-->>useAddFilterHook: return
    else Field found
      OperationExecutor->>YjsView: get(filters)
      alt Filters array missing
        OperationExecutor->>Logger: debug [useAddFilter] Creating new filters array
        OperationExecutor->>YjsView: set(filters, new YArray)
      end
      OperationExecutor->>YjsView: get(fieldType)
      OperationExecutor->>useAddFilterHook: getDefaultFilterCondition(fieldType)
      alt No default condition
        OperationExecutor->>Logger: warn [useAddFilter] No default condition
        OperationExecutor-->>useAddFilterHook: return
      else Default condition found
        OperationExecutor->>Logger: debug [useAddFilter] Setting filter data
        OperationExecutor->>YjsFiltersArray: create new filter
        YjsFiltersArray->>YjsFiltersArray: set id, field_id, condition, content
        YjsFiltersArray->>YjsFiltersArray: set filter_type = FilterType.Data
        YjsFiltersArray->>YjsFiltersArray: push(filter)
        OperationExecutor->>Logger: debug [useAddFilter] Filter created successfully
      end
    end
  end
  useAddFilterHook-->>WebUI: filter added
Loading

Class diagram for updated filter-related types and hooks

classDiagram
  class FilterType {
    <<enumeration>>
    And = 0
    Or = 1
    Data = 2
  }

  class Filter {
    +string id
    +string field_id
    +string condition
    +any content
    +FilterType filter_type
  }

  class YjsDatabaseView {
    +YDatabaseFilters filters
  }

  class YDatabaseFilters {
    +Filter[] items
    +push(filter)
  }

  class DatabaseYjsHooks {
    +useAddFilter() function
    +useUpdateFilter() function
  }

  class Logger {
    +debug(message, context)
    +warn(message, context)
  }

  DatabaseYjsHooks --> FilterType : uses
  DatabaseYjsHooks --> Filter : creates and updates
  DatabaseYjsHooks --> YjsDatabaseView : reads and writes filters
  DatabaseYjsHooks --> YDatabaseFilters : appends filters
  DatabaseYjsHooks --> Logger : logs guardrails and filter lifecycle
  YjsDatabaseView "1" o-- "0..1" YDatabaseFilters : filters
  YDatabaseFilters "1" o-- "*" Filter : contains
Loading

File-Level Changes

Change Details Files
Harden filter creation flow and improve observability in useAddFilter.
  • Add debug and warning logs around filter creation lifecycle, including initial call, ID generation, field lookup, filters array initialization, condition setup, and success completion.
  • Introduce guards to skip filter creation when view or fieldId is missing/empty, the field cannot be found, or no default condition exists for the field type.
  • Ensure FilterType.Data is explicitly set on newly created filters after condition and content are initialized.
src/application/database-yjs/dispatch.ts
Add guard when updating filters to prevent invalid field IDs.
  • Log a warning and abort the update operation when fieldId is missing before mutating the filter.
  • Retain existing behavior of setting the filter's field_id when a valid fieldId is present.
src/application/database-yjs/dispatch.ts
Align FilterType enum values ordering with desktop implementation.
  • Reorder FilterType enum so And=0, Or=1, and Data=2 instead of Data=0, And=1, Or=2 to match expected filter type semantics across platforms.
src/application/database-yjs/database.type.ts

Possibly linked issues

  • #unknown: PR corrects filter type values and adds guards, likely fixing the web grid crash from invalid filters.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and found some issues that need to be addressed.

  • Reordering the FilterType enum values (And/Or/Data) changes their underlying numeric representations and may break existing persisted data or protocol expectations; consider keeping the original numeric mapping and only adjusting usage or adding explicit serialization if needed.
  • In useUpdateFilter, the new if (!fieldId) { Log.warn...; return; } guard followed immediately by if (fieldId) { filter.set(...) } is redundant; you can simplify this by removing the second conditional and setting field_id directly after the guard.
  • The new debug logging in useAddFilter is fairly verbose (multiple logs per filter creation); consider collapsing some of these into fewer, more informative log lines or gating them behind a higher log level to reduce noise in production logs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Reordering the `FilterType` enum values (`And/Or/Data`) changes their underlying numeric representations and may break existing persisted data or protocol expectations; consider keeping the original numeric mapping and only adjusting usage or adding explicit serialization if needed.
- In `useUpdateFilter`, the new `if (!fieldId) { Log.warn...; return; }` guard followed immediately by `if (fieldId) { filter.set(...) }` is redundant; you can simplify this by removing the second conditional and setting `field_id` directly after the guard.
- The new debug logging in `useAddFilter` is fairly verbose (multiple logs per filter creation); consider collapsing some of these into fewer, more informative log lines or gating them behind a higher log level to reduce noise in production logs.

## Individual Comments

### Comment 1
<location> `src/application/database-yjs/database.type.ts:45-47` </location>
<code_context>
-  Data = 0,
-  And = 1,
-  Or = 2,
+  And = 0,
+  Or = 1,
+  Data = 2,
 }

</code_context>

<issue_to_address>
**issue (bug_risk):** Reordering `FilterType` enum members will change the persisted numeric values and may break existing data.

This reorders the numeric values from `Data=0, And=1, Or=2` to `And=0, Or=1, Data=2`, so existing `filter_type` values stored via `YjsDatabaseKey.filter_type` will be misinterpreted. Either preserve the original numeric assignments while reordering the declarations, or add a migration to update existing persisted values.
</issue_to_address>

### Comment 2
<location> `src/application/database-yjs/dispatch.ts:3603-3610` </location>
<code_context>
               return;
             }

+            if (!fieldId) {
+              Log.warn('[useUpdateFilter] FieldId is missing');
+              return;
+            }
+
             if (fieldId) {
               filter.set(YjsDatabaseKey.field_id, fieldId);
             }
</code_context>

<issue_to_address>
**suggestion:** The `fieldId` checks in `useUpdateFilter` are redundant and can be simplified.

Because we already return when `!fieldId`, the subsequent `if (fieldId)` is guaranteed to be true and is redundant. You can replace both conditions with a single early return and then call `filter.set` unconditionally:

```ts
if (!fieldId) {
  Log.warn('[useUpdateFilter] FieldId is missing');
  return;
}

filter.set(YjsDatabaseKey.field_id, fieldId);
```

```suggestion
            if (!fieldId) {
              Log.warn('[useUpdateFilter] FieldId is missing');
              return;
            }

            filter.set(YjsDatabaseKey.field_id, fieldId);
```
</issue_to_address>

### Comment 3
<location> `src/application/database-yjs/dispatch.ts:3525` </location>
<code_context>

             filters.push([filter]);
+
+            Log.debug('[useAddFilter] Filter created successfully', { filterId: id, filter: filter.toJSON() });
           },
         ],
</code_context>

<issue_to_address>
**suggestion (performance):** Logging `filter.toJSON()` on every filter creation may have performance and noise implications.

Converting the Yjs map to JSON and logging the entire filter on each creation can be costly and produce very noisy logs in hot paths. Consider either logging only key identifiers (e.g. `filterId`, `fieldId`, `fieldType`) or wrapping this in a more verbose log level / feature flag to keep diagnostics useful without adding unnecessary overhead.

Suggested implementation:

```typescript
            Log.debug('[useAddFilter] Filter created successfully', { filterId: id });

```

If `fieldId`, `fieldType`, or other lightweight identifiers are available in this scope, you may want to extend the logged object, e.g.:

`{ filterId: id, fieldId, fieldType }`

as long as they do not require converting the entire Yjs map to JSON or otherwise performing heavy computations.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +45 to +47
And = 0,
Or = 1,
Data = 2,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Reordering FilterType enum members will change the persisted numeric values and may break existing data.

This reorders the numeric values from Data=0, And=1, Or=2 to And=0, Or=1, Data=2, so existing filter_type values stored via YjsDatabaseKey.filter_type will be misinterpreted. Either preserve the original numeric assignments while reordering the declarations, or add a migration to update existing persisted values.

@appflowy appflowy merged commit 095f42a into main Dec 17, 2025
1 of 2 checks passed
@appflowy appflowy deleted the fix_filter branch December 17, 2025 04:08
josue693 pushed a commit to josue693/AppFlowy-Web that referenced this pull request Dec 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants