Skip to content

Commit

Permalink
Redesign deck page (#263)
Browse files Browse the repository at this point in the history
<img width="776" alt="image"
src="https://github.com/user-attachments/assets/a6caab43-df9e-4502-bef5-2865bd31114c">

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
nilsreichardt and coderabbitai[bot] authored Oct 15, 2024
1 parent 2420926 commit e2970a6
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 68 deletions.
208 changes: 141 additions & 67 deletions lib/src/pages/deck_page.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import 'dart:math';

import 'package:animations/animations.dart';
import 'package:ankigpt/src/models/session_id.dart';
import 'package:ankigpt/src/pages/deck_page/share_dialog.dart';
import 'package:ankigpt/src/pages/home_page/controls.dart';
import 'package:ankigpt/src/pages/deck_page/result_section.dart';
import 'package:ankigpt/src/pages/widgets/ankigpt_card.dart';
import 'package:ankigpt/src/pages/widgets/elevated_button.dart';
import 'package:ankigpt/src/pages/widgets/extensions.dart';
import 'package:ankigpt/src/pages/widgets/footer.dart';
import 'package:ankigpt/src/pages/widgets/input_text_field.dart';
import 'package:ankigpt/src/pages/widgets/max_width_constrained_box.dart';
import 'package:ankigpt/src/pages/widgets/video_player.dart';
import 'package:ankigpt/src/providers/deck_page_scroll_controller_provider.dart';
import 'package:ankigpt/src/providers/logger/logger_provider.dart';
import 'package:ankigpt/src/providers/session_id_provider.dart';
import 'package:ankigpt/src/providers/watch_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:url_launcher/url_launcher.dart';

class DeckPage extends ConsumerStatefulWidget {
const DeckPage({
Expand Down Expand Up @@ -56,9 +61,19 @@ class _SessionPageState extends ConsumerState<DeckPage> {
child: SelectionArea(
child: Scaffold(
appBar: AppBar(
actions: [
_ShareIconButton(sessionId: sessionId),
],
automaticallyImplyLeading: false,
title: const MaxWidthConstrainedBox(
maxWidth: 900,
child: Row(
children: [
BackButton(),
Spacer(),
_ExportToAnkiButton(),
],
),
),
centerTitle: false,
backgroundColor: Colors.transparent,
),
body: SafeArea(
child: SingleChildScrollView(
Expand All @@ -83,93 +98,152 @@ class _SessionPageState extends ConsumerState<DeckPage> {
}
}

class _ShareIconButton extends StatelessWidget {
const _ShareIconButton({
required this.sessionId,
});
class _ExportToAnkiButton extends ConsumerWidget {
const _ExportToAnkiButton();

final SessionId sessionId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final sessionId = ref.watch(sessionIdProvider) ?? 'does-not-exist';
final view = ref.watch(watchProvider(sessionId));
return AnkiGptElevatedButton.icon(
icon: const Icon(Icons.download),
label: const Text(
'Export to Anki',
style: TextStyle(
fontSize: 18,
),
),
onPressed: view.isDownloadAvailable
? () {
launchUrl(Uri.parse(view.downloadUrl!));
showModal(
context: context,
builder: (context) => const _ExportToAnkiDialog(),
routeSettings: const RouteSettings(name: '/export-to-anki'),
);
}
: null,
tooltip: view.downloadButtonTooltip,
center: context.isMobile,
isEnabled: view.isDownloadAvailable,
);
}
}

class _ExportToAnkiDialog extends StatelessWidget {
const _ExportToAnkiDialog();

@override
Widget build(BuildContext context) {
return IconButton(
tooltip: 'Share',
icon: const Icon(Icons.ios_share),
onPressed: () {
showModal(
context: context,
builder: (context) => ShareDialog(
sessionId: sessionId,
),
);
},
return AlertDialog(
title: const Text('Tutorial: How to import a .csv file into Anki'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: min(MediaQuery.of(context).size.height * 2, 300),
child: const TutorialVideoPlayer(
aspectRatio: 4 / 2.9,
videoUrl:
'https://firebasestorage.googleapis.com/v0/b/ankigpt-prod.appspot.com/o/assets%2Fanki-import-tutorial.mp4?alt=media&token=87f434d0-8318-47d0-b0e1-4c86753b9eb3',
),
),
const SizedBox(height: 18),
const ListTile(
title:
Text('1. Open Anki and click on "Import" in the menu bar.'),
),
const ListTile(
title: Text('2. Select the .csv file you just downloaded.'),
),
const ListTile(
title: Text('3. Select "Comma" as Field Separator.'),
),
const ListTile(
title: Text('4. Make sure to select the right deck.'),
),
const ListTile(
title: Text('5. Click on "Import".'),
),
const SizedBox(height: 12),
const _WarningAfterDownload(),
const SizedBox(height: 12),
const _ContactSupportNote(),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
);
}
}

class _Body extends StatelessWidget {
const _Body();
class _ContactSupportNote extends StatelessWidget {
const _ContactSupportNote();

@override
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
_Input(),
Controls(),
ResultSection(),
],
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300),
child: MarkdownBody(
data:
'If you have any questions, please reach out to the [AnkiGPT Support](https://ankigpt.help/support).',
styleSheet: MarkdownStyleSheet(
textAlign: WrapAlignment.center,
p: const TextStyle(
fontSize: 16,
),
),
onTapLink: (text, href, title) => launchUrl(Uri.parse(href!)),
),
);
}
}

class _Input extends ConsumerWidget {
const _Input();
class _WarningAfterDownload extends ConsumerWidget {
const _WarningAfterDownload();

@override
Widget build(BuildContext context, WidgetRef ref) {
final sessionId = ref.watch(sessionIdProvider)!;
final view = ref.watch(watchProvider(sessionId));
return AnimatedSwitcher(
duration: const Duration(milliseconds: 275),
child: view.hasFile
? const _FileCard()
: InputTextField(
controller: TextEditingController(text: view.inputText),
isEnabled: false,
const color = Colors.deepOrange;
return SizedBox(
width: min(MediaQuery.of(context).size.height * 2, 400),
child: const AnkiGptCard(
color: Color(0xFFFFDFC1),
padding: EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.warning, color: color, size: 30),
SizedBox(width: 12),
Flexible(
child: Text(
"AnkiGPT is your co-pilot, not the captain! Remember, even AI stumbles sometimes, so be sure to double-check those cards!",
style: TextStyle(
color: color,
fontSize: 12,
),
),
),
],
),
),
);
}
}

class _FileCard extends ConsumerWidget {
const _FileCard();
class _Body extends StatelessWidget {
const _Body();

@override
Widget build(BuildContext context, WidgetRef ref) {
final sessionId = ref.watch(sessionIdProvider)!;
final fileName =
ref.watch(watchProvider(sessionId).select((view) => view.fileName));
const borderRadius = BorderRadius.all(Radius.circular(12));
return AnkiGptCard(
borderRadius: borderRadius,
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
child: SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 40, 12, 40),
child: Column(
children: [
const Icon(Icons.upload_file),
const SizedBox(height: 13),
Text(fileName ?? 'File picked.'),
const SizedBox(height: 13),
],
),
),
),
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.fromLTRB(12, 0, 12, 12),
child: ResultSection(),
);
}
}
17 changes: 16 additions & 1 deletion lib/src/pages/deck_page/result_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,19 @@ class _Subtitle extends ConsumerWidget {
final Language? language;
final Model? model;

String getTitle(WidgetRef ref) {
final sessionId = ref.watch(sessionIdProvider)!;
final view = ref.watch(watchProvider(sessionId));

if (view.hasFile) {
final fileName =
ref.watch(watchProvider(sessionId).select((view) => view.fileName));
return fileName ?? 'File';
}

return '${(view.inputText?.length ?? 0) > 30 ? view.inputText?.substring(0, 30) : view.inputText}...';
}

@override
Widget build(BuildContext context, WidgetRef ref) {
final query = ref.watch(searchQueryProvider);
Expand All @@ -847,7 +860,9 @@ class _Subtitle extends ConsumerWidget {
return Padding(
padding: const EdgeInsets.only(bottom: 12, top: 12),
child: Text(
'Detected language: ${language == null ? '...' : language!.getDisplayName()}, $cardsCount cards (${model?.getUiText()})',
'Deck: "${getTitle(ref)}", Language: ${language == null ? '...' : language!.getDisplayName()}, $cardsCount cards',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.grey[500]),
),
);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e2970a6

Please sign in to comment.