bookmarkTarget function
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)}',
);
}