bookmarkTarget function

  1. @riverpod
Future<({String route, String title})> bookmarkTarget(
  1. Ref<Object?> ref,
  2. Bookmark bookmark
)

Resolved display title + in-app route for bookmark.

The raw row only stores a target id, so this resolves a human title (the bookmark's note if set, else the owning topic's title — never a bare UUID) and a real route via SupabaseDatasource.resolveBookmarkTarget. Topics and chunks open the topic's lesson (/domains/:slug/topics/:topicId/lesson); sessions resume the Socratic tutor (/tutor/chat?topicId=…). Falls back to /progress when the topic or its domain can't be resolved.

Implementation

@riverpod
Future<({String title, String route})> bookmarkTarget(
  Ref ref,
  Bookmark bookmark,
) async {
  final ds = ref.watch(supabaseDatasourceProvider);
  final ctx = await ds.resolveBookmarkTarget(
    kind: bookmark.kind,
    topicId: bookmark.topicId,
    chunkId: bookmark.chunkId,
    sessionId: bookmark.sessionId,
  );

  var title = bookmark.note ?? '';
  final topicId = ctx.topicId;
  if (topicId == null) {
    return (
      title: title.isEmpty ? bookmark.targetId : title,
      route: '/progress',
    );
  }
  if (title.isEmpty) title = ctx.topicTitle ?? bookmark.targetId;

  // Sessions resume the Socratic tutor for their topic (the chat route needs
  // only the topic id). Topics and chunks open the topic's lesson.
  if (bookmark.kind == BookmarkKind.session) {
    final tt = ctx.topicTitle != null
        ? '&topicTitle=${Uri.encodeComponent(ctx.topicTitle!)}'
        : '';
    return (
      title: title,
      route: '/tutor/chat?topicId=$topicId&mode=socratic$tt',
    );
  }

  final slug = ctx.domainSlug;
  if (slug == null || slug.isEmpty) return (title: title, route: '/progress');
  return (
    title: title,
    route:
        '/domains/$slug/topics/$topicId/lesson?title=${Uri.encodeComponent(title)}',
  );
}