feat: Add remote_version endpoint to mock server, resolve versions, update search

This commit is contained in:
Anton Stubenbord
2023-05-29 15:54:43 +02:00
parent f46ae73f49
commit d8ee418828
14 changed files with 4429 additions and 75 deletions

View File

@@ -5,4 +5,6 @@ files: [
"type" : "arb", "type" : "arb",
} }
] ]
# Add your credentials here # Add your credentials here
"project_id": "568557"
"api_token_env": "CROWDIN_PAPERLESS_MOBILE_API_TOKEN"

View File

@@ -51,7 +51,6 @@ Future<void> pushDocumentSearchPage(BuildContext context) {
builder: (context, _) { builder: (context, _) {
return BlocProvider( return BlocProvider(
create: (context) => DocumentSearchCubit( create: (context) => DocumentSearchCubit(
context.read(),
context.read(), context.read(),
context.read(), context.read(),
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(currentUser)!, Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(currentUser)!,

View File

@@ -4,7 +4,6 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart';
@@ -16,7 +15,6 @@ class DocumentSearchCubit extends Cubit<DocumentSearchState> with DocumentPaging
@override @override
final PaperlessDocumentsApi api; final PaperlessDocumentsApi api;
final LabelRepository _labelRepository;
@override @override
final DocumentChangedNotifier notifier; final DocumentChangedNotifier notifier;
@@ -24,22 +22,8 @@ class DocumentSearchCubit extends Cubit<DocumentSearchState> with DocumentPaging
DocumentSearchCubit( DocumentSearchCubit(
this.api, this.api,
this.notifier, this.notifier,
this._labelRepository,
this._userAppState, this._userAppState,
) : super(DocumentSearchState(searchHistory: _userAppState.documentSearchHistory)) { ) : super(DocumentSearchState(searchHistory: _userAppState.documentSearchHistory)) {
_labelRepository.addListener(
this,
onChanged: (labels) {
emit(
state.copyWith(
correspondents: labels.correspondents,
documentTypes: labels.documentTypes,
tags: labels.tags,
storagePaths: labels.storagePaths,
),
);
},
);
notifier.addListener( notifier.addListener(
this, this,
onDeleted: remove, onDeleted: remove,
@@ -95,8 +79,6 @@ class DocumentSearchCubit extends Cubit<DocumentSearchState> with DocumentPaging
state.copyWith( state.copyWith(
isLoading: true, isLoading: true,
view: SearchView.suggestions, view: SearchView.suggestions,
value: [],
suggestions: [],
), ),
); );
final suggestions = await api.autocomplete(query); final suggestions = await api.autocomplete(query);
@@ -122,7 +104,6 @@ class DocumentSearchCubit extends Cubit<DocumentSearchState> with DocumentPaging
@override @override
Future<void> close() { Future<void> close() {
notifier.removeListener(this); notifier.removeListener(this);
_labelRepository.removeListener(this);
return super.close(); return super.close();
} }

View File

@@ -57,10 +57,6 @@ class DocumentSearchState extends DocumentPagingState {
List<String>? suggestions, List<String>? suggestions,
SearchView? view, SearchView? view,
ViewType? viewType, ViewType? viewType,
Map<int, Correspondent>? correspondents,
Map<int, DocumentType>? documentTypes,
Map<int, Tag>? tags,
Map<int, StoragePath>? storagePaths,
}) { }) {
return DocumentSearchState( return DocumentSearchState(
value: value ?? this.value, value: value ?? this.value,
@@ -79,3 +75,31 @@ class DocumentSearchState extends DocumentPagingState {
Map<String, dynamic> toJson() => _$DocumentSearchStateToJson(this); Map<String, dynamic> toJson() => _$DocumentSearchStateToJson(this);
} }
// sealed class DocumentSearchState1 {}
// class DocumentSearchStateInitial {}
// class SuggestionsLoadingState extends DocumentSearchState1 {
// final List<String> history;
// SuggestionsLoadingState({required this.history});
// }
// class SuggestionsLoadedState extends DocumentSearchState1 {
// final List<String> history;
// final List<String> suggestions;
// SuggestionsLoadedState({
// required this.history,
// required this.suggestions,
// });
// }
// class SuggestionsErrorState extends DocumentSearchState1 {}
// class ResultsLoadingState extends DocumentSearchState1 {}
// class ResultsLoadedState extends DocumentSearchState1 {}
// class ResultsErrorState extends DocumentSearchState1 {}

View File

@@ -93,7 +93,6 @@ class _DocumentSearchBarState extends State<DocumentSearchBar> {
], ],
child: Provider( child: Provider(
create: (_) => DocumentSearchCubit( create: (_) => DocumentSearchCubit(
context.read(),
context.read(), context.read(),
context.read(), context.read(),
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState) Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState)

View File

@@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:math' as math;
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/navigation/push_routes.dart';
@@ -12,8 +12,6 @@ import 'package:paperless_mobile/features/documents/view/widgets/adaptive_docume
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'dart:math' as math;
class DocumentSearchPage extends StatefulWidget { class DocumentSearchPage extends StatefulWidget {
const DocumentSearchPage({super.key}); const DocumentSearchPage({super.key});
@@ -28,13 +26,15 @@ class _DocumentSearchPageState extends State<DocumentSearchPage> {
Timer? _debounceTimer; Timer? _debounceTimer;
String get query => _queryController.text; String get query => _queryController.text;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
const double progressIndicatorHeight = 4;
final theme = Theme.of(context); final theme = Theme.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: theme.colorScheme.surfaceVariant, backgroundColor: theme.colorScheme.surfaceVariant,
toolbarHeight: 72, toolbarHeight: 72 - progressIndicatorHeight,
leading: BackButton( leading: BackButton(
color: theme.colorScheme.onSurfaceVariant, color: theme.colorScheme.onSurfaceVariant,
), ),
@@ -77,13 +77,13 @@ class _DocumentSearchPageState extends State<DocumentSearchPage> {
).padded(), ).padded(),
], ],
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: Size.fromHeight(1), preferredSize: const Size.fromHeight(progressIndicatorHeight),
child: BlocBuilder<DocumentSearchCubit, DocumentSearchState>( child: BlocBuilder<DocumentSearchCubit, DocumentSearchState>(
builder: (context, state) { builder: (context, state) {
if (state.isLoading) { if (state.isLoading) {
return const LinearProgressIndicator(); return const LinearProgressIndicator();
} }
return const SizedBox.shrink(); return ColoredBox(color: Theme.of(context).colorScheme.surface);
}, },
), ),
), ),
@@ -140,6 +140,13 @@ class _DocumentSearchPageState extends State<DocumentSearchPage> {
childCount: suggestions.length, childCount: suggestions.length,
), ),
), ),
if (suggestions.isEmpty && historyMatches.isEmpty)
SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverToBoxAdapter(
child: Center(child: Text(S.of(context)!.noMatchesFound)),
),
),
], ],
); );
} }

View File

@@ -1,11 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart'; import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart';
import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart';
import 'package:paperless_mobile/features/document_search/view/document_search_bar.dart'; import 'package:paperless_mobile/features/document_search/view/document_search_bar.dart';
class SliverSearchBar extends StatelessWidget { class SliverSearchBar extends StatelessWidget {
@@ -30,15 +27,7 @@ class SliverSearchBar extends StatelessWidget {
maxExtent: kToolbarHeight, maxExtent: kToolbarHeight,
child: Container( child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16.0), margin: const EdgeInsets.symmetric(horizontal: 16.0),
child: BlocProvider( child: const DocumentSearchBar(),
create: (context) => DocumentSearchCubit(
context.read(),
context.read(),
context.read(),
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(currentUser)!,
),
child: const DocumentSearchBar(),
),
), ),
), ),
); );

View File

@@ -78,11 +78,12 @@ void main() async {
// URL: http://localhost:3131 // URL: http://localhost:3131
// Login: admin:test // Login: admin:test
await LocalMockApiServer( await LocalMockApiServer(
RandomDelayGenerator( // RandomDelayGenerator(
const Duration(milliseconds: 100), // const Duration(milliseconds: 100),
const Duration(milliseconds: 800), // const Duration(milliseconds: 800),
), // ),
).start(); )
.start();
} }
await _initHive(); await _initHive();
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();

View File

File diff suppressed because it is too large Load Diff

View File

@@ -5,16 +5,12 @@ export 'response_delay_generator.dart';
import 'dart:convert'; import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:mock_server/english_words.dart';
import 'package:mock_server/response_delay_generator.dart'; import 'package:mock_server/response_delay_generator.dart';
import 'package:shelf/shelf.dart'; import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io; import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_router/shelf_router.dart' as shelf_router; import 'package:shelf_router/shelf_router.dart' as shelf_router;
import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/services.dart' show rootBundle;
Logger log = Logger('LocalMockApiServer'); Logger log = Logger('LocalMockApiServer');
@@ -233,6 +229,23 @@ class LocalMockApiServer {
var data = await loadFixture('statistics'); var data = await loadFixture('statistics');
return JsonMockResponse.ok(data, _delayGenerator.nextDelay()); return JsonMockResponse.ok(data, _delayGenerator.nextDelay());
}); });
app.get('/api/search/autocomplete/', (Request req) async {
log.info("Responding to /api/search/autocomplete");
final term = req.url.queryParameters["term"] ?? '';
final limit = int.parse(req.url.queryParameters["limit"] ?? '5');
return JsonMockResponse.ok(
mostFrequentWords.where((element) => element.startsWith(term)).take(limit).toList(),
_delayGenerator.nextDelay(),
);
});
app.get('/api/remote_version/', (Request req) async {
return JsonMockResponse.ok({
'version': 'v1.14.5',
'update_available': false,
}, _delayGenerator.nextDelay());
});
} }
Future<void> start() async { Future<void> start() async {

View File

@@ -65,7 +65,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
); );
} }
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -82,7 +82,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
throw const PaperlessServerException(ErrorCode.documentUpdateFailed); throw const PaperlessServerException(ErrorCode.documentUpdateFailed);
} }
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -108,7 +108,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
throw const PaperlessServerException(ErrorCode.documentLoadFailed); throw const PaperlessServerException(ErrorCode.documentLoadFailed);
} }
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -122,7 +122,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
} }
throw const PaperlessServerException(ErrorCode.documentDeleteFailed); throw const PaperlessServerException(ErrorCode.documentDeleteFailed);
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -148,7 +148,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
} }
throw const PaperlessServerException(ErrorCode.documentPreviewFailed); throw const PaperlessServerException(ErrorCode.documentPreviewFailed);
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -170,7 +170,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
} on PaperlessServerException { } on PaperlessServerException {
throw const PaperlessServerException(ErrorCode.documentAsnQueryFailed); throw const PaperlessServerException(ErrorCode.documentAsnQueryFailed);
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -189,7 +189,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
); );
} }
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -206,7 +206,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
); );
return response.data; return response.data;
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -226,7 +226,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
); );
return response.data; return response.data;
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -239,7 +239,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
response.data as Map<String, dynamic>, response.data as Map<String, dynamic>,
); );
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -258,7 +258,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
} }
throw const PaperlessServerException(ErrorCode.autocompleteQueryError); throw const PaperlessServerException(ErrorCode.autocompleteQueryError);
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -271,7 +271,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
} }
throw const PaperlessServerException(ErrorCode.suggestionsQueryError); throw const PaperlessServerException(ErrorCode.suggestionsQueryError);
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
@@ -285,7 +285,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
return null; return null;
} }
} on DioError catch (err) { } on DioError catch (err) {
throw err.error!; throw err.error ?? const PaperlessServerException.unknown();
} }
} }
} }

View File

@@ -14,7 +14,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
equatable: ^2.0.5 equatable: ^2.0.5
http: ^0.13.5 http: ^1.0.0
json_annotation: ^4.8.1 json_annotation: ^4.8.1
intl: any #^0.18.0 intl: any #^0.18.0
dio: ^5.0.0 dio: ^5.0.0

View File

@@ -796,10 +796,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.5" version: "1.0.0"
http_methods: http_methods:
dependency: transitive dependency: transitive
description: description:
@@ -1072,10 +1072,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: package_info_plus name: package_info_plus
sha256: d39e8fbff4c5aef4592737e25ad6ac500df006ce7a7a8e1f838ce1256e167542 sha256: "28386bbe89ab5a7919a47cea99cdd1128e5a6e0bbd7eaafe20440ead84a15de3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.1"
package_info_plus_platform_interface: package_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:

View File

@@ -20,7 +20,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 2.3.0+35 version: 2.3.0+35
environment: environment:
sdk: ">=2.19.0 <4.0.0" sdk: ">=3.0.0 <4.0.0"
# Dependencies specify other packages that your package needs in order to work. # Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions # To automatically upgrade your package dependencies to the latest versions
@@ -51,14 +51,14 @@ dependencies:
url_launcher: ^6.1.2 url_launcher: ^6.1.2
file_picker: ^5.2.4 file_picker: ^5.2.4
web_socket_channel: ^2.2.0 web_socket_channel: ^2.2.0
http: ^0.13.4 http: ^1.0.0
flutter_cache_manager: ^3.3.0 flutter_cache_manager: ^3.3.0
cached_network_image: ^3.2.1 cached_network_image: ^3.2.1
shimmer: ^2.0.0 shimmer: ^2.0.0
flutter_bloc: ^8.1.1 flutter_bloc: ^8.1.1
equatable: ^2.0.3 equatable: ^2.0.3
flutter_form_builder: ^8.0.0 flutter_form_builder: ^8.0.0
package_info_plus: ^4.0.0 package_info_plus: ^4.0.1
font_awesome_flutter: ^10.1.0 font_awesome_flutter: ^10.1.0
local_auth: ^2.1.2 local_auth: ^2.1.2
connectivity_plus: ^4.0.0 connectivity_plus: ^4.0.0
@@ -98,6 +98,7 @@ dependencies:
dependency_overrides: dependency_overrides:
intl: ^0.18.0 intl: ^0.18.0
graphs: 2.2.0 graphs: 2.2.0
http: ^1.0.0
dev_dependencies: dev_dependencies: