Skip to content

fix: render the Board view whenever its database opens - #667

Merged
appflowy merged 4 commits into
mainfrom
fix/board-never-renders
Oct 23, 2026
Merged

appflowy merged 4 commits into
mainfrom
fix/board-never-renders

Conversation

@appflowy

@appflowy appflowy commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Description

A database that opens in its Grid view could leave its Board view loading forever, blank, or stuck on a dead-end message. There were three separate causes, each reproduced in the browser against a local Cloud built from main (0de377aa1):

  1. One row whose data never loads kept the Board loading forever. Before its first render, the Board waits until every row in row_orders has row data (areGroupRowsHydrated). A row order whose DatabaseRow collab is missing on the server never loads. The background loader gave up after its retries, but nothing marked the row as settled, so the Board kept its Kanban skeleton. The Grid renders row_orders directly, and its filter/sort path already stops waiting for such rows.
    • The background row loader now marks a row as unavailable when it is still missing after its first retry, or when it cannot be retried at all. Marks are batched per animation frame. Board, Grid, List and Timeline grouping readiness count these rows as settled, and a late row still joins when its data arrives.
    • The loader used each view's own is_deleted flags, while the rendered row list uses the inline view's tombstones (materializeVisibleRowOrders). A row the Board waited for could therefore never be loaded. The loader now uses the same visibility as the row list.
  2. A Board with no grouping and no Select or Checkbox property rendered nothing. useGroupsSelector returned [], so Board returned null.
    • For editors, the Board now creates a "Status" Single select property and groups by it. Desktop does the same (ensure_board_grouping_field), and so does the server recipe for new Boards. This automatic setup is not recorded as an undo step.
    • Readers see "No group by property" instead of a blank area.
  3. A Board grouped by a type the web Board cannot draw yet showed a dead-end message. This covers Date, URL, Number, Text, Person, Relation, Created by and Last edited by. For example, the server copies a Grid's Date grouping into a Board added from it. The Board showed "No group by property" and offered no way out.
    • The message now names the grouping property.
    • Editors get a Group by menu that lists the Select and Checkbox properties, or offers "New Status property" when there are none.
    • Drawing these group types on the web Board is left for a follow-up (desktop parity).

useGroupsSelector now computes the group ids during render. useDatabase already re-renders on every database change, and the copy previously kept in state made a Board that had just been grouped report itself as ungrouped for one render. BOARD_GROUPABLE_FIELD_TYPES replaces three copies of the Select/Checkbox type list.

Validation

  • New BDD feature board-always-renders.feature (3 scenarios), in its own first commit:
    • On main, all 3 scenarios fail: the skeleton is still showing after 20 s, the Board is blank, and the old message has neither the property name nor an action.
    • With the fix, all 3 pass, including a --repeat-each=2 run.
    • The feature is added to the database-bdd CI group.
  • New Jest tests: board-always-renders.test.tsx (6 tests) and BoardNoGroup.test.tsx (3 tests). 4 of the 5 hook and Board tests that apply to main fail there. The fifth guards against the one-render "ungrouped" flash.
  • Full Jest suite: 803 suites and 13,498 tests pass. tsc --project tsconfig.web.json and ESLint on the changed files are clean.
  • Existing tests that still pass:
    • BDD: board-group-visibility scenarios 1–3, board-grouping-default, list-group-visibility (6 of 7 scenarios; scenario 4 is listed below), number-grouping and view-tab-order.
    • e2e: board-edit-operations, board-scroll-stability, database-view-consistency, database-grid-grouping, the Board cases of atomic-database-layouts and atomic-database-view, and the embedded bottom-scroll spec.
  • Pre-existing failures, identical on main and not fixed here:
    • board-hidden-empty-groups.feature (4 scenarios), board-group-visibility.feature scenario 4 and list-group-visibility.feature scenario 4. These assert old Group menu toggle behaviour and are not in the CI matrix.
    • The atomic Chart, Form and Timeline specs fail locally without EXPERIMENTAL_DATABASE_VIEW_CREATION_ENABLED.
  • Changed tests: two existing tests modelled a still-loading row as one that can never load. They now give it a pending ensureRow, so they still exercise the "still hydrating" state.

Checklist

  • Relevant code comments included.
  • Regression tests added (BDD and Jest), and the related suites pass.
  • Tested in multiple browser/OS environments (only Chromium on Linux).
  • Follow-up: draw Date, URL, Number, Text, Person, Relation and attribution groups on the web Board.

🤖 Generated with Claude Code

Summary by Sourcery

Ensure every database Board reaches a usable rendered or actionable state when its database opens.

New Features:

  • Add recovery controls for Boards with unsupported or missing grouping fields, including regrouping options and automatic Status grouping for editable Boards.

Bug Fixes:

  • Ensure Boards render when rows are unavailable, have no grouping, or use grouping types unsupported by the web renderer.
  • Align background row loading with visible row orders and mark permanently missing rows as settled so grouped views do not remain stuck loading.
  • Eliminate transient ungrouped Board states after collaborative grouping changes.

Enhancements:

  • Centralize Board-supported grouping types and make automatic Board initialization converge safely across collaborative replicas.

CI:

  • Add the Board rendering regression feature to the database BDD CI group.

Tests:

  • Add BDD and Jest coverage for unavailable rows, automatic grouping, unsupported groupings, and Board recovery actions.

appflowy and others added 3 commits October 23, 2026 08:55
Add a Playwright BDD feature covering three ways the Board view of a
database could stay empty while its Grid view worked:

- a row_orders entry whose DatabaseRow data never loads kept the Board on
  its loading skeleton forever
- a Board without persisted grouping and without a Select or Checkbox
  property rendered nothing at all
- a Board grouped by a property type the web Board cannot draw (for
  example a Date grouping copied from the Grid) showed a dead-end message

The scenarios fail on main and run in the database-bdd CI group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A database that opened in its Grid view could leave its Board view loading
forever, blank, or stuck on a dead-end message.

- Missing rows no longer block the Board. Grouped views waited for every
  row in row_orders to load. The background row loader now settles a row
  that is still missing after its first retry (or that cannot be retried),
  so the Board renders with the rows it has and adds late rows when they
  arrive. The loader also follows the same inline-view tombstones as the
  rendered row list, so a row the Board shows is always loaded.
- A Board with nothing to group by is grouped by a new "Status" Single
  select property for editors, matching Desktop and the server recipe.
  Readers see the "No group by property" state instead of a blank view.
- A Board grouped by a type the web Board cannot draw yet (Date, URL,
  Number, Text, Person, Relation, Created/Last edited by) names that
  property and offers a Group by menu to switch to a Select or Checkbox
  property, or to create a Status property.

useGroupsSelector now derives its ids while rendering, so a Board never
reports itself as ungrouped for one render after its grouping is written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Sorry @appflowy, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 1 hour and 2 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR makes Board opening resilient by settling rows that cannot hydrate, using the same visible row set as rendering, synchronously resolving grouping state, and providing automatic or actionable grouping recovery for ungrouped and unsupported Boards; comprehensive Jest and BDD regressions are added and wired into CI.

Sequence diagram for resilient Board opening

sequenceDiagram
    participant Board
    participant Loader as BackgroundRowDocLoader
    participant RowSync as RowDataSource
    participant Grouping as GroupingSelector

    Board->>Loader: useBackgroundRowDocLoader
    Loader->>Loader: materializeVisibleRowOrders
    Loader->>RowSync: ensureRow(rowId)
    alt row data loads
        RowSync-->>Loader: row data
        Loader-->>Grouping: cached row document
    else row remains missing after retry
        Loader->>Loader: markRowUnavailable(rowId)
        Loader-->>Grouping: unavailableRowIds
    end
    Grouping->>Grouping: areGroupRowsHydrated(rows, rowMetas, unavailableRowIds)
    Grouping-->>Board: grouping ready
    Board-->>Board: render loaded cards and columns
Loading

Flow diagram for Board grouping recovery

flowchart TD
    A[Board opens] --> B{Grouping available?}
    B -->|Select or Checkbox grouping| C[Render Board columns]
    B -->|No grouping and editable| D[createBoardGroupingField]
    D --> E[Create Status Single select]
    E --> F[Persist Board grouping without undo step]
    F --> C
    B -->|No grouping and read-only| G[Show No group by property]
    B -->|Unsupported grouping type| H[Show grouping property name]
    H --> I{Editor action}
    I -->|Choose supported property| C
    I -->|No supported properties| D
Loading

File-Level Changes

Change Details Files
Treat permanently missing row data as settled while preserving late hydration and aligning loading with rendered row visibility.
  • Track unavailable rows in the shared background loader and publish batched updates per animation frame.
  • Use inline-view tombstones when determining which rows to load and observe visibility changes.
  • Pass settled row IDs into Board, Grid, List, and Timeline grouping readiness checks.
src/application/database-yjs/hooks/useBackgroundRowDocLoader.ts
src/application/database-yjs/group.ts
src/application/database-yjs/selector.ts
src/application/database-yjs/__tests__/board-always-renders.test.tsx
src/application/database-yjs/__tests__/useGridGroupingSelector.test.tsx
src/application/database-yjs/__tests__/useRowsByGroup.test.tsx
src/components/database/components/settings/__tests__/BoardSettingGroup.test.tsx
Ensure Boards without a usable persisted grouping render an actionable state or initialize a default Status grouping.
  • Compute group IDs synchronously during render to avoid a one-render stale ungrouped state.
  • Create a Status Single select field and grouping for editable ungrouped Boards without recording an undo operation.
  • Show a non-editable explanation for readers and expose grouping controls for editors.
src/application/database-yjs/board-default-group.ts
src/application/database-yjs/dispatch.ts
src/application/database-yjs/selector.ts
src/components/database/board/Board.tsx
src/components/database/components/board/group/BoardNoGroup.tsx
src/components/database/components/board/group/Group.tsx
src/components/database/components/board/group/index.ts
src/components/database/yjs/__tests__/board-always-renders.test.tsx
src/components/database/components/board/group/__tests__/BoardNoGroup.test.tsx
Replace the Board dead-end for unsupported grouping types with a descriptive recovery UI limited to web-renderable group types.
  • Centralize Select, MultiSelect, and Checkbox as the web Board's supported grouping types.
  • Name unsupported grouping properties in the empty-state message.
  • Allow editors to regroup by an existing supported property or create a new Status property.
src/application/database-yjs/board-default-group.ts
src/application/database-yjs/selector.ts
src/components/database/components/board/group/BoardNoGroup.tsx
src/components/database/components/settings/BoardSettingGroup.tsx
src/components/database/components/board/group/__tests__/BoardNoGroup.test.tsx
Add end-to-end and unit regression coverage for all Board render failure modes and run the new BDD suite in CI.
  • Add scenarios for missing row data, automatic Status grouping, and unsupported grouping recovery.
  • Add focused Jest coverage for loader settlement, default grouping, reader behavior, synchronous grouping, and undo semantics.
  • Register the feature in the database BDD workflow and update translations.
playwright/bdd/features/database/board-always-renders.feature
playwright/bdd/steps/board-always-renders.steps.ts
.github/workflows/playwright-test.yml
src/@types/translations/en.json
src/application/database-yjs/__tests__/board-always-renders.test.tsx
src/components/database/components/board/group/__tests__/BoardNoGroup.test.tsx

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

Replay deterministic Yjs defaults for the Status field, view field orders, settings, and grouping so concurrent initialization preserves one shared schema and pre-sync card edits. Keep explicit creation undoable and skip repeated automatic initialization.

Validated with 155 focused tests, TypeScript checking, ESLint, and formatting checks.
@appflowy
appflowy merged commit 0fddfa8 into main Oct 23, 2026
21 of 23 checks passed
@appflowy
appflowy deleted the fix/board-never-renders branch October 23, 2026 13:30
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.

1 participant