Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete gemini integration #1

Merged
merged 7 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix issues
  • Loading branch information
iamEtornam committed Mar 9, 2024
commit 8a379b7f975b90b9a3fe4a5a8a9c3f9ef643cd17
9 changes: 7 additions & 2 deletions lib/managers/news_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ class NewsManager extends ChangeNotifier {
return await newsServices.otherNews(country: country);
}

Future <GenerateContentResponse> summarize({required String article}) async {
return await newsServices.summarize(article: article);
Future<GenerateContentResponse> summarize({required String article}) async {
return await newsServices.summarize(articleuRL: article);
}

Future <GenerateContentResponse> translate(
{required String text, required String language}) async {
return await newsServices.translate(text: text, language: language);
}
}
22 changes: 19 additions & 3 deletions lib/services/news_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import 'package:news_assistant/utils/util.dart';
abstract class NewsServices {
Future<News> headlines({String country = 'gh'});
Future<News> otherNews({String country = 'gh'});
Future<GenerateContentResponse> summarize({required String article});
Future<GenerateContentResponse> summarize({required String articleuRL});
Future<GenerateContentResponse> translate(
{required String text, required String language});
}

class NewsServicesImpl extends NewsServices {
Expand Down Expand Up @@ -66,9 +68,10 @@ class NewsServicesImpl extends NewsServices {
}

@override
Future<GenerateContentResponse> summarize({required String article}) async {
Future<GenerateContentResponse> summarize(
{required String articleuRL}) async {
try {
final webContent = await getMainArticleContent(article);
final webContent = await getMainArticleContent(articleuRL);
final String prompt =
'Given the following article content, please generate a concise summary'
' highlighting the main points and key details. '
Expand All @@ -83,4 +86,17 @@ class NewsServicesImpl extends NewsServices {
throw Exception(e);
}
}

@override
Future<GenerateContentResponse> translate(
{required String text, required String language}) async {
try {
final prompt = 'Translate the following text to $language: $text';
final content = [Content.text(prompt)];
final response = await model.generateContent(content);
return response;
} catch (e) {
rethrow;
}
}
}
130 changes: 98 additions & 32 deletions lib/views/ai_summarize_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand Down Expand Up @@ -26,20 +28,34 @@ class AiSummarizeView extends ConsumerStatefulWidget {
}

class _AiSummarizeViewState extends ConsumerState<AiSummarizeView> {
GenerateContentResponse? response;
bool loading = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {});
WidgetsBinding.instance.addPostFrameCallback((_) async {
setState(() {
loading = true;
});
final newsManager = ref.read(_newsManager);

response = await newsManager.summarize(article: widget.article.url ?? '');
setState(() {
loading = false;
});
});
}

@override
Widget build(BuildContext context) {
final newsManager = ref.read(_newsManager);

return Scaffold(
body: FutureBuilder<GenerateContentResponse>(
future: newsManager.summarize(article: widget.article.url ?? ''),
body: FutureBuilder<GenerateContentResponse?>(
future: Future.value(response),
builder: (context, snapshot) {
if (loading) {
return const LoadingWidget();
}

return Scaffold(
appBar: AppBar(
leading: IconButton(
Expand All @@ -49,35 +65,48 @@ class _AiSummarizeViewState extends ConsumerState<AiSummarizeView> {
width: 35,
)),
actions: [
IconButton(
onPressed: () async {
if(snapshot.data?.text == null) return;
await Clipboard.setData(
ClipboardData(text: snapshot.data?.text ?? ''))
.then((value) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10))),
content: Text('Copied to clipboard!')));
});
},
icon: const Icon(Icons.copy_rounded))
if (snapshot.data != null) ...{
IconButton(
onPressed: () async {
if (snapshot.data?.text == null) return;
await Clipboard.setData(ClipboardData(
text: snapshot.data?.text ?? ''))
.then((value) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10))),
content: Text('Copied to clipboard!')));
});
},
icon: const Icon(Icons.copy_rounded))
}
],
),
floatingActionButton: snapshot.data == null
? null
: FloatingActionButton(
onPressed: () async => showTranslationList(),
child: const Icon(Icons.translate),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: FutureBuilder<GenerateContentResponse>(
future: newsManager.summarize(
article: widget.article.url ?? ''),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting &&
!snapshot.hasData) {
return const LoadingWidget();
}
return SelectableText.rich(
child: snapshot.data == null
? Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.sentiment_dissatisfied),
Text(
'${snapshot.error ?? 'Couldn\t summarize article'}',
style: Theme.of(context).textTheme.bodyLarge,
),
],
))
: SelectableText.rich(
TextSpan(
style: Theme.of(context)
.textTheme
Expand All @@ -87,13 +116,50 @@ class _AiSummarizeViewState extends ConsumerState<AiSummarizeView> {
snapshot.data?.text ?? '',
),
),
);
}),
),
),
);
}),
);
}

showTranslationList() async {
final language = await showModalBottomSheet(
context: context,
builder: (context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
ListTile(
title: const Text('French'),
onTap: () => Navigator.pop(context, 'French'),
),
const Divider(),
ListTile(
title: const Text('Spanish'),
onTap: () => Navigator.pop(context, 'Spanish'),
)
],
),
);
});

if (language == null) return;
if (response?.text == null) return;
setState(() {
loading = true;
});
final translate = await ref
.read(_newsManager)
.translate(text: response?.text ?? '', language: language);
log('translate: ${translate.text}');

setState(() {
response = translate;
loading = false;
});
}
}

class LoadingWidget extends StatelessWidget {
Expand Down
52 changes: 23 additions & 29 deletions lib/views/news_detail_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import 'package:news_assistant/router.dart';
import 'package:share_plus/share_plus.dart';

class NewsDetailView extends StatelessWidget {
const NewsDetailView({super.key, required this.articles, required this.heroTag});
const NewsDetailView(
{super.key, required this.articles, required this.heroTag});

final Articles articles;
final String heroTag;

@override
Widget build(BuildContext context) {

return Scaffold(
appBar: AppBar(
onBookmark: () {},
Expand All @@ -28,34 +28,28 @@ class NewsDetailView extends StatelessWidget {
body: ListView(
padding: const EdgeInsets.all(16),
children: [
InkWell(
onTap: () =>
context.pushNamed(Routes.summarize.name, extra: articles),
borderRadius: BorderRadius.circular(45),
child: Align(
alignment: Alignment.centerRight,
child: Material(
borderRadius: BorderRadius.circular(45),
color: Theme.of(context).colorScheme.primary.withOpacity(.2),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(Svgs.stars,
Align(
alignment: Alignment.centerRight,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.primary.withOpacity(.2),
shape: const StadiumBorder()),
onPressed: () =>
context.pushNamed(Routes.summarize.name, extra: articles),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(Svgs.stars,
color: Theme.of(context).colorScheme.primary),
const SizedBox(width: 6),
Text(
'AI Summarize',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).colorScheme.primary),
const SizedBox(width: 6),
Text(
'AI Summarize',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).colorScheme.primary),
)
],
),
),
),
),
)
],
)),
),
SizedBox(
height: 310,
Expand Down