# Expo Chat Day Separator Bars Implementation Plan

> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add horizontal day separator bars to the Expo chat timeline so multi-day conversations show clear date boundaries.

**Architecture:** Extract the date-boundary logic into a small pure helper that converts grouped chat messages into timeline items, then update the chat screen to render either a message group or a day-separator row. Keep the existing chat hook, message grouping behavior, pagination, typing indicator, and input UI unchanged.

**Tech Stack:** Expo SDK 54, React Native, Expo Router, TypeScript, Jest, Expo ESLint.

**Spec:** `docs/superpowers/specs/2026-04-19-expo-chat-day-separator-bars-design.md`

---

## File Structure

### New files
- `xtablo-expo/app/(app)/channel/channelTimeline.ts` — pure helper types and functions for converting `MessageGroup[]` into `TimelineItem[]`
- `xtablo-expo/app/(app)/channel/channelTimeline.test.ts` — unit tests for day-boundary insertion and French date labels

### Modified files
- `xtablo-expo/app/(app)/channel/[cid].tsx` — consume the helper, render separator rows, and add separator styles

### Existing files used as references
- `xtablo-expo/app/(app)/channel/[cid].tsx`
- `xtablo-expo/types/chat.types.ts`

---

## Chunk 1: Add a Testable Timeline Helper

### Task 1: Create pure timeline-item logic with unit tests

**Files:**
- Create: `xtablo-expo/app/(app)/channel/channelTimeline.ts`
- Create: `xtablo-expo/app/(app)/channel/channelTimeline.test.ts`

- [ ] **Step 1: Write the failing test**

Create `xtablo-expo/app/(app)/channel/channelTimeline.test.ts` with focused cases:

```ts
import { describe, expect, it } from "@jest/globals";
import {
  buildTimelineItems,
  formatDayLabel,
  type MessageGroup,
} from "./channelTimeline";

const makeGroup = (key: string, createdAt: string): MessageGroup => ({
  key,
  userId: "user-1",
  isOwn: false,
  messages: [
    {
      id: key,
      userId: "user-1",
      text: "hello",
      createdAt,
      clientId: key,
    },
  ],
});

describe("buildTimelineItems", () => {
  it("inserts one separator for the first group of a day", () => {
    const items = buildTimelineItems([
      makeGroup("a", "2026-04-19T08:00:00.000Z"),
      makeGroup("b", "2026-04-19T10:00:00.000Z"),
    ], new Date("2026-04-19T12:00:00.000Z"));

    expect(items.filter((item) => item.type === "day-separator")).toHaveLength(1);
  });

  it("inserts a new separator when the day changes", () => {
    const items = buildTimelineItems([
      makeGroup("a", "2026-04-18T23:00:00.000Z"),
      makeGroup("b", "2026-04-19T08:00:00.000Z"),
    ], new Date("2026-04-19T12:00:00.000Z"));

    expect(items.filter((item) => item.type === "day-separator")).toHaveLength(2);
  });
});

describe("formatDayLabel", () => {
  it("returns Aujourd’hui for the current day", () => {
    expect(
      formatDayLabel(new Date("2026-04-19T08:00:00.000Z"), new Date("2026-04-19T12:00:00.000Z"))
    ).toBe("Aujourd’hui");
  });

  it("returns Hier for the previous day", () => {
    expect(
      formatDayLabel(new Date("2026-04-18T08:00:00.000Z"), new Date("2026-04-19T12:00:00.000Z"))
    ).toBe("Hier");
  });
});
```

- [ ] **Step 2: Run the test to verify it fails**

Run:

```bash
cd xtablo-expo && npx jest --runInBand --watchman=false app/'(app)'/channel/channelTimeline.test.ts
```

Expected: FAIL because `channelTimeline.ts` does not exist yet.

- [ ] **Step 3: Implement the minimal helper**

Create `xtablo-expo/app/(app)/channel/channelTimeline.ts` with:
- exported `MessageGroup` type (matching the current screen’s shape)
- exported `TimelineItem` union type:
  - `{ type: "day-separator"; key: string; label: string }`
  - `{ type: "message-group"; key: string; group: MessageGroup }`
- `formatDayLabel(date, now)` helper returning:
  - `Aujourd’hui`
  - `Hier`
  - otherwise a French localized date
- `buildTimelineItems(groups, now)` helper that:
  - walks groups in chronological order
  - inserts one separator before the first group of each day
  - returns stable keys such as `day-YYYY-MM-DD`

Implementation rules:
- compare by local calendar day, not raw timestamp equality
- derive the day from the first message in each group
- keep the helper pure and UI-free

- [ ] **Step 4: Run the helper test to verify it passes**

Run:

```bash
cd xtablo-expo && npx jest --runInBand --watchman=false app/'(app)'/channel/channelTimeline.test.ts
```

Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add xtablo-expo/app/'(app)'/channel/channelTimeline.ts xtablo-expo/app/'(app)'/channel/channelTimeline.test.ts
git commit -m "feat(expo-chat): add timeline day separator helper"
```

---

## Chunk 2: Integrate Separator Bars into the Chat Screen

### Task 2: Render horizontal day bars in the chat timeline

**Files:**
- Modify: `xtablo-expo/app/(app)/channel/[cid].tsx`
- Use: `xtablo-expo/app/(app)/channel/channelTimeline.ts`

- [ ] **Step 1: Write the failing screen verification**

Run:

```bash
rg -n "day-separator|separatorLine|separatorLabel|TimelineItem" 'xtablo-expo/app/(app)/channel/[cid].tsx'
```

Expected: no results, confirming the chat screen does not yet support day separators.

- [ ] **Step 2: Refactor the screen to consume timeline items**

Update `xtablo-expo/app/(app)/channel/[cid].tsx` to:
- import `buildTimelineItems`, `TimelineItem`, and `MessageGroup` from `channelTimeline.ts`
- remove the local `MessageGroup` type if duplicated
- replace `messageGroups` list rendering with `timelineItems`
- call `buildTimelineItems(groupMessages(messages, currentUserId), new Date())`
- reverse timeline items for the inverted `FlatList` after separators are inserted in chronological order

- [ ] **Step 3: Add the separator renderer**

Add a `renderTimelineItem` function that:
- renders existing message groups through the current bubble UI
- renders day separators as a centered row with:
  - left line segment
  - centered label
  - right line segment

Style requirements:
- neutral line color using existing light/dark text or border tones
- subdued label text color
- enough vertical spacing to visually separate days

Suggested structure:

```tsx
<View style={styles.daySeparator}>
  <View style={[styles.daySeparatorLine, { backgroundColor: separatorColor }]} />
  <Text style={[styles.daySeparatorLabel, { color: subtextColor }]}>{item.label}</Text>
  <View style={[styles.daySeparatorLine, { backgroundColor: separatorColor }]} />
</View>
```

- [ ] **Step 4: Keep existing chat behavior unchanged**

While integrating the separator:
- keep pagination via `onEndReached`
- keep typing indicator logic untouched
- keep message grouping layout untouched
- keep input behavior untouched

- [ ] **Step 5: Run verification**

Run:

```bash
cd xtablo-expo && npx tsc --noEmit
cd xtablo-expo && npm run lint
cd xtablo-expo && npx jest --runInBand --watchman=false
```

Expected:
- TypeScript passes
- lint has no new errors from the chat screen change
- Jest passes, including `channelTimeline.test.ts`

- [ ] **Step 6: Commit**

```bash
git add xtablo-expo/app/'(app)'/channel/'[cid]'.tsx
git commit -m "feat(expo-chat): add day separator bars to chat timeline"
```

---

## Chunk 3: Final Manual Validation

### Task 3: Confirm the UI behavior on device or simulator

**Files:**
- No additional file changes required

- [ ] **Step 1: Run the Expo app**

Run one of:

```bash
cd xtablo-expo && npm run ios
```

or

```bash
cd xtablo-expo && npx expo start --dev-client
```

- [ ] **Step 2: Verify a multi-day chat thread**

Manual checks:
- separators appear once per day
- separator label is `Aujourd’hui` for today
- separator label is `Hier` for yesterday
- older days render as French localized dates
- message order is still correct in the inverted list

- [ ] **Step 3: Verify no regression in chat interactions**

Manual checks:
- loading older messages still works
- typing indicator still appears
- sending a message still works
- input remains pinned and usable

- [ ] **Step 4: Commit if any final UI polish was required**

```bash
git add xtablo-expo
git commit -m "chore(expo-chat): polish day separator bars"
```
