CommunityPro / CommunityPro/community-pro-api

Mentorship: messaging thread + unread tracking (Phase 6.5)

Open
#12 0 comments 0 reactions 0 assignees View on GitHub
mentorship
Dominant language
C#
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Scope
Per-mentorship message thread. **Polling v1 — no websockets** (frontend polls with `refetchInterval: 5000` while the thread is open).

## Data model
```csharp
public sealed class ThreadMessage
{
public Guid Id { get; set; }
public Guid MentorshipId { get; set; }
public Guid SenderId { get; set; }
public string Body { get; set; }
public DateTimeOffset SentAt { get; set; }
}
// plus LastReadAt per participant (e.g. MentorshipParticipantState) for unread counts
```

## Endpoints (participants only)
| Method | Path | Notes |
|---|---|---|
| GET | `/mentorship/{id}/messages?after=` | incremental fetch for polling; `after` = cursor (message id or timestamp); capped page size |
| POST | `/mentorship/{id}/messages` | body length-capped (~2000 chars), trimmed, non-empty |
| POST | `/mentorship/{id}/read` | stamp caller's `LastReadAt` |

Unread counts (per mentorship, for the dashboard) derived from `LastReadAt` — expose on the `GET /mentorship/mine` payload from #10 rather than a separate endpoint.

## Rules
- Only `Active` mentorship participants can post; reading history stays allowed after conclusion.
- Plain text only in v1 — no HTML rendering path at all.

## Error codes
`mentorship.not_found`, `mentorship.forbidden`, `mentorship.invalid_message`, `mentorship.invalid_state`

## Dependencies
Blocked by #10.

## Acceptance criteria
- [ ] `after` cursor returns strictly newer messages, stable ordering
- [ ] Non-participant gets not_found-shaped forbidden (no existence leak)
- [ ] Unread counts correct across both participants
- [ ] Posting to a concluded mentorship rejected

### Conventions (project-wide, non-negotiable)
- .NET 9, records for immutable shapes, file-scoped namespaces, primary constructors where they read well. Minimal-API endpoints grouped per module via `IEndpointModule.MapEndpoints`.
- `Result` (SharedKernel) instead of exception-driven control flow. Endpoint results map failures to ProblemDetails with the stable error codes listed above — the frontend keys off them.
- Module owns its EF Core `DbContext` mapped to its own Postgres schema. Modules never reference each other's internals — cross-module needs go through a public contract interface or an in-process domain event (`IEventPublisher`).
- All external calls (GitHub, Stripe, Brevo, Cloudinary, Meilisearch) behind interfaces owned by the consuming module.
- Every list endpoint paginated (offset is fine). xUnit tests in `tests/CommunityPro.Tests//` following the existing harness patterns (see `Members/MembersTestHarness.cs`).

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.