@@ -19,6 +19,8 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
1919 String selectedSortOrder = '-' ;
2020 DateTime ? _publishedAfter;
2121 DateTime ? _publishedBefore;
22+ final TextEditingController issnController = TextEditingController ();
23+ bool _isOpenAccess = false ;
2224
2325 String _dateMode = 'none' ;
2426 // bool _filtersExpanded = false;
@@ -33,6 +35,13 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
3335 _checkApiKey ();
3436 }
3537
38+ @override
39+ void dispose () {
40+ issnController.dispose ();
41+ queryNameController.dispose ();
42+ super .dispose ();
43+ }
44+
3645 void _addQueryPart (String type) {
3746 setState (() {
3847 if (queryParts.isNotEmpty && queryParts.last['type' ] != 'operator' ) {
@@ -125,6 +134,7 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
125134 String ? sortField = selectedSortField == '-' ? null : selectedSortField;
126135 String ? sortOrder = selectedSortOrder == '-' ? null : selectedSortOrder;
127136 String ? dateFilter;
137+ String ? issnFilter;
128138
129139 String formatDate (DateTime d) => d.toIso8601String ().split ('T' )[0 ];
130140
@@ -139,6 +149,16 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
139149 "to_publication_date:${formatDate (_publishedBefore !)}" ;
140150 }
141151
152+ if (issnController.text.trim ().isNotEmpty) {
153+ final issns = issnController.text
154+ .split (',' )
155+ .map ((e) => e.trim ())
156+ .where ((e) => e.isNotEmpty)
157+ .join ('|' );
158+
159+ issnFilter = "locations.source.issn:$issns " ;
160+ }
161+
142162 try {
143163 showDialog (
144164 context: context,
@@ -182,8 +202,33 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
182202 }
183203 }
184204
205+ String issnPart = '' ;
206+
207+ if (issnFilter != null && issnFilter.isNotEmpty) {
208+ if (searchField.startsWith ('filter=' )) {
209+ issnPart = ',$issnFilter ' ;
210+ } else if (datePart.isNotEmpty) {
211+ issnPart = ',$issnFilter ' ;
212+ } else {
213+ issnPart = '&filter=$issnFilter ' ;
214+ }
215+ }
216+
217+ String oaPart = '' ;
218+ if (_isOpenAccess) {
219+ if (searchField.startsWith ('filter=' )) {
220+ oaPart = ',is_oa:true' ;
221+ } else if (datePart.isNotEmpty || issnPart.isNotEmpty) {
222+ oaPart = ',is_oa:true' ;
223+ } else {
224+ oaPart = '&filter=is_oa:true' ;
225+ }
226+ }
227+
185228 queryString = '$searchField $query '
186229 '$datePart '
230+ '$issnPart '
231+ '$oaPart '
187232 '$selectedSortBy '
188233 '$selectedSortOrder ' ;
189234 await dbHelper.saveSearchQuery (queryName, queryString, 'OpenAlex' );
@@ -208,6 +253,8 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
208253 if (sortField != null ) 'sortField' : sortField,
209254 if (sortOrder != null ) 'sortOrder' : sortOrder,
210255 if (dateFilter != null ) 'dateFilter' : dateFilter,
256+ if (issnFilter != null ) 'issnFilter' : issnFilter,
257+ 'isOpenAccess' : _isOpenAccess,
211258 },
212259 source: 'OpenAlex' ,
213260 ),
@@ -281,6 +328,16 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
281328 border: OutlineInputBorder (),
282329 ),
283330 ),
331+ SizedBox (height: 16 ),
332+ TextFormField (
333+ controller: issnController,
334+ decoration: InputDecoration (
335+ labelText: AppLocalizations .of (context)! .issnFilter,
336+ hintText: "0022-1694, 1234-5678" ,
337+ border: OutlineInputBorder (),
338+ ),
339+ ),
340+
284341 SizedBox (height: 16 ),
285342
286343 DropdownButtonFormField <String >(
@@ -416,8 +473,24 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
416473 ),
417474 ],
418475 ),
476+ SizedBox (height: 16 ),
477+ SwitchListTile (
478+ title: Text (
479+ AppLocalizations .of (context)! .openAccessOnly,
480+ style: TextStyle (fontWeight: FontWeight .w500),
481+ ),
482+ value: _isOpenAccess,
483+ onChanged: (bool value) {
484+ setState (() {
485+ _isOpenAccess = value;
486+ });
487+ },
488+ ),
489+ SizedBox (height: 10 ),
490+ Divider (
491+ height: 5 ,
492+ ),
419493 SizedBox (height: 10 ),
420-
421494 // Dynamic query builder
422495 Column (
423496 children: () {
@@ -548,27 +621,29 @@ class OpenAlexSearchFormState extends State<OpenAlexSearchForm> {
548621 ],
549622 ),*/
550623 SizedBox (height: 20 ),
551- Text (
552- AppLocalizations . of (context) ! .saveQuery,
553- style : TextStyle (fontWeight : FontWeight .bold) ,
554- ),
555- Switch (
624+ SwitchListTile (
625+ title : Text (
626+ AppLocalizations . of (context) ! .saveQuery ,
627+ style : TextStyle (fontWeight : FontWeight .w600 ),
628+ ),
556629 value: saveQuery,
557630 onChanged: (bool value) {
558631 setState (() {
559632 saveQuery = value;
560633 });
561634 },
562635 ),
563- SizedBox (height : 8 ),
564- if (saveQuery)
636+ if (saveQuery) ...[
637+ const SizedBox (height : 8 ),
565638 TextFormField (
566639 controller: queryNameController,
567640 decoration: InputDecoration (
568641 labelText: AppLocalizations .of (context)! .queryName,
569- border: OutlineInputBorder (),
642+ border: const OutlineInputBorder (),
570643 ),
571644 ),
645+ ],
646+
572647 SizedBox (height: 70 ),
573648 ],
574649 ),
0 commit comments