Skip to content

Commit 8420e3d

Browse files
committed
feat: ui redesign
1 parent ee07cb6 commit 8420e3d

30 files changed

+757
-197
lines changed

lib/app/view/app.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class _AppViewState extends State<AppView> {
105105
return Listener(
106106
onPointerDown: (_) => FocusManager().primaryFocus?.unfocus(),
107107
child: MaterialApp(
108+
color: theme.primaryColor,
108109
debugShowCheckedModeBanner: false,
109110
theme: ThemeData(
110111
appBarTheme: AppBarTheme(
@@ -120,7 +121,7 @@ class _AppViewState extends State<AppView> {
120121
),
121122
scaffoldBackgroundColor: theme.scaffoldColor,
122123
cardColor: theme.cardColor,
123-
primaryColor: AppColors.primary500,
124+
primaryColor: AppColors.primary450,
124125
),
125126
localizationsDelegates: AppLocalizations.localizationsDelegates,
126127
supportedLocales: AppLocalizations.supportedLocales,

lib/bootstrap.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,7 @@ Future<void> bootstrap(
107107
const WordOfTheDayRepository(),
108108
deepSeekApiKey: translatorSettings.apiKeys[KeyNameConstants.deepSeek],
109109
// ignore: unawaited_futures
110-
);
111-
112-
await wordOfTheDayCubit.fetchWordOfTheDay();
110+
)..fetchWordOfTheDay();
113111

114112
Bloc.observer = const AppBlocObserver();
115113

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'package:flutter/material.dart';
2+
3+
class DictionaryPage extends StatelessWidget {
4+
const DictionaryPage({super.key});
5+
6+
@override
7+
Widget build(BuildContext context) {
8+
return const Scaffold();
9+
}
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export 'history_list_tile.dart';
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_svg/svg.dart';
3+
import 'package:gap/gap.dart';
4+
import 'package:qack/layout/layout_handler.dart';
5+
import 'package:qack/presentation/history/models/models.dart';
6+
import 'package:qack/presentation/settings/models/models.dart';
7+
import 'package:qack/theme/theme.dart';
8+
import 'package:vector_graphics/vector_graphics.dart';
9+
10+
class HistoryListTile extends StatefulWidget {
11+
const HistoryListTile({required this.translationHistory, super.key});
12+
final TranslationHistory translationHistory;
13+
14+
@override
15+
State<HistoryListTile> createState() => _HistoryListTileState();
16+
}
17+
18+
class _HistoryListTileState extends State<HistoryListTile> {
19+
@override
20+
Widget build(BuildContext context) {
21+
const theme = LightTheme();
22+
return LayoutHandler(
23+
mobile: HistoryListTileView(
24+
translationHistory: widget.translationHistory,
25+
theme: theme,
26+
cardMargin: const EdgeInsets.only(bottom: 16),
27+
cardPadding:
28+
const EdgeInsets.only(top: 12, left: 12, right: 12, bottom: 12),
29+
titleGap: 8,
30+
translationInputStyle: AppTextStyle.textMD.semiBold.copyWith(
31+
color: theme.textColor1,
32+
),
33+
translationOutputStyle: AppTextStyle.textMD.medium.copyWith(
34+
color: theme.textColor1,
35+
),
36+
),
37+
tablet: HistoryListTileView(
38+
translationHistory: widget.translationHistory,
39+
theme: theme,
40+
cardMargin: const EdgeInsets.only(bottom: 16),
41+
cardPadding:
42+
const EdgeInsets.only(top: 12, left: 12, right: 12, bottom: 12),
43+
titleGap: 8,
44+
translationInputStyle: AppTextStyle.textMD.semiBold.copyWith(
45+
color: theme.textColor1,
46+
),
47+
translationOutputStyle: AppTextStyle.textMD.medium.copyWith(
48+
color: theme.textColor1,
49+
),
50+
),
51+
);
52+
}
53+
}
54+
55+
class HistoryListTileView extends StatelessWidget {
56+
const HistoryListTileView({
57+
required this.translationHistory,
58+
required this.theme,
59+
required this.cardMargin,
60+
required this.cardPadding,
61+
required this.titleGap,
62+
required this.translationInputStyle,
63+
required this.translationOutputStyle,
64+
super.key,
65+
});
66+
final TranslationHistory translationHistory;
67+
68+
final BaseTheme theme;
69+
final EdgeInsets cardMargin;
70+
final EdgeInsets cardPadding;
71+
final double titleGap;
72+
final TextStyle translationInputStyle;
73+
final TextStyle translationOutputStyle;
74+
75+
@override
76+
Widget build(BuildContext context) {
77+
return Padding(
78+
padding: cardMargin,
79+
child: DecoratedBox(
80+
decoration: BoxDecoration(
81+
color: theme.cardColor,
82+
boxShadow: theme.cardShadow,
83+
borderRadius: BorderRadius.circular(4),
84+
),
85+
child: Padding(
86+
padding: cardPadding,
87+
child: Column(
88+
crossAxisAlignment: CrossAxisAlignment.start,
89+
children: [
90+
Text(
91+
translationHistory.input,
92+
style: translationInputStyle,
93+
),
94+
...List.generate(
95+
translationHistory.items?.length ?? 0,
96+
(index) {
97+
final translator = translationHistory.items!
98+
.elementAt(index)
99+
.translator
100+
.toTranslator();
101+
102+
late final EdgeInsets padding;
103+
104+
if (index == 0) {
105+
padding = const EdgeInsets.only(top: 8, bottom: 12);
106+
} else if (index == translationHistory.items!.length - 1) {
107+
padding = const EdgeInsets.only(bottom: 8);
108+
} else {
109+
padding = const EdgeInsets.only(bottom: 12);
110+
}
111+
112+
return Padding(
113+
padding: padding,
114+
child: Row(
115+
crossAxisAlignment: CrossAxisAlignment.start,
116+
children: [
117+
SizedBox(
118+
height: 28,
119+
width: 28,
120+
child: SvgPicture(
121+
AssetBytesLoader(
122+
translator.svgPath,
123+
),
124+
),
125+
),
126+
Gap(titleGap),
127+
Expanded(
128+
child: Text(
129+
translationHistory.items!
130+
.elementAt(index)
131+
.output
132+
.trim(),
133+
style: translationOutputStyle,
134+
),
135+
),
136+
],
137+
),
138+
);
139+
},
140+
),
141+
],
142+
),
143+
),
144+
),
145+
);
146+
}
147+
}

lib/presentation/history/models/history.dart

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,16 @@ class TranslationHistoryItem extends Equatable {
8383
String json,
8484
) {
8585
final jsonList = jsonDecode(json) as List<dynamic>?;
86-
return jsonList != null
87-
? jsonList
88-
.map(
89-
(json) =>
90-
TranslationHistoryItem.fromJson(json as Map<String, dynamic>),
91-
)
92-
.toList()
93-
: [];
86+
87+
// Skip individual translations with empty outputs
88+
return jsonList
89+
?.map((json) {
90+
final item =
91+
TranslationHistoryItem.fromJson(json as Map<String, dynamic>);
92+
return item.output.isNotEmpty ? item : null;
93+
})
94+
.whereType<TranslationHistoryItem>()
95+
.toList() ??
96+
[];
9497
}
9598
}

lib/presentation/history/view/history_page.dart

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
33
import 'package:qack/constants/constants.dart';
44
import 'package:qack/layout/layout_handler.dart';
55
import 'package:qack/presentation/history/bloc/history_bloc.dart';
6-
import 'package:qack/presentation/history/repositories/history_repository.dart';
7-
import 'package:qack/presentation/settings/models/translator_details.dart';
6+
import 'package:qack/presentation/history/components/history_list_tile.dart';
87
import 'package:qack/theme/theme.dart';
9-
import 'package:qack/utils/database/database.dart';
108
import 'package:qack/widgets/input/input.dart';
119

1210
class HistoryPage extends StatelessWidget {
@@ -62,46 +60,38 @@ class _HistoryViewState extends State<HistoryView> {
6260
),
6361
),
6462
SliverPadding(
65-
padding: const EdgeInsets.all(16),
63+
padding: const EdgeInsets.symmetric(horizontal: 16),
6664
sliver: BlocBuilder<HistoryBloc, HistoryState>(
6765
buildWhen: (previous, current) =>
6866
current.status != HistoryStatus.loading,
6967
builder: (context, state) {
68+
if (state.status == HistoryStatus.error) {
69+
return Center(
70+
child: Text(
71+
'Failed to load history',
72+
style: AppTextStyle.displayXS.medium.copyWith(
73+
color: theme.errorColor,
74+
),
75+
),
76+
);
77+
} else if (state.history.isEmpty) {
78+
return Center(
79+
child: Text(
80+
'No history available',
81+
style: AppTextStyle.displayXS.medium.copyWith(
82+
color: theme.textColor1,
83+
),
84+
),
85+
);
86+
}
87+
7088
return SliverList(
7189
delegate: SliverChildBuilderDelegate(
7290
(context, index) {
73-
if (state.status == HistoryStatus.error) {
74-
return Center(
75-
child: Text(
76-
'Failed to load history',
77-
// TODO: Change the textstyle
78-
style: AppTextStyle.textMD.medium.copyWith(
79-
color: theme.errorColor,
80-
),
81-
),
82-
);
83-
} else if (state.filteredHistory.isEmpty) {
84-
// TODO: only show this text if history and filter
85-
// are empty
86-
return const Center(
87-
child: Text('No history available'),
88-
);
89-
}
90-
9191
// Temporary history
9292
final translationHistory = state.filteredHistory[index];
93-
return ListTile(
94-
title: Text(translationHistory.input),
95-
subtitle: Text(
96-
translationHistory.items
97-
?.map(
98-
(item) =>
99-
'${item.translator.toTranslator()}: '
100-
'${item.output}',
101-
)
102-
.join('\n\n') ??
103-
'',
104-
),
93+
return HistoryListTile(
94+
translationHistory: translationHistory,
10595
);
10696
},
10797
childCount: state.filteredHistory.length,

lib/presentation/home/bloc/home_bloc.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:async';
2+
import 'dart:developer';
23

34
import 'package:bloc/bloc.dart';
45
import 'package:bloc_concurrency/bloc_concurrency.dart';
@@ -10,6 +11,7 @@ import 'package:qack/presentation/settings/bloc/settings_bloc.dart';
1011
import 'package:qack/presentation/settings/models/models.dart';
1112
import 'package:qack/utils/database/database.dart';
1213
import 'package:rxdart/rxdart.dart';
14+
import 'package:sentry_flutter/sentry_flutter.dart';
1315

1416
part 'home_event.dart';
1517
part 'home_state.dart';
@@ -86,12 +88,15 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
8688
return state.success(translationDetails);
8789
},
8890
onError: (e, stackTrace) {
89-
debugPrint('error: $e');
91+
log('error: $e, stackTrace: $stackTrace');
92+
// Log error to Sentry
93+
Sentry.captureException(e, stackTrace: stackTrace);
9094
return state.failure(Exception(e));
9195
},
9296
);
9397
} on Exception catch (e) {
94-
debugPrint(e.toString());
98+
log(e.toString());
99+
unawaited(Sentry.captureException(e));
95100
emit(state.failure(e));
96101
}
97102
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export 'translation_card.dart';
2+
export 'translation_input_text.dart';
3+
export 'word_of_the_day.dart';

lib/presentation/home/components/translation_card.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ class TranslationCardView extends StatelessWidget {
127127
if (translationDetails.status == TranslationStatus.loading) {
128128
translationText = 'Loading...';
129129
} else if (translationDetails.status == TranslationStatus.success) {
130-
translationText = translationDetails.translatedText!.outputText;
130+
translationText =
131+
translationDetails.translatedText!.outputText.trim();
131132
} else if (translationDetails.status == TranslationStatus.failure ||
132133
translationDetails.exception != null) {
133134
translationText =
@@ -148,7 +149,7 @@ class TranslationCardView extends StatelessWidget {
148149
decoration: BoxDecoration(
149150
color: theme.cardColor,
150151
boxShadow: theme.cardShadow,
151-
borderRadius: BorderRadius.circular(8),
152+
borderRadius: BorderRadius.circular(4),
152153
),
153154
child: Padding(
154155
padding: cardPadding,
@@ -168,7 +169,7 @@ class TranslationCardView extends StatelessWidget {
168169
),
169170
Gap(titleGap),
170171
Text(
171-
translator.name,
172+
translator.translatorName,
172173
style: titleStyle,
173174
),
174175
const Spacer(),

0 commit comments

Comments
 (0)