Skip to content

Commit 28e89bf

Browse files
committed
Refactor bookmarks and notes pages, remove redundant header building code.
1 parent 449f447 commit 28e89bf

19 files changed

Lines changed: 564 additions & 772 deletions

client/lib/pages/annotations_page.dart

Lines changed: 16 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:papyrus/data/data_store.dart';
44
import 'package:papyrus/models/annotation.dart';
55
import 'package:papyrus/providers/annotations_provider.dart';
66
import 'package:papyrus/themes/design_tokens.dart';
7+
import 'package:papyrus/widgets/shared/book_group_header.dart';
78
import 'package:papyrus/widgets/annotations/annotation_action_sheet.dart';
89
import 'package:papyrus/widgets/book_details/annotation_card.dart';
910
import 'package:papyrus/widgets/library/library_drawer.dart';
@@ -363,12 +364,21 @@ class _AnnotationsPageState extends State<AnnotationsPage> {
363364
for (final entry in groups.entries) {
364365
final isCollapsed = _collapsedGroups.contains(entry.key);
365366
items.add(
366-
_buildBookGroupHeader(
367-
context,
368-
provider,
369-
entry.key,
370-
entry.value.length,
371-
isCollapsed,
367+
BookGroupHeader(
368+
bookTitle: provider.getBookTitle(entry.key),
369+
coverUrl: provider.getBookCoverUrl(entry.key),
370+
count: entry.value.length,
371+
itemLabel: 'annotation',
372+
isCollapsed: isCollapsed,
373+
onToggle: () {
374+
setState(() {
375+
if (_collapsedGroups.contains(entry.key)) {
376+
_collapsedGroups.remove(entry.key);
377+
} else {
378+
_collapsedGroups.add(entry.key);
379+
}
380+
});
381+
},
372382
),
373383
);
374384
if (!isCollapsed) {
@@ -392,99 +402,6 @@ class _AnnotationsPageState extends State<AnnotationsPage> {
392402
);
393403
}
394404

395-
Widget _buildBookGroupHeader(
396-
BuildContext context,
397-
AnnotationsProvider provider,
398-
String bookId,
399-
int count,
400-
bool isCollapsed,
401-
) {
402-
final colorScheme = Theme.of(context).colorScheme;
403-
final textTheme = Theme.of(context).textTheme;
404-
final bookTitle = provider.getBookTitle(bookId);
405-
final coverUrl = provider.getBookCoverUrl(bookId);
406-
407-
return Padding(
408-
padding: const EdgeInsets.only(top: Spacing.md, bottom: Spacing.sm),
409-
child: InkWell(
410-
borderRadius: BorderRadius.circular(AppRadius.sm),
411-
onTap: () {
412-
setState(() {
413-
if (_collapsedGroups.contains(bookId)) {
414-
_collapsedGroups.remove(bookId);
415-
} else {
416-
_collapsedGroups.add(bookId);
417-
}
418-
});
419-
},
420-
child: Padding(
421-
padding: const EdgeInsets.symmetric(vertical: Spacing.xs),
422-
child: Row(
423-
children: [
424-
// Small book cover thumbnail
425-
ClipRRect(
426-
borderRadius: BorderRadius.circular(AppRadius.sm),
427-
child: SizedBox(
428-
width: 32,
429-
height: 48,
430-
child: coverUrl != null && coverUrl.isNotEmpty
431-
? Image.network(
432-
coverUrl,
433-
fit: BoxFit.cover,
434-
errorBuilder: (context, error, stackTrace) =>
435-
Container(
436-
color: colorScheme.surfaceContainerHighest,
437-
child: Icon(
438-
Icons.menu_book,
439-
size: 16,
440-
color: colorScheme.onSurfaceVariant,
441-
),
442-
),
443-
)
444-
: Container(
445-
color: colorScheme.surfaceContainerHighest,
446-
child: Icon(
447-
Icons.menu_book,
448-
size: 16,
449-
color: colorScheme.onSurfaceVariant,
450-
),
451-
),
452-
),
453-
),
454-
const SizedBox(width: Spacing.sm),
455-
Expanded(
456-
child: Column(
457-
crossAxisAlignment: CrossAxisAlignment.start,
458-
children: [
459-
Text(
460-
bookTitle,
461-
style: textTheme.titleSmall?.copyWith(
462-
fontWeight: FontWeight.w600,
463-
),
464-
maxLines: 1,
465-
overflow: TextOverflow.ellipsis,
466-
),
467-
Text(
468-
'$count ${count == 1 ? 'annotation' : 'annotations'}',
469-
style: textTheme.bodySmall?.copyWith(
470-
color: colorScheme.onSurfaceVariant,
471-
),
472-
),
473-
],
474-
),
475-
),
476-
Icon(
477-
isCollapsed ? Icons.expand_more : Icons.expand_less,
478-
color: colorScheme.onSurfaceVariant,
479-
size: IconSizes.action,
480-
),
481-
],
482-
),
483-
),
484-
),
485-
);
486-
}
487-
488405
// ============================================================================
489406
// ACTIONS
490407
// ============================================================================

client/lib/pages/book_edit_page.dart

Lines changed: 122 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -321,90 +321,11 @@ class _BookEditPageState extends State<BookEditPage> {
321321
bool skipMetadata = false,
322322
}) {
323323
return [
324-
_buildSectionCard(
325-
title: 'Basic information',
326-
children: [
327-
_buildTextField(
328-
controller: _titleController,
329-
label: 'Title',
330-
required: true,
331-
onChanged: _provider.updateTitle,
332-
),
333-
const SizedBox(height: Spacing.md),
334-
_buildTextField(
335-
controller: _subtitleController,
336-
label: 'Subtitle',
337-
onChanged: _provider.updateSubtitle,
338-
),
339-
const SizedBox(height: Spacing.md),
340-
_buildResponsiveRow([
341-
_buildTextField(
342-
controller: _authorController,
343-
label: 'Author',
344-
required: true,
345-
onChanged: _provider.updateAuthor,
346-
),
347-
]),
348-
const SizedBox(height: Spacing.md),
349-
_buildCoAuthorsSection(context),
350-
const SizedBox(height: Spacing.md),
351-
_buildRatingRow(context, provider),
352-
],
353-
),
324+
_buildBasicInfoSection(context, provider),
354325
const SizedBox(height: Spacing.sm),
355-
_buildSectionCard(
356-
title: 'Publication details',
357-
children: [
358-
_buildResponsiveRow([
359-
_buildTextField(
360-
controller: _publisherController,
361-
label: 'Publisher',
362-
onChanged: _provider.updatePublisher,
363-
),
364-
_buildTextField(
365-
controller: _languageController,
366-
label: 'Language',
367-
onChanged: _provider.updateLanguage,
368-
),
369-
]),
370-
const SizedBox(height: Spacing.md),
371-
_buildResponsiveRow([
372-
_buildDateField(
373-
controller: _publicationDateController,
374-
label: 'Publication date',
375-
value: _provider.editedBook?.publicationDate,
376-
onChanged: _provider.updatePublicationDate,
377-
),
378-
_buildTextField(
379-
controller: _pageCountController,
380-
label: 'Page count',
381-
keyboardType: TextInputType.number,
382-
onChanged: (value) {
383-
final pages = int.tryParse(value);
384-
_provider.updatePageCount(pages);
385-
},
386-
),
387-
]),
388-
],
389-
),
326+
_buildPublicationSection(),
390327
const SizedBox(height: Spacing.sm),
391-
_buildSectionCard(
392-
title: 'Identifiers',
393-
children: [
394-
_buildResponsiveRow([
395-
_buildTextField(
396-
controller: _isbnController,
397-
label: 'ISBN',
398-
onChanged: _provider.updateIsbn,
399-
),
400-
_buildTextField(
401-
controller: _isbn13Controller,
402-
label: 'ISBN-13',
403-
onChanged: _provider.updateIsbn13,
404-
),
405-
]),
406-
],
407-
),
328+
_buildIdentifiersSection(),
408329
const SizedBox(height: Spacing.sm),
409330
_buildSectionCard(
410331
title: 'Description',
@@ -418,29 +339,7 @@ class _BookEditPageState extends State<BookEditPage> {
418339
],
419340
),
420341
const SizedBox(height: Spacing.sm),
421-
_buildSectionCard(
422-
title: 'Series',
423-
children: [
424-
_buildResponsiveRow([
425-
_buildTextField(
426-
controller: _seriesNameController,
427-
label: 'Series name',
428-
onChanged: _provider.updateSeriesName,
429-
),
430-
_buildTextField(
431-
controller: _seriesNumberController,
432-
label: 'Number in series',
433-
keyboardType: const TextInputType.numberWithOptions(
434-
decimal: true,
435-
),
436-
onChanged: (value) {
437-
final number = double.tryParse(value);
438-
_provider.updateSeriesNumber(number);
439-
},
440-
),
441-
]),
442-
],
443-
),
342+
_buildSeriesSection(),
444343
const SizedBox(height: Spacing.sm),
445344
Card(
446345
child: Padding(
@@ -462,6 +361,124 @@ class _BookEditPageState extends State<BookEditPage> {
462361
];
463362
}
464363

364+
Widget _buildBasicInfoSection(
365+
BuildContext context,
366+
BookEditProvider provider,
367+
) {
368+
return _buildSectionCard(
369+
title: 'Basic information',
370+
children: [
371+
_buildTextField(
372+
controller: _titleController,
373+
label: 'Title',
374+
required: true,
375+
onChanged: _provider.updateTitle,
376+
),
377+
const SizedBox(height: Spacing.md),
378+
_buildTextField(
379+
controller: _subtitleController,
380+
label: 'Subtitle',
381+
onChanged: _provider.updateSubtitle,
382+
),
383+
const SizedBox(height: Spacing.md),
384+
_buildResponsiveRow([
385+
_buildTextField(
386+
controller: _authorController,
387+
label: 'Author',
388+
required: true,
389+
onChanged: _provider.updateAuthor,
390+
),
391+
]),
392+
const SizedBox(height: Spacing.md),
393+
_buildCoAuthorsSection(context),
394+
const SizedBox(height: Spacing.md),
395+
_buildRatingRow(context, provider),
396+
],
397+
);
398+
}
399+
400+
Widget _buildPublicationSection() {
401+
return _buildSectionCard(
402+
title: 'Publication details',
403+
children: [
404+
_buildResponsiveRow([
405+
_buildTextField(
406+
controller: _publisherController,
407+
label: 'Publisher',
408+
onChanged: _provider.updatePublisher,
409+
),
410+
_buildTextField(
411+
controller: _languageController,
412+
label: 'Language',
413+
onChanged: _provider.updateLanguage,
414+
),
415+
]),
416+
const SizedBox(height: Spacing.md),
417+
_buildResponsiveRow([
418+
_buildDateField(
419+
controller: _publicationDateController,
420+
label: 'Publication date',
421+
value: _provider.editedBook?.publicationDate,
422+
onChanged: _provider.updatePublicationDate,
423+
),
424+
_buildTextField(
425+
controller: _pageCountController,
426+
label: 'Page count',
427+
keyboardType: TextInputType.number,
428+
onChanged: (value) {
429+
final pages = int.tryParse(value);
430+
_provider.updatePageCount(pages);
431+
},
432+
),
433+
]),
434+
],
435+
);
436+
}
437+
438+
Widget _buildIdentifiersSection() {
439+
return _buildSectionCard(
440+
title: 'Identifiers',
441+
children: [
442+
_buildResponsiveRow([
443+
_buildTextField(
444+
controller: _isbnController,
445+
label: 'ISBN',
446+
onChanged: _provider.updateIsbn,
447+
),
448+
_buildTextField(
449+
controller: _isbn13Controller,
450+
label: 'ISBN-13',
451+
onChanged: _provider.updateIsbn13,
452+
),
453+
]),
454+
],
455+
);
456+
}
457+
458+
Widget _buildSeriesSection() {
459+
return _buildSectionCard(
460+
title: 'Series',
461+
children: [
462+
_buildResponsiveRow([
463+
_buildTextField(
464+
controller: _seriesNameController,
465+
label: 'Series name',
466+
onChanged: _provider.updateSeriesName,
467+
),
468+
_buildTextField(
469+
controller: _seriesNumberController,
470+
label: 'Number in series',
471+
keyboardType: const TextInputType.numberWithOptions(decimal: true),
472+
onChanged: (value) {
473+
final number = double.tryParse(value);
474+
_provider.updateSeriesNumber(number);
475+
},
476+
),
477+
]),
478+
],
479+
);
480+
}
481+
465482
// ============================================================================
466483
// SECTION CARD
467484
// ============================================================================

0 commit comments

Comments
 (0)