Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions 1 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ If you run into any issue while using Wispar, have a question or want to share y
<li><a href="https://unpaywall.org/" target='_blank'>Unpaywall</a></li>
<li><a href="https://www.crossref.org/" target='_blank'>Crossref</a></li>
<li><a href="https://openalex.org/" target='_blank'>OpenAlex</a></li>
<li><a href="https://github.com/hitfyd/ShowJCR" target='_blank'>Journal topics database</a> by <a href="https://github.com/hitfyd"target='_blank'> hitfyd</a></li>
</ul>

## Screenshots
Expand Down
8 changes: 8 additions & 0 deletions 8 lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@
"journals": "Journals",
"@journals": {
"description": "The journals menu button and the app bar title when in the journals screen."
},
"countJournals": "{count, plural, =0{No journals} =1{1 journal} other{{count} journals}}",
"@countJournals": {
"placeholders": {"count": {}}
},
"queries": "Queries",
"@queries": {
Expand Down Expand Up @@ -189,6 +193,8 @@
"@searchByDOI": {},
"searchByTitle": "Search by title",
"@searchByTitle": {},
"searchByTopic": "Search by topic",
"@searchByTopic":{},
"searchByISSN": "Search by ISSN",
"@searchByISSN": {},
"queryHasNoNameError": "You must enter a query name in order to save it.",
Expand All @@ -215,6 +221,8 @@
"@category": {},
"publisher": "Publisher",
"@publisher": {},
"publisherName": "Publisher: {publisherName}",
"@publisherName":{},
"publishedin": "Published in",
"@publishedin": {},
"subjects": "Subjects",
Expand Down
13 changes: 13 additions & 0 deletions 13 lib/models/journal_topics_models.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class JournalTopicsCsv {
final String journal;
final String issn;
final String eissn;
final List<String> categories;

JournalTopicsCsv({
required this.journal,
required this.issn,
required this.eissn,
required this.categories,
});
}
28 changes: 15 additions & 13 deletions 28 lib/screens/journals_search_results_screen.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import 'package:flutter/material.dart';
import '../generated_l10n/app_localizations.dart';
import '../services/crossref_api.dart';
import '../models/crossref_journals_models.dart' as Journals;
import '../widgets/journal_search_results_card.dart';
import '../services/logs_helper.dart';
import 'package:wispar/generated_l10n/app_localizations.dart';
import 'package:wispar/services/crossref_api.dart';
import 'package:wispar/models/crossref_journals_models.dart' as Journals;
import 'package:wispar/widgets/journal_search_results_card.dart';
import 'package:wispar/services/logs_helper.dart';

class SearchResultsScreen extends StatefulWidget {
final ListAndMore<Journals.Item> searchResults;
final String searchQuery;

const SearchResultsScreen({
Key? key,
super.key,
required this.searchResults,
required this.searchQuery,
}) : super(key: key);
});

@override
_SearchResultsScreenState createState() => _SearchResultsScreenState();
SearchResultsScreenState createState() => SearchResultsScreenState();
}

class _SearchResultsScreenState extends State<SearchResultsScreen> {
class SearchResultsScreenState extends State<SearchResultsScreen> {
final logger = LogsService().logger;
List<Journals.Item> items = [];
bool isLoading = false;
Expand Down Expand Up @@ -101,10 +101,12 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
}
});
} catch (e, stackTrace) {
logger.severe('Failed to load more journals.', e, stackTrace);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text(AppLocalizations.of(context)!.failLoadMorePublication)));
if (mounted) {
logger.severe('Failed to load more journals.', e, stackTrace);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text(AppLocalizations.of(context)!.failLoadMorePublication)));
}
} finally {
setState(() => isLoading = false);
}
Expand Down
46 changes: 46 additions & 0 deletions 46 lib/services/journal_topics_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:http/http.dart' as http;
import 'package:wispar/models/journal_topics_models.dart';
import 'package:csv/csv.dart';

Future<List<JournalTopicsCsv>> fetchCsvCategories() async {
final url =
"https://raw.githubusercontent.com/hitfyd/ShowJCR/refs/heads/master/%E4%B8%AD%E7%A7%91%E9%99%A2%E5%88%86%E5%8C%BA%E8%A1%A8%E5%8F%8AJCR%E5%8E%9F%E5%A7%8B%E6%95%B0%E6%8D%AE%E6%96%87%E4%BB%B6/JCR2024-UTF8.csv";

final response = await http.get(Uri.parse(url));
if (response.statusCode != 200) {
throw Exception("Failed to load CSV");
}

final csvRows = const CsvToListConverter(eol: '\n', shouldParseNumbers: false)
.convert(response.body);

final List<JournalTopicsCsv> entries = [];

for (int i = 1; i < csvRows.length; i++) {
final row = csvRows[i];
final journal = row[0].toString();
final issn =
(row[1].toString().toUpperCase() == 'N/A') ? '' : row[1].toString();
final eissn =
(row[2].toString().toUpperCase() == 'N/A') ? '' : row[2].toString();
final categories = row[3].toString().split(';').map((c) {
String clean = c.replaceAll(RegExp(r'\([A-Z]+\)$'), '').trim();
return clean
.toLowerCase()
.split(' ')
.map((word) => word.isEmpty
? ''
: '${word[0].toUpperCase()}${word.substring(1)}')
.join(' ');
}).toList();

entries.add(JournalTopicsCsv(
journal: journal,
issn: issn,
eissn: eissn,
categories: categories,
));
}

return entries;
}
103 changes: 55 additions & 48 deletions 103 lib/widgets/article_crossref_search_form.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import 'package:flutter/material.dart';
import '../generated_l10n/app_localizations.dart';
import 'article_doi_search_form.dart';
import 'article_query_search_form.dart';
import '../services/crossref_api.dart';
import '../screens/article_screen.dart';
import '../services/logs_helper.dart';
import 'package:wispar/generated_l10n/app_localizations.dart';
import 'package:wispar/widgets/article_doi_search_form.dart';
import 'package:wispar/widgets/article_query_search_form.dart';
import 'package:wispar/services/crossref_api.dart';
import 'package:wispar/screens/article_screen.dart';
import 'package:wispar/services/logs_helper.dart';

class CrossRefSearchForm extends StatefulWidget {
const CrossRefSearchForm({super.key});

@override
_CrossRefSearchFormState createState() => _CrossRefSearchFormState();
CrossRefSearchFormState createState() => CrossRefSearchFormState();
}

class _CrossRefSearchFormState extends State<CrossRefSearchForm> {
class CrossRefSearchFormState extends State<CrossRefSearchForm> {
final logger = LogsService().logger;
int selectedSearchIndex = 0; // 0 for Query, 1 for DOI
final TextEditingController doiController = TextEditingController();
Expand Down Expand Up @@ -63,41 +65,47 @@ class _CrossRefSearchFormState extends State<CrossRefSearchForm> {
final article = await CrossRefApi.getWorkByDOI(extractedDoi);

// Dismiss the loading dialog
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArticleScreen(
doi: article.doi,
title: article.title,
issn: article.issn,
abstract: article.abstract,
journalTitle: article.journalTitle,
publishedDate: article.publishedDate,
authors: article.authors,
url: article.url,
license: article.license,
licenseName: article.licenseName,
publisher: article.publisher,
if (mounted) {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArticleScreen(
doi: article.doi,
title: article.title,
issn: article.issn,
abstract: article.abstract,
journalTitle: article.journalTitle,
publishedDate: article.publishedDate,
authors: article.authors,
url: article.url,
license: article.license,
licenseName: article.licenseName,
publisher: article.publisher,
),
),
),
);
);
}
} catch (e, stackTrace) {
logger.severe(
'Error searching by DOI for DOI ${doi}.', e, stackTrace);
Navigator.pop(context); // Close loading dialog
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.errorOccured)),
);
logger.severe('Error searching by DOI for DOI $doi.', e, stackTrace);
if (mounted) {
Navigator.pop(context); // Close loading dialog
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.errorOccured)),
);
}
}
}
} catch (e, stackTrace) {
logger.severe('Error searching articles using ${selectedSearchIndex}.', e,
logger.severe('Error searching articles using $selectedSearchIndex.', e,
stackTrace);
Navigator.pop(context); // Close loading dialog
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.errorOccured)),
);
if (mounted) {
Navigator.pop(context); // Close loading dialog
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.errorOccured)),
);
}
}
}

Expand All @@ -118,6 +126,16 @@ class _CrossRefSearchFormState extends State<CrossRefSearchForm> {
child: LayoutBuilder(
builder: (context, constraints) {
return ToggleButtons(
isSelected: [
selectedSearchIndex == 0,
selectedSearchIndex == 1,
],
onPressed: (int index) {
setState(() {
selectedSearchIndex = index;
});
},
borderRadius: BorderRadius.circular(15.0),
children: [
Container(
width: constraints.maxWidth / 2 - 1.5,
Expand All @@ -131,16 +149,6 @@ class _CrossRefSearchFormState extends State<CrossRefSearchForm> {
child: Text(AppLocalizations.of(context)!.searchByDOI),
),
],
isSelected: [
selectedSearchIndex == 0,
selectedSearchIndex == 1,
],
onPressed: (int index) {
setState(() {
selectedSearchIndex = index;
});
},
borderRadius: BorderRadius.circular(15.0),
);
},
),
Expand All @@ -155,7 +163,6 @@ class _CrossRefSearchFormState extends State<CrossRefSearchForm> {
floatingActionButton: FloatingActionButton(
onPressed: _handleSearch,
child: Icon(Icons.search),
shape: CircleBorder(),
),
);
}
Expand Down
1 change: 0 additions & 1 deletion 1 lib/widgets/article_openAlex_search_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,6 @@ class _OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
floatingActionButton: FloatingActionButton(
onPressed: _executeSearch,
child: Icon(Icons.search),
shape: CircleBorder(),
),
);
}
Expand Down
10 changes: 6 additions & 4 deletions 10 lib/widgets/article_search_form.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import 'package:flutter/material.dart';
import './article_crossref_search_form.dart';
import './article_openAlex_search_form.dart';
import 'package:wispar/widgets/article_crossref_search_form.dart';
import 'package:wispar/widgets/article_openAlex_search_form.dart';

class ArticleSearchScreen extends StatefulWidget {
const ArticleSearchScreen({super.key});

@override
_ArticleSearchScreenState createState() => _ArticleSearchScreenState();
ArticleSearchScreenState createState() => ArticleSearchScreenState();
}

class _ArticleSearchScreenState extends State<ArticleSearchScreen> {
class ArticleSearchScreenState extends State<ArticleSearchScreen> {
int selectedProviderIndex = 0; // 0 = OpenAlex, 1 = Crossref

@override
Expand Down
11 changes: 6 additions & 5 deletions 11 lib/widgets/journal_header.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import '../generated_l10n/app_localizations.dart';
import '../models/crossref_journals_models.dart' as Journals;
import './journal_follow_button.dart';
import 'package:wispar/generated_l10n/app_localizations.dart';
import 'package:wispar/models/crossref_journals_models.dart' as Journals;
import 'package:wispar/widgets/journal_follow_button.dart';

class JournalInfoHeader extends SliverPersistentHeaderDelegate {
final String title;
Expand Down Expand Up @@ -57,8 +57,9 @@ class JournalInfoHeader extends SliverPersistentHeaderDelegate {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 8.0),
Text(
'${AppLocalizations.of(context)!.publisher}: ${publisher}'),
if (publisher.isNotEmpty)
Text(AppLocalizations.of(context)!
.publisherName(publisher)),
Text('ISSN: ${issn}'),
SizedBox(height: 8.0),
],
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.