mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-06 13:15:49 -06:00
Added search bar on all pages
This commit is contained in:
@@ -1,217 +1,217 @@
|
|||||||
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/constants.dart';
|
// import 'package:paperless_mobile/constants.dart';
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
// import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
// import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
// import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
import 'package:paperless_mobile/features/settings/view/settings_page.dart';
|
// import 'package:paperless_mobile/features/settings/view/settings_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
// import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:url_launcher/link.dart';
|
// import 'package:url_launcher/link.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
// import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
/// Declares selectable actions in menu.
|
// /// Declares selectable actions in menu.
|
||||||
enum AppPopupMenuEntries {
|
// enum AppPopupMenuEntries {
|
||||||
// Documents preview
|
// // Documents preview
|
||||||
documentsSelectListView,
|
// documentsSelectListView,
|
||||||
documentsSelectGridView,
|
// documentsSelectGridView,
|
||||||
// Generic actions
|
// // Generic actions
|
||||||
openAboutThisAppDialog,
|
// openAboutThisAppDialog,
|
||||||
reportBug,
|
// reportBug,
|
||||||
openSettings,
|
// openSettings,
|
||||||
// Adds a divider
|
// // Adds a divider
|
||||||
divider;
|
// divider;
|
||||||
}
|
// }
|
||||||
|
|
||||||
class AppOptionsPopupMenu extends StatelessWidget {
|
// class AppOptionsPopupMenu extends StatelessWidget {
|
||||||
final List<AppPopupMenuEntries> displayedActions;
|
// final List<AppPopupMenuEntries> displayedActions;
|
||||||
const AppOptionsPopupMenu({
|
// const AppOptionsPopupMenu({
|
||||||
super.key,
|
// super.key,
|
||||||
required this.displayedActions,
|
// required this.displayedActions,
|
||||||
});
|
// });
|
||||||
|
|
||||||
@override
|
// @override
|
||||||
Widget build(BuildContext context) {
|
// Widget build(BuildContext context) {
|
||||||
return PopupMenuButton<AppPopupMenuEntries>(
|
// return PopupMenuButton<AppPopupMenuEntries>(
|
||||||
position: PopupMenuPosition.under,
|
// position: PopupMenuPosition.under,
|
||||||
icon: const Icon(Icons.more_vert),
|
// icon: const Icon(Icons.more_vert),
|
||||||
onSelected: (action) {
|
// onSelected: (action) {
|
||||||
switch (action) {
|
// switch (action) {
|
||||||
case AppPopupMenuEntries.documentsSelectListView:
|
// case AppPopupMenuEntries.documentsSelectListView:
|
||||||
context.read<ApplicationSettingsCubit>().setViewType(ViewType.list);
|
// context.read<ApplicationSettingsCubit>().setViewType(ViewType.list);
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.documentsSelectGridView:
|
// case AppPopupMenuEntries.documentsSelectGridView:
|
||||||
context.read<ApplicationSettingsCubit>().setViewType(ViewType.grid);
|
// context.read<ApplicationSettingsCubit>().setViewType(ViewType.grid);
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.openAboutThisAppDialog:
|
// case AppPopupMenuEntries.openAboutThisAppDialog:
|
||||||
_showAboutDialog(context);
|
// _showAboutDialog(context);
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.openSettings:
|
// case AppPopupMenuEntries.openSettings:
|
||||||
Navigator.of(context).push(
|
// Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
// MaterialPageRoute(
|
||||||
builder: (context) => BlocProvider.value(
|
// builder: (context) => BlocProvider.value(
|
||||||
value: context.read<ApplicationSettingsCubit>(),
|
// value: context.read<ApplicationSettingsCubit>(),
|
||||||
child: const SettingsPage(),
|
// child: const SettingsPage(),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.reportBug:
|
// case AppPopupMenuEntries.reportBug:
|
||||||
launchUrlString(
|
// launchUrlString(
|
||||||
'https://github.com/astubenbord/paperless-mobile/issues/new',
|
// 'https://github.com/astubenbord/paperless-mobile/issues/new',
|
||||||
);
|
// );
|
||||||
break;
|
// break;
|
||||||
default:
|
// default:
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
itemBuilder: _buildEntries,
|
// itemBuilder: _buildEntries,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
PopupMenuItem<AppPopupMenuEntries> _buildReportBugTile(BuildContext context) {
|
// PopupMenuItem<AppPopupMenuEntries> _buildReportBugTile(BuildContext context) {
|
||||||
return PopupMenuItem(
|
// return PopupMenuItem(
|
||||||
value: AppPopupMenuEntries.reportBug,
|
// value: AppPopupMenuEntries.reportBug,
|
||||||
padding: EdgeInsets.zero,
|
// padding: EdgeInsets.zero,
|
||||||
child: ListTile(
|
// child: ListTile(
|
||||||
leading: const Icon(Icons.bug_report),
|
// leading: const Icon(Icons.bug_report),
|
||||||
title: Text(S.of(context).appDrawerReportBugLabel),
|
// title: Text(S.of(context).appDrawerReportBugLabel),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
PopupMenuItem<AppPopupMenuEntries> _buildSettingsTile(BuildContext context) {
|
// PopupMenuItem<AppPopupMenuEntries> _buildSettingsTile(BuildContext context) {
|
||||||
return PopupMenuItem(
|
// return PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
// padding: EdgeInsets.zero,
|
||||||
value: AppPopupMenuEntries.openSettings,
|
// value: AppPopupMenuEntries.openSettings,
|
||||||
child: ListTile(
|
// child: ListTile(
|
||||||
leading: const Icon(Icons.settings_outlined),
|
// leading: const Icon(Icons.settings_outlined),
|
||||||
title: Text(S.of(context).appDrawerSettingsLabel),
|
// title: Text(S.of(context).appDrawerSettingsLabel),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
PopupMenuItem<AppPopupMenuEntries> _buildAboutTile(BuildContext context) {
|
// PopupMenuItem<AppPopupMenuEntries> _buildAboutTile(BuildContext context) {
|
||||||
return PopupMenuItem(
|
// return PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
// padding: EdgeInsets.zero,
|
||||||
value: AppPopupMenuEntries.openAboutThisAppDialog,
|
// value: AppPopupMenuEntries.openAboutThisAppDialog,
|
||||||
child: ListTile(
|
// child: ListTile(
|
||||||
leading: const Icon(Icons.info_outline),
|
// leading: const Icon(Icons.info_outline),
|
||||||
title: Text(S.of(context).appDrawerAboutLabel),
|
// title: Text(S.of(context).appDrawerAboutLabel),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
PopupMenuItem<AppPopupMenuEntries> _buildListViewTile() {
|
// PopupMenuItem<AppPopupMenuEntries> _buildListViewTile() {
|
||||||
return PopupMenuItem(
|
// return PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
// padding: EdgeInsets.zero,
|
||||||
child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
// child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||||
builder: (context, state) {
|
// builder: (context, state) {
|
||||||
return ListTile(
|
// return ListTile(
|
||||||
leading: const Icon(Icons.list),
|
// leading: const Icon(Icons.list),
|
||||||
title: const Text("List"),
|
// title: const Text("List"),
|
||||||
trailing: state.preferredViewType == ViewType.list
|
// trailing: state.preferredViewType == ViewType.list
|
||||||
? const Icon(Icons.check)
|
// ? const Icon(Icons.check)
|
||||||
: null,
|
// : null,
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
value: AppPopupMenuEntries.documentsSelectListView,
|
// value: AppPopupMenuEntries.documentsSelectListView,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
PopupMenuItem<AppPopupMenuEntries> _buildGridViewTile() {
|
// PopupMenuItem<AppPopupMenuEntries> _buildGridViewTile() {
|
||||||
return PopupMenuItem(
|
// return PopupMenuItem(
|
||||||
value: AppPopupMenuEntries.documentsSelectGridView,
|
// value: AppPopupMenuEntries.documentsSelectGridView,
|
||||||
padding: EdgeInsets.zero,
|
// padding: EdgeInsets.zero,
|
||||||
child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
// child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||||
builder: (context, state) {
|
// builder: (context, state) {
|
||||||
return ListTile(
|
// return ListTile(
|
||||||
leading: const Icon(Icons.grid_view_rounded),
|
// leading: const Icon(Icons.grid_view_rounded),
|
||||||
title: const Text("Grid"),
|
// title: const Text("Grid"),
|
||||||
trailing: state.preferredViewType == ViewType.grid
|
// trailing: state.preferredViewType == ViewType.grid
|
||||||
? const Icon(Icons.check)
|
// ? const Icon(Icons.check)
|
||||||
: null,
|
// : null,
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
void _showAboutDialog(BuildContext context) {
|
// void _showAboutDialog(BuildContext context) {
|
||||||
showAboutDialog(
|
// showAboutDialog(
|
||||||
context: context,
|
// context: context,
|
||||||
applicationIcon: const ImageIcon(
|
// applicationIcon: const ImageIcon(
|
||||||
AssetImage('assets/logos/paperless_logo_green.png'),
|
// AssetImage('assets/logos/paperless_logo_green.png'),
|
||||||
),
|
// ),
|
||||||
applicationName: 'Paperless Mobile',
|
// applicationName: 'Paperless Mobile',
|
||||||
applicationVersion: packageInfo.version + '+' + packageInfo.buildNumber,
|
// applicationVersion: packageInfo.version + '+' + packageInfo.buildNumber,
|
||||||
children: [
|
// children: [
|
||||||
Text(S.of(context).aboutDialogDevelopedByText('Anton Stubenbord')),
|
// Text(S.of(context).aboutDialogDevelopedByText('Anton Stubenbord')),
|
||||||
Link(
|
// Link(
|
||||||
uri: Uri.parse('https://github.com/astubenbord/paperless-mobile'),
|
// uri: Uri.parse('https://github.com/astubenbord/paperless-mobile'),
|
||||||
builder: (context, followLink) => GestureDetector(
|
// builder: (context, followLink) => GestureDetector(
|
||||||
onTap: followLink,
|
// onTap: followLink,
|
||||||
child: Text(
|
// child: Text(
|
||||||
'https://github.com/astubenbord/paperless-mobile',
|
// 'https://github.com/astubenbord/paperless-mobile',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
|
// style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
const SizedBox(height: 16),
|
// const SizedBox(height: 16),
|
||||||
Text(
|
// Text(
|
||||||
'Credits',
|
// 'Credits',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
// style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
// ),
|
||||||
_buildOnboardingImageCredits(),
|
// _buildOnboardingImageCredits(),
|
||||||
],
|
// ],
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
Widget _buildOnboardingImageCredits() {
|
// Widget _buildOnboardingImageCredits() {
|
||||||
return Link(
|
// return Link(
|
||||||
uri: Uri.parse(
|
// uri: Uri.parse(
|
||||||
'https://www.freepik.com/free-vector/business-team-working-cogwheel-mechanism-together_8270974.htm#query=setting&position=4&from_view=author'),
|
// 'https://www.freepik.com/free-vector/business-team-working-cogwheel-mechanism-together_8270974.htm#query=setting&position=4&from_view=author'),
|
||||||
builder: (context, followLink) => Wrap(
|
// builder: (context, followLink) => Wrap(
|
||||||
children: [
|
// children: [
|
||||||
const Text('Onboarding images by '),
|
// const Text('Onboarding images by '),
|
||||||
GestureDetector(
|
// GestureDetector(
|
||||||
onTap: followLink,
|
// onTap: followLink,
|
||||||
child: Text(
|
// child: Text(
|
||||||
'pch.vector',
|
// 'pch.vector',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
|
// style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
const Text(' on Freepik.')
|
// const Text(' on Freepik.')
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
List<PopupMenuEntry<AppPopupMenuEntries>> _buildEntries(
|
// List<PopupMenuEntry<AppPopupMenuEntries>> _buildEntries(
|
||||||
BuildContext context) {
|
// BuildContext context) {
|
||||||
List<PopupMenuEntry<AppPopupMenuEntries>> items = [];
|
// List<PopupMenuEntry<AppPopupMenuEntries>> items = [];
|
||||||
for (final entry in displayedActions) {
|
// for (final entry in displayedActions) {
|
||||||
switch (entry) {
|
// switch (entry) {
|
||||||
case AppPopupMenuEntries.documentsSelectListView:
|
// case AppPopupMenuEntries.documentsSelectListView:
|
||||||
items.add(_buildListViewTile());
|
// items.add(_buildListViewTile());
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.documentsSelectGridView:
|
// case AppPopupMenuEntries.documentsSelectGridView:
|
||||||
items.add(_buildGridViewTile());
|
// items.add(_buildGridViewTile());
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.openAboutThisAppDialog:
|
// case AppPopupMenuEntries.openAboutThisAppDialog:
|
||||||
items.add(_buildAboutTile(context));
|
// items.add(_buildAboutTile(context));
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.reportBug:
|
// case AppPopupMenuEntries.reportBug:
|
||||||
items.add(_buildReportBugTile(context));
|
// items.add(_buildReportBugTile(context));
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.openSettings:
|
// case AppPopupMenuEntries.openSettings:
|
||||||
items.add(_buildSettingsTile(context));
|
// items.add(_buildSettingsTile(context));
|
||||||
break;
|
// break;
|
||||||
case AppPopupMenuEntries.divider:
|
// case AppPopupMenuEntries.divider:
|
||||||
items.add(const PopupMenuDivider());
|
// items.add(const PopupMenuDivider());
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return items;
|
// return items;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class SearchBar extends StatelessWidget {
|
|||||||
surfaceTintColor: colorScheme.surfaceTint,
|
surfaceTintColor: colorScheme.surfaceTint,
|
||||||
borderRadius: BorderRadius.circular(effectiveHeight / 2),
|
borderRadius: BorderRadius.circular(effectiveHeight / 2),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {},
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(effectiveHeight / 2),
|
borderRadius: BorderRadius.circular(effectiveHeight / 2),
|
||||||
highlightColor: Colors.transparent,
|
highlightColor: Colors.transparent,
|
||||||
splashFactory: InkRipple.splashFactory,
|
splashFactory: InkRipple.splashFactory,
|
||||||
@@ -51,7 +51,9 @@ class SearchBar extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 8),
|
padding: const EdgeInsets.only(right: 8),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
|
onTap: onTap,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
|
enabled: false,
|
||||||
cursorColor: colorScheme.primary,
|
cursorColor: colorScheme.primary,
|
||||||
style: textTheme.bodyLarge,
|
style: textTheme.bodyLarge,
|
||||||
textAlignVertical: TextAlignVertical.center,
|
textAlignVertical: TextAlignVertical.center,
|
||||||
@@ -64,7 +66,6 @@ class SearchBar extends StatelessWidget {
|
|||||||
color: colorScheme.onSurfaceVariant,
|
color: colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: onTap,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,18 +2,25 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
class PaperlessLogo extends StatelessWidget {
|
class PaperlessLogo extends StatelessWidget {
|
||||||
|
static const _paperlessGreen = Color(0xFF18541F);
|
||||||
final double? height;
|
final double? height;
|
||||||
final double? width;
|
final double? width;
|
||||||
final String _path;
|
final Color _color;
|
||||||
|
|
||||||
const PaperlessLogo.white({super.key, this.height, this.width})
|
const PaperlessLogo.white({
|
||||||
: _path = "assets/logos/paperless_logo_white.svg";
|
super.key,
|
||||||
|
this.height,
|
||||||
|
this.width,
|
||||||
|
}) : _color = Colors.white;
|
||||||
|
|
||||||
const PaperlessLogo.green({super.key, this.height, this.width})
|
const PaperlessLogo.green({super.key, this.height, this.width})
|
||||||
: _path = "assets/logos/paperless_logo_green.svg";
|
: _color = _paperlessGreen;
|
||||||
|
|
||||||
const PaperlessLogo.black({super.key, this.height, this.width})
|
const PaperlessLogo.black({super.key, this.height, this.width})
|
||||||
: _path = "assets/logos/paperless_logo_black.svg";
|
: _color = Colors.black;
|
||||||
|
|
||||||
|
const PaperlessLogo.colored(Color color, {super.key, this.height, this.width})
|
||||||
|
: _color = color;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -24,7 +31,8 @@ class PaperlessLogo extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
padding: const EdgeInsets.only(right: 8),
|
padding: const EdgeInsets.only(right: 8),
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
_path,
|
"assets/logos/paperless_logo_white.svg",
|
||||||
|
color: _color,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:paperless_mobile/constants.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
|
||||||
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/settings/view/settings_page.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:url_launcher/link.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
class AppDrawer extends StatelessWidget {
|
||||||
|
const AppDrawer({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SafeArea(
|
||||||
|
top: true,
|
||||||
|
child: Drawer(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const PaperlessLogo.green(),
|
||||||
|
Text(
|
||||||
|
"Paperless Mobile",
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padded(),
|
||||||
|
const Divider(),
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
title: Text(S.of(context).appDrawerAboutLabel),
|
||||||
|
leading: const Icon(Icons.info_outline),
|
||||||
|
onTap: () => _showAboutDialog(context),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: const Icon(Icons.bug_report_outlined),
|
||||||
|
title: Text(S.of(context).appDrawerReportBugLabel),
|
||||||
|
onTap: () {
|
||||||
|
launchUrlString(
|
||||||
|
'https://github.com/astubenbord/paperless-mobile/issues/new');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: const Icon(Icons.settings_outlined),
|
||||||
|
title: Text(
|
||||||
|
S.of(context).appDrawerSettingsLabel,
|
||||||
|
),
|
||||||
|
onTap: () => Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => BlocProvider.value(
|
||||||
|
value: context.read<ApplicationSettingsCubit>(),
|
||||||
|
child: const SettingsPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showAboutDialog(BuildContext context) {
|
||||||
|
showAboutDialog(
|
||||||
|
context: context,
|
||||||
|
applicationIcon: const ImageIcon(
|
||||||
|
AssetImage('assets/logos/paperless_logo_green.png'),
|
||||||
|
),
|
||||||
|
applicationName: 'Paperless Mobile',
|
||||||
|
applicationVersion: packageInfo.version + '+' + packageInfo.buildNumber,
|
||||||
|
children: [
|
||||||
|
Text(S.of(context).aboutDialogDevelopedByText('Anton Stubenbord')),
|
||||||
|
Link(
|
||||||
|
uri: Uri.parse('https://github.com/astubenbord/paperless-mobile'),
|
||||||
|
builder: (context, followLink) => GestureDetector(
|
||||||
|
onTap: followLink,
|
||||||
|
child: Text(
|
||||||
|
'https://github.com/astubenbord/paperless-mobile',
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Credits',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
_buildOnboardingImageCredits(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOnboardingImageCredits() {
|
||||||
|
return Link(
|
||||||
|
uri: Uri.parse(
|
||||||
|
'https://www.freepik.com/free-vector/business-team-working-cogwheel-mechanism-together_8270974.htm#query=setting&position=4&from_view=author'),
|
||||||
|
builder: (context, followLink) => Wrap(
|
||||||
|
children: [
|
||||||
|
const Text('Onboarding images by '),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: followLink,
|
||||||
|
child: Text(
|
||||||
|
'pch.vector',
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text(' on Freepik.')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:open_filex/open_filex.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
|
||||||
import 'package:open_filex/open_filex.dart';
|
|
||||||
|
|
||||||
part 'document_details_state.dart';
|
part 'document_details_state.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -482,7 +482,6 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
child: _DetailsItem(
|
child: _DetailsItem(
|
||||||
label: S.of(context).documentStoragePathPropertyLabel,
|
label: S.of(context).documentStoragePathPropertyLabel,
|
||||||
content: StoragePathWidget(
|
content: StoragePathWidget(
|
||||||
isClickable: widget.isLabelClickable,
|
|
||||||
pathId: document.storagePath,
|
pathId: document.storagePath,
|
||||||
),
|
),
|
||||||
).paddedSymmetrically(vertical: 16),
|
).paddedSymmetrically(vertical: 16),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_search/cubit/document_search_state.dart';
|
||||||
import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart';
|
import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart';
|
||||||
import 'package:paperless_mobile/features/search/cubit/document_search_state.dart';
|
|
||||||
|
|
||||||
class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
|
class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
|
||||||
with PagedDocumentsMixin {
|
with PagedDocumentsMixin {
|
||||||
@@ -27,11 +27,8 @@ class DocumentSearchState extends PagedDocumentsState {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [
|
List<Object?> get props => [
|
||||||
hasLoaded,
|
...super.props,
|
||||||
isLoading,
|
|
||||||
filter,
|
|
||||||
value,
|
|
||||||
searchHistory,
|
searchHistory,
|
||||||
suggestions,
|
suggestions,
|
||||||
view,
|
view,
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.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_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_search/cubit/document_search_state.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||||
import 'package:paperless_mobile/features/search/cubit/document_search_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/search/cubit/document_search_state.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
|
|
||||||
Future<void> showDocumentSearchPage(BuildContext context) {
|
Future<void> showDocumentSearchPage(BuildContext context) {
|
||||||
return Navigator.of(context).push(
|
return Navigator.of(context).push(
|
||||||
@@ -151,12 +153,27 @@ class _DocumentSearchPageState extends State<DocumentSearchPage> {
|
|||||||
isLabelClickable: false,
|
isLabelClickable: false,
|
||||||
isLoading: state.isLoading,
|
isLoading: state.isLoading,
|
||||||
hasLoaded: state.hasLoaded,
|
hasLoaded: state.hasLoaded,
|
||||||
|
onTap: (document) async {
|
||||||
|
final updatedDocument = await Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
DocumentDetailsRoute.routeName,
|
||||||
|
arguments: DocumentDetailsRouteArguments(
|
||||||
|
document: document,
|
||||||
|
isLabelClickable: false,
|
||||||
|
),
|
||||||
|
) as DocumentModel?;
|
||||||
|
if (updatedDocument != document) {
|
||||||
|
context.read<DocumentSearchCubit>().reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _selectSuggestion(String suggestion) {
|
void _selectSuggestion(String suggestion) {
|
||||||
|
_queryController.text = suggestion;
|
||||||
context.read<DocumentSearchCubit>().search(suggestion);
|
context.read<DocumentSearchCubit>().search(suggestion);
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,10 +12,7 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
|||||||
@override
|
@override
|
||||||
final PaperlessDocumentsApi api;
|
final PaperlessDocumentsApi api;
|
||||||
|
|
||||||
final SavedViewRepository _savedViewRepository;
|
DocumentsCubit(this.api) : super(const DocumentsState());
|
||||||
|
|
||||||
DocumentsCubit(this.api, this._savedViewRepository)
|
|
||||||
: super(const DocumentsState());
|
|
||||||
|
|
||||||
Future<void> bulkRemove(List<DocumentModel> documents) async {
|
Future<void> bulkRemove(List<DocumentModel> documents) async {
|
||||||
log("[DocumentsCubit] bulkRemove");
|
log("[DocumentsCubit] bulkRemove");
|
||||||
|
|||||||
@@ -1,33 +1,28 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:badges/badges.dart' as b;
|
import 'package:badges/badges.dart' as b;
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.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_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||||
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
import 'package:paperless_mobile/features/document_search/view/document_search_page.dart';
|
||||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/new_items_loading_widget.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_panel.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_panel.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/view_actions.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart';
|
import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart';
|
||||||
import 'package:paperless_mobile/features/search/view/document_search_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
|
|
||||||
class DocumentFilterIntent {
|
class DocumentFilterIntent {
|
||||||
final DocumentFilter? filter;
|
final DocumentFilter? filter;
|
||||||
@@ -116,6 +111,7 @@ class _DocumentsPageState extends State<DocumentsPage>
|
|||||||
},
|
},
|
||||||
builder: (context, connectivityState) {
|
builder: (context, connectivityState) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
drawer: const AppDrawer(),
|
||||||
floatingActionButton: BlocBuilder<DocumentsCubit, DocumentsState>(
|
floatingActionButton: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final appliedFiltersCount = state.filter.appliedFiltersCount;
|
final appliedFiltersCount = state.filter.appliedFiltersCount;
|
||||||
@@ -165,6 +161,7 @@ class _DocumentsPageState extends State<DocumentsPage>
|
|||||||
context,
|
context,
|
||||||
),
|
),
|
||||||
sliver: SearchAppBar(
|
sliver: SearchAppBar(
|
||||||
|
hintText: "Search documents", //TODO: INTL
|
||||||
onOpenSearch: showDocumentSearchPage,
|
onOpenSearch: showDocumentSearchPage,
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
@@ -177,9 +174,12 @@ class _DocumentsPageState extends State<DocumentsPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
body: NotificationListener<ScrollUpdateNotification>(
|
body: NotificationListener<ScrollNotification>(
|
||||||
onNotification: (notification) {
|
onNotification: (notification) {
|
||||||
final metrics = notification.metrics;
|
final metrics = notification.metrics;
|
||||||
|
if (metrics.maxScrollExtent == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final desiredTab =
|
final desiredTab =
|
||||||
(metrics.pixels / metrics.maxScrollExtent).round();
|
(metrics.pixels / metrics.maxScrollExtent).round();
|
||||||
if (metrics.axis == Axis.horizontal &&
|
if (metrics.axis == Axis.horizontal &&
|
||||||
@@ -414,29 +414,21 @@ class _DocumentsPageState extends State<DocumentsPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openDetails(DocumentModel document) async {
|
Future<void> _openDetails(DocumentModel document) async {
|
||||||
final updatedModel = await Navigator.of(context).push<DocumentModel?>(
|
final updatedModel = await Navigator.pushNamed(
|
||||||
_buildDetailsPageRoute(document),
|
context,
|
||||||
);
|
DocumentDetailsRoute.routeName,
|
||||||
|
arguments: DocumentDetailsRouteArguments(
|
||||||
|
document: document,
|
||||||
|
),
|
||||||
|
) as DocumentModel?;
|
||||||
|
// final updatedModel = await Navigator.of(context).push<DocumentModel?>(
|
||||||
|
// _buildDetailsPageRoute(document),
|
||||||
|
// );
|
||||||
if (updatedModel != document) {
|
if (updatedModel != document) {
|
||||||
context.read<DocumentsCubit>().reload();
|
context.read<DocumentsCubit>().reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialPageRoute<DocumentModel?> _buildDetailsPageRoute(
|
|
||||||
DocumentModel document) {
|
|
||||||
return MaterialPageRoute(
|
|
||||||
builder: (_) => BlocProvider(
|
|
||||||
create: (context) => DocumentDetailsCubit(
|
|
||||||
context.read<PaperlessDocumentsApi>(),
|
|
||||||
document,
|
|
||||||
),
|
|
||||||
child: const LabelRepositoriesProvider(
|
|
||||||
child: DocumentDetailsPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addTagToFilter(int tagId) {
|
void _addTagToFilter(int tagId) {
|
||||||
try {
|
try {
|
||||||
final tagsQuery =
|
final tagsQuery =
|
||||||
|
|||||||
@@ -48,11 +48,18 @@ class HomePage extends StatefulWidget {
|
|||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
int _currentIndex = 0;
|
int _currentIndex = 0;
|
||||||
final DocumentScannerCubit _scannerCubit = DocumentScannerCubit();
|
final DocumentScannerCubit _scannerCubit = DocumentScannerCubit();
|
||||||
|
late final InboxCubit _inboxCubit;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_initializeData(context);
|
_initializeData(context);
|
||||||
|
_inboxCubit = InboxCubit(
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
);
|
||||||
context.read<ConnectivityCubit>().reload();
|
context.read<ConnectivityCubit>().reload();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
_listenForReceivedFiles();
|
_listenForReceivedFiles();
|
||||||
@@ -147,6 +154,12 @@ class _HomePageState extends State<HomePage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_inboxCubit.close();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final destinations = [
|
final destinations = [
|
||||||
@@ -182,28 +195,15 @@ class _HomePageState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
label: S.of(context).bottomNavInboxPageLabel,
|
label: S.of(context).bottomNavInboxPageLabel,
|
||||||
),
|
),
|
||||||
// RouteDescription(
|
|
||||||
// icon: const Icon(Icons.settings_outlined),
|
|
||||||
// selectedIcon: Icon(
|
|
||||||
// Icons.settings,
|
|
||||||
// color: Theme.of(context).colorScheme.primary,
|
|
||||||
// ),
|
|
||||||
// label: S.of(context).appDrawerSettingsLabel,
|
|
||||||
// ),
|
|
||||||
];
|
];
|
||||||
final routes = <Widget>[
|
final routes = <Widget>[
|
||||||
MultiBlocProvider(
|
MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => DocumentsCubit(
|
create: (context) => DocumentsCubit(context.read()),
|
||||||
context.read<PaperlessDocumentsApi>(),
|
|
||||||
context.read<SavedViewRepository>(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => SavedViewCubit(
|
create: (context) => SavedViewCubit(context.read()),
|
||||||
context.read<SavedViewRepository>(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const DocumentsPage(),
|
child: const DocumentsPage(),
|
||||||
@@ -213,13 +213,8 @@ class _HomePageState extends State<HomePage> {
|
|||||||
child: const ScannerPage(),
|
child: const ScannerPage(),
|
||||||
),
|
),
|
||||||
const LabelsPage(),
|
const LabelsPage(),
|
||||||
BlocProvider(
|
BlocProvider.value(
|
||||||
create: (context) => InboxCubit(
|
value: _inboxCubit,
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
),
|
|
||||||
child: const InboxPage(),
|
child: const InboxPage(),
|
||||||
),
|
),
|
||||||
// const SettingsPage(),
|
// const SettingsPage(),
|
||||||
@@ -249,7 +244,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
builder: (context, sizingInformation) {
|
builder: (context, sizingInformation) {
|
||||||
if (!sizingInformation.isMobile) {
|
if (!sizingInformation.isMobile) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
// drawer: const AppDrawer(),
|
|
||||||
body: Row(
|
body: Row(
|
||||||
children: [
|
children: [
|
||||||
NavigationRail(
|
NavigationRail(
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_search/view/document_search_page.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_list_loading_widget.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/documents_list_loading_widget.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||||
@@ -11,6 +13,7 @@ import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
|||||||
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
|
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
|
||||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_empty_widget.dart';
|
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_empty_widget.dart';
|
||||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
|
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
|
||||||
|
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
|
|
||||||
@@ -54,39 +57,8 @@ class _InboxPageState extends State<InboxPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
const _progressBarHeight = 4.0;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: PreferredSize(
|
drawer: const AppDrawer(),
|
||||||
preferredSize:
|
|
||||||
const Size.fromHeight(kToolbarHeight + _progressBarHeight),
|
|
||||||
child: BlocBuilder<InboxCubit, InboxState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return AppBar(
|
|
||||||
title: Text(S.of(context).bottomNavInboxPageLabel),
|
|
||||||
actions: [
|
|
||||||
if (state.hasLoaded)
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
|
||||||
child: ColoredBox(
|
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
|
||||||
child: Text(
|
|
||||||
(state.value.isEmpty
|
|
||||||
? '0 '
|
|
||||||
: '${state.value.first.count} ') +
|
|
||||||
S.of(context).inboxPageUnseenText,
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
).paddedSymmetrically(horizontal: 4.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).paddedSymmetrically(horizontal: 8)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: BlocBuilder<InboxCubit, InboxState>(
|
floatingActionButton: BlocBuilder<InboxCubit, InboxState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (!state.hasLoaded || state.documents.isEmpty) {
|
if (!state.hasLoaded || state.documents.isEmpty) {
|
||||||
@@ -104,91 +76,95 @@ class _InboxPageState extends State<InboxPage> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
body: BlocBuilder<InboxCubit, InboxState>(
|
body: NestedScrollView(
|
||||||
builder: (context, state) {
|
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||||
if (!state.hasLoaded) {
|
SearchAppBar(
|
||||||
return const DocumentsListLoadingWidget();
|
hintText: "Search documents",
|
||||||
}
|
onOpenSearch: showDocumentSearchPage,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
body: BlocBuilder<InboxCubit, InboxState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (!state.hasLoaded) {
|
||||||
|
return const CustomScrollView(
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
slivers: [DocumentsListLoadingWidget()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (state.documents.isEmpty) {
|
if (state.documents.isEmpty) {
|
||||||
return InboxEmptyWidget(
|
return InboxEmptyWidget(
|
||||||
emptyStateRefreshIndicatorKey: _emptyStateRefreshIndicatorKey,
|
emptyStateRefreshIndicatorKey: _emptyStateRefreshIndicatorKey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a list of slivers alternating between SliverToBoxAdapter
|
// Build a list of slivers alternating between SliverToBoxAdapter
|
||||||
// (group header) and a SliverList (inbox items).
|
// (group header) and a SliverList (inbox items).
|
||||||
final List<Widget> slivers = _groupByDate(state.documents)
|
final List<Widget> slivers = _groupByDate(state.documents)
|
||||||
.entries
|
.entries
|
||||||
.map(
|
.map(
|
||||||
(entry) => [
|
(entry) => [
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(32.0),
|
borderRadius: BorderRadius.circular(32.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
entry.key,
|
entry.key,
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
).padded(),
|
).padded(),
|
||||||
),
|
),
|
||||||
).paddedOnly(top: 8.0),
|
).paddedOnly(top: 8.0),
|
||||||
),
|
),
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount: entry.value.length,
|
childCount: entry.value.length,
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index < entry.value.length - 1) {
|
if (index < entry.value.length - 1) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
_buildListItem(
|
_buildListItem(
|
||||||
entry.value[index],
|
entry.value[index],
|
||||||
),
|
),
|
||||||
const Divider(
|
const Divider(
|
||||||
indent: 16,
|
indent: 16,
|
||||||
endIndent: 16,
|
endIndent: 16,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _buildListItem(
|
||||||
|
entry.value[index],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
return _buildListItem(
|
),
|
||||||
entry.value[index],
|
),
|
||||||
);
|
],
|
||||||
},
|
)
|
||||||
|
.flattened
|
||||||
|
.toList()
|
||||||
|
..add(const SliverToBoxAdapter(child: SizedBox(height: 78)));
|
||||||
|
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => context.read<InboxCubit>().initializeInbox(),
|
||||||
|
child: CustomScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: HintCard(
|
||||||
|
show: !state.isHintAcknowledged,
|
||||||
|
hintText: S.of(context).inboxPageUsageHintText,
|
||||||
|
onHintAcknowledged: () =>
|
||||||
|
context.read<InboxCubit>().acknowledgeHint(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
...slivers,
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
.flattened
|
);
|
||||||
.toList()
|
},
|
||||||
..add(const SliverToBoxAdapter(child: SizedBox(height: 78)));
|
),
|
||||||
|
|
||||||
return RefreshIndicator(
|
|
||||||
onRefresh: () => context.read<InboxCubit>().initializeInbox(),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: CustomScrollView(
|
|
||||||
controller: _scrollController,
|
|
||||||
slivers: [
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: HintCard(
|
|
||||||
show: !state.isHintAcknowledged,
|
|
||||||
hintText: S.of(context).inboxPageUsageHintText,
|
|
||||||
onHintAcknowledged: () =>
|
|
||||||
context.read<InboxCubit>().acknowledgeHint(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
...slivers,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
|||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
|
|
||||||
class InboxItem extends StatefulWidget {
|
class InboxItem extends StatefulWidget {
|
||||||
static const _a4AspectRatio = 1 / 1.4142;
|
static const _a4AspectRatio = 1 / 1.4142;
|
||||||
@@ -40,24 +41,16 @@ class _InboxItemState extends State<InboxItem> {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final returnedDocument = await Navigator.push<DocumentModel?>(
|
final updatedDocument = await Navigator.pushNamed(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
DocumentDetailsRoute.routeName,
|
||||||
builder: (context) => BlocProvider(
|
arguments: DocumentDetailsRouteArguments(
|
||||||
create: (context) => DocumentDetailsCubit(
|
document: widget.document,
|
||||||
context.read<PaperlessDocumentsApi>(),
|
isLabelClickable: false,
|
||||||
widget.document,
|
|
||||||
),
|
|
||||||
child: const LabelRepositoriesProvider(
|
|
||||||
child: DocumentDetailsPage(
|
|
||||||
isLabelClickable: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
) as DocumentModel?;
|
||||||
if (returnedDocument != null) {
|
if (updatedDocument != null) {
|
||||||
widget.onDocumentUpdated(returnedDocument);
|
widget.onDocumentUpdated(updatedDocument);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
|
|||||||
@@ -266,6 +266,10 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
Widget _buildNotAssignedTag(FormFieldState<TagsQuery> field) {
|
Widget _buildNotAssignedTag(FormFieldState<TagsQuery> field) {
|
||||||
return ColoredChipWrapper(
|
return ColoredChipWrapper(
|
||||||
child: InputChip(
|
child: InputChip(
|
||||||
|
labelPadding: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
side: BorderSide.none,
|
||||||
label: Text(
|
label: Text(
|
||||||
S.of(context).labelNotAssignedText,
|
S.of(context).labelNotAssignedText,
|
||||||
),
|
),
|
||||||
@@ -288,6 +292,10 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
}
|
}
|
||||||
return ColoredChipWrapper(
|
return ColoredChipWrapper(
|
||||||
child: InputChip(
|
child: InputChip(
|
||||||
|
labelPadding: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
side: BorderSide.none,
|
||||||
label: Text(
|
label: Text(
|
||||||
tag.name,
|
tag.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -312,6 +320,10 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
Widget _buildAnyAssignedTag(FormFieldState<TagsQuery> field) {
|
Widget _buildAnyAssignedTag(FormFieldState<TagsQuery> field) {
|
||||||
return ColoredChipWrapper(
|
return ColoredChipWrapper(
|
||||||
child: InputChip(
|
child: InputChip(
|
||||||
|
labelPadding: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
side: BorderSide.none,
|
||||||
label: Text(S.of(context).labelAnyAssignedText),
|
label: Text(S.of(context).labelAnyAssignedText),
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.12),
|
Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.12),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
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_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
@@ -8,6 +10,9 @@ import 'package:paperless_mobile/core/repository/state/impl/document_type_reposi
|
|||||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
||||||
|
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_search/view/document_search_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
||||||
@@ -16,12 +21,13 @@ import 'package:paperless_mobile/features/edit_label/view/impl/edit_corresponden
|
|||||||
import 'package:paperless_mobile/features/edit_label/view/impl/edit_document_type_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/edit_document_type_page.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/edit_storage_path_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/edit_storage_path_page.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/edit_tag_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/edit_tag_page.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/app_drawer.dart';
|
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/providers/correspondent_bloc_provider.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/correspondent_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/providers/document_type_bloc_provider.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/document_type_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/providers/storage_path_bloc_provider.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/storage_path_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/providers/tag_bloc_provider.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/tag_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
||||||
|
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class LabelsPage extends StatefulWidget {
|
class LabelsPage extends StatefulWidget {
|
||||||
@@ -51,154 +57,264 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
child: BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
child: BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
builder: (context, connectedState) {
|
builder: (context, connectedState) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
drawer: const AppDrawer(),
|
||||||
title: Text(
|
floatingActionButton: FloatingActionButton(
|
||||||
[
|
onPressed: [
|
||||||
S.of(context).labelsPageCorrespondentsTitleText,
|
_openAddCorrespondentPage,
|
||||||
S.of(context).labelsPageDocumentTypesTitleText,
|
_openAddDocumentTypePage,
|
||||||
S.of(context).labelsPageTagsTitleText,
|
_openAddTagPage,
|
||||||
S.of(context).labelsPageStoragePathTitleText
|
_openAddStoragePathPage,
|
||||||
][_currentIndex],
|
][_currentIndex],
|
||||||
),
|
child: Icon(Icons.add),
|
||||||
actions: [
|
),
|
||||||
IconButton(
|
body: NestedScrollView(
|
||||||
onPressed: [
|
floatHeaderSlivers: true,
|
||||||
_openAddCorrespondentPage,
|
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||||
_openAddDocumentTypePage,
|
SliverOverlapAbsorber(
|
||||||
_openAddTagPage,
|
// This widget takes the overlapping behavior of the SliverAppBar,
|
||||||
_openAddStoragePathPage,
|
// and redirects it to the SliverOverlapInjector below. If it is
|
||||||
][_currentIndex],
|
// missing, then it is possible for the nested "inner" scroll view
|
||||||
icon: const Icon(Icons.add),
|
// below to end up under the SliverAppBar even when the inner
|
||||||
)
|
// scroll view thinks it has not been scrolled.
|
||||||
|
// This is not necessary if the "headerSliverBuilder" only builds
|
||||||
|
// widgets that do not overlap the next sliver.
|
||||||
|
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
sliver: SearchAppBar(
|
||||||
|
hintText: "Search documents", //TODO: INTL
|
||||||
|
onOpenSearch: showDocumentSearchPage,
|
||||||
|
bottom: TabBar(
|
||||||
|
controller: _tabController,
|
||||||
|
tabs: [
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.person_outline,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.description_outlined,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.label_outline,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.folder_open,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
bottom: PreferredSize(
|
body: NotificationListener<ScrollNotification>(
|
||||||
preferredSize: Size.fromHeight(
|
onNotification: (notification) {
|
||||||
kToolbarHeight + (!connectedState.isConnected ? 16 : 0)),
|
final metrics = notification.metrics;
|
||||||
child: Column(
|
if (metrics.maxScrollExtent == 0) {
|
||||||
children: [
|
return true;
|
||||||
if (!connectedState.isConnected) const OfflineBanner(),
|
}
|
||||||
ColoredBox(
|
final desiredTab =
|
||||||
color: Theme.of(context).bottomAppBarColor,
|
((metrics.pixels / metrics.maxScrollExtent) *
|
||||||
child: TabBar(
|
(_tabController.length - 1))
|
||||||
indicatorColor: Theme.of(context).colorScheme.primary,
|
.round();
|
||||||
controller: _tabController,
|
|
||||||
tabs: [
|
if (metrics.axis == Axis.horizontal &&
|
||||||
Tab(
|
_currentIndex != desiredTab) {
|
||||||
icon: Icon(
|
setState(() => _currentIndex = desiredTab);
|
||||||
Icons.person_outline,
|
}
|
||||||
color: Theme.of(context)
|
return true;
|
||||||
.colorScheme
|
},
|
||||||
.onPrimaryContainer,
|
child: MultiBlocProvider(
|
||||||
),
|
providers: [
|
||||||
),
|
BlocProvider(
|
||||||
Tab(
|
create: (context) => LabelCubit<Correspondent>(
|
||||||
icon: Icon(
|
context.read<
|
||||||
Icons.description_outlined,
|
LabelRepository<Correspondent,
|
||||||
color: Theme.of(context)
|
CorrespondentRepositoryState>>(),
|
||||||
.colorScheme
|
),
|
||||||
.onPrimaryContainer,
|
),
|
||||||
),
|
BlocProvider(
|
||||||
),
|
create: (context) => LabelCubit<DocumentType>(
|
||||||
Tab(
|
context.read<
|
||||||
icon: Icon(
|
LabelRepository<DocumentType,
|
||||||
Icons.label_outline,
|
DocumentTypeRepositoryState>>(),
|
||||||
color: Theme.of(context)
|
),
|
||||||
.colorScheme
|
),
|
||||||
.onPrimaryContainer,
|
BlocProvider(
|
||||||
),
|
create: (context) => LabelCubit<Tag>(
|
||||||
),
|
context
|
||||||
Tab(
|
.read<LabelRepository<Tag, TagRepositoryState>>(),
|
||||||
icon: Icon(
|
),
|
||||||
Icons.folder_open,
|
),
|
||||||
color: Theme.of(context)
|
BlocProvider(
|
||||||
.colorScheme
|
create: (context) => LabelCubit<StoragePath>(
|
||||||
.onPrimaryContainer,
|
context.read<
|
||||||
),
|
LabelRepository<StoragePath,
|
||||||
)
|
StoragePathRepositoryState>>(),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
child: RefreshIndicator(
|
||||||
|
edgeOffset: kToolbarHeight,
|
||||||
|
notificationPredicate: (notification) =>
|
||||||
|
connectedState.isConnected,
|
||||||
|
onRefresh: () => [
|
||||||
|
context.read<LabelCubit<Correspondent>>(),
|
||||||
|
context.read<LabelCubit<DocumentType>>(),
|
||||||
|
context.read<LabelCubit<Tag>>(),
|
||||||
|
context.read<LabelCubit<StoragePath>>(),
|
||||||
|
][_currentIndex]
|
||||||
|
.reload(),
|
||||||
|
child: TabBarView(
|
||||||
|
controller: _tabController,
|
||||||
|
children: [
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverOverlapInjector(
|
||||||
|
handle: NestedScrollView
|
||||||
|
.sliverOverlapAbsorberHandleFor(context),
|
||||||
|
),
|
||||||
|
LabelTabView<Correspondent>(
|
||||||
|
filterBuilder: (label) => DocumentFilter(
|
||||||
|
correspondent:
|
||||||
|
IdQueryParameter.fromId(label.id),
|
||||||
|
pageSize: label.documentCount ?? 0,
|
||||||
|
),
|
||||||
|
onEdit: _openEditCorrespondentPage,
|
||||||
|
emptyStateActionButtonLabel: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageCorrespondentEmptyStateAddNewLabel,
|
||||||
|
emptyStateDescription: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageCorrespondentEmptyStateDescriptionText,
|
||||||
|
onAddNew: _openAddCorrespondentPage,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverOverlapInjector(
|
||||||
|
handle: NestedScrollView
|
||||||
|
.sliverOverlapAbsorberHandleFor(context),
|
||||||
|
),
|
||||||
|
DocumentTypeBlocProvider(
|
||||||
|
child: LabelTabView<DocumentType>(
|
||||||
|
filterBuilder: (label) => DocumentFilter(
|
||||||
|
documentType:
|
||||||
|
IdQueryParameter.fromId(label.id),
|
||||||
|
pageSize: label.documentCount ?? 0,
|
||||||
|
),
|
||||||
|
onEdit: _openEditDocumentTypePage,
|
||||||
|
emptyStateActionButtonLabel: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageDocumentTypeEmptyStateAddNewLabel,
|
||||||
|
emptyStateDescription: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageDocumentTypeEmptyStateDescriptionText,
|
||||||
|
onAddNew: _openAddDocumentTypePage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverOverlapInjector(
|
||||||
|
handle: NestedScrollView
|
||||||
|
.sliverOverlapAbsorberHandleFor(context),
|
||||||
|
),
|
||||||
|
TagBlocProvider(
|
||||||
|
child: LabelTabView<Tag>(
|
||||||
|
filterBuilder: (label) => DocumentFilter(
|
||||||
|
tags: IdsTagsQuery.fromIds([label.id!]),
|
||||||
|
pageSize: label.documentCount ?? 0,
|
||||||
|
),
|
||||||
|
onEdit: _openEditTagPage,
|
||||||
|
leadingBuilder: (t) => CircleAvatar(
|
||||||
|
backgroundColor: t.color,
|
||||||
|
child: t.isInboxTag ?? false
|
||||||
|
? Icon(
|
||||||
|
Icons.inbox,
|
||||||
|
color: t.textColor,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
emptyStateActionButtonLabel: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageTagsEmptyStateAddNewLabel,
|
||||||
|
emptyStateDescription: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageTagsEmptyStateDescriptionText,
|
||||||
|
onAddNew: _openAddTagPage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverOverlapInjector(
|
||||||
|
handle: NestedScrollView
|
||||||
|
.sliverOverlapAbsorberHandleFor(context),
|
||||||
|
),
|
||||||
|
StoragePathBlocProvider(
|
||||||
|
child: LabelTabView<StoragePath>(
|
||||||
|
onEdit: _openEditStoragePathPage,
|
||||||
|
filterBuilder: (label) => DocumentFilter(
|
||||||
|
storagePath:
|
||||||
|
IdQueryParameter.fromId(label.id),
|
||||||
|
pageSize: label.documentCount ?? 0,
|
||||||
|
),
|
||||||
|
contentBuilder: (path) =>
|
||||||
|
Text(path.path ?? ""),
|
||||||
|
emptyStateActionButtonLabel: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageStoragePathEmptyStateAddNewLabel,
|
||||||
|
emptyStateDescription: S
|
||||||
|
.of(context)
|
||||||
|
.labelsPageStoragePathEmptyStateDescriptionText,
|
||||||
|
onAddNew: _openAddStoragePathPage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: TabBarView(
|
|
||||||
controller: _tabController,
|
|
||||||
children: [
|
|
||||||
CorrespondentBlocProvider(
|
|
||||||
child: LabelTabView<Correspondent>(
|
|
||||||
filterBuilder: (label) => DocumentFilter(
|
|
||||||
correspondent: IdQueryParameter.fromId(label.id),
|
|
||||||
pageSize: label.documentCount ?? 0,
|
|
||||||
),
|
|
||||||
onEdit: _openEditCorrespondentPage,
|
|
||||||
emptyStateActionButtonLabel: S
|
|
||||||
.of(context)
|
|
||||||
.labelsPageCorrespondentEmptyStateAddNewLabel,
|
|
||||||
emptyStateDescription: S
|
|
||||||
.of(context)
|
|
||||||
.labelsPageCorrespondentEmptyStateDescriptionText,
|
|
||||||
onAddNew: _openAddCorrespondentPage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DocumentTypeBlocProvider(
|
|
||||||
child: LabelTabView<DocumentType>(
|
|
||||||
filterBuilder: (label) => DocumentFilter(
|
|
||||||
documentType: IdQueryParameter.fromId(label.id),
|
|
||||||
pageSize: label.documentCount ?? 0,
|
|
||||||
),
|
|
||||||
onEdit: _openEditDocumentTypePage,
|
|
||||||
emptyStateActionButtonLabel: S
|
|
||||||
.of(context)
|
|
||||||
.labelsPageDocumentTypeEmptyStateAddNewLabel,
|
|
||||||
emptyStateDescription: S
|
|
||||||
.of(context)
|
|
||||||
.labelsPageDocumentTypeEmptyStateDescriptionText,
|
|
||||||
onAddNew: _openAddDocumentTypePage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TagBlocProvider(
|
|
||||||
child: LabelTabView<Tag>(
|
|
||||||
filterBuilder: (label) => DocumentFilter(
|
|
||||||
tags: IdsTagsQuery.fromIds([label.id!]),
|
|
||||||
pageSize: label.documentCount ?? 0,
|
|
||||||
),
|
|
||||||
onEdit: _openEditTagPage,
|
|
||||||
leadingBuilder: (t) => CircleAvatar(
|
|
||||||
backgroundColor: t.color,
|
|
||||||
child: t.isInboxTag ?? false
|
|
||||||
? Icon(
|
|
||||||
Icons.inbox,
|
|
||||||
color: t.textColor,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
emptyStateActionButtonLabel:
|
|
||||||
S.of(context).labelsPageTagsEmptyStateAddNewLabel,
|
|
||||||
emptyStateDescription:
|
|
||||||
S.of(context).labelsPageTagsEmptyStateDescriptionText,
|
|
||||||
onAddNew: _openAddTagPage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
StoragePathBlocProvider(
|
|
||||||
child: LabelTabView<StoragePath>(
|
|
||||||
onEdit: _openEditStoragePathPage,
|
|
||||||
filterBuilder: (label) => DocumentFilter(
|
|
||||||
storagePath: IdQueryParameter.fromId(label.id),
|
|
||||||
pageSize: label.documentCount ?? 0,
|
|
||||||
),
|
|
||||||
contentBuilder: (path) => Text(path.path ?? ""),
|
|
||||||
emptyStateActionButtonLabel: S
|
|
||||||
.of(context)
|
|
||||||
.labelsPageStoragePathEmptyStateAddNewLabel,
|
|
||||||
emptyStateDescription: S
|
|
||||||
.of(context)
|
|
||||||
.labelsPageStoragePathEmptyStateDescriptionText,
|
|
||||||
onAddNew: _openAddStoragePathPage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -46,45 +46,46 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
final labels = state.labels.values.toList()..sort();
|
final labels = state.labels.values.toList()..sort();
|
||||||
if (labels.isEmpty) {
|
if (labels.isEmpty) {
|
||||||
return Center(
|
return SliverFillRemaining(
|
||||||
child: Column(
|
child: Center(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
Text(
|
children: [
|
||||||
emptyStateDescription,
|
Text(
|
||||||
textAlign: TextAlign.center,
|
emptyStateDescription,
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
TextButton(
|
),
|
||||||
onPressed: onAddNew,
|
TextButton(
|
||||||
child: Text(emptyStateActionButtonLabel),
|
onPressed: onAddNew,
|
||||||
),
|
child: Text(emptyStateActionButtonLabel),
|
||||||
].padded(),
|
),
|
||||||
|
].padded(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return RefreshIndicator(
|
return SliverList(
|
||||||
onRefresh: context.read<LabelCubit<T>>().reload,
|
delegate: SliverChildBuilderDelegate(
|
||||||
notificationPredicate: (notification) =>
|
(context, index) {
|
||||||
connectivityState.isConnected,
|
final l = labels.elementAt(index);
|
||||||
child: ListView(
|
return LabelItem<T>(
|
||||||
children: labels
|
name: l.name,
|
||||||
.map((l) => LabelItem<T>(
|
content: contentBuilder?.call(l) ??
|
||||||
name: l.name,
|
Text(
|
||||||
content: contentBuilder?.call(l) ??
|
translateMatchingAlgorithmName(
|
||||||
Text(
|
context, l.matchingAlgorithm) +
|
||||||
translateMatchingAlgorithmName(
|
((l.match?.isNotEmpty ?? false)
|
||||||
context, l.matchingAlgorithm) +
|
? ": ${l.match}"
|
||||||
((l.match?.isNotEmpty ?? false)
|
: ""),
|
||||||
? ": ${l.match}"
|
maxLines: 2,
|
||||||
: ""),
|
),
|
||||||
maxLines: 2,
|
onOpenEditPage: onEdit,
|
||||||
),
|
filterBuilder: filterBuilder,
|
||||||
onOpenEditPage: onEdit,
|
leading: leadingBuilder?.call(l),
|
||||||
filterBuilder: filterBuilder,
|
label: l,
|
||||||
leading: leadingBuilder?.call(l),
|
);
|
||||||
label: l,
|
},
|
||||||
))
|
childCount: labels.length,
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:paperless_mobile/features/linked_documents/bloc/linked_documents
|
|||||||
import 'package:paperless_mobile/features/linked_documents/bloc/state/linked_documents_state.dart';
|
import 'package:paperless_mobile/features/linked_documents/bloc/state/linked_documents_state.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
|
|
||||||
class LinkedDocumentsPage extends StatefulWidget {
|
class LinkedDocumentsPage extends StatefulWidget {
|
||||||
const LinkedDocumentsPage({super.key});
|
const LinkedDocumentsPage({super.key});
|
||||||
@@ -59,6 +60,19 @@ class _LinkedDocumentsPageState extends State<LinkedDocumentsPage> {
|
|||||||
isLabelClickable: false,
|
isLabelClickable: false,
|
||||||
isLoading: state.isLoading,
|
isLoading: state.isLoading,
|
||||||
hasLoaded: state.hasLoaded,
|
hasLoaded: state.hasLoaded,
|
||||||
|
onTap: (document) async {
|
||||||
|
final updatedDocument = await Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
DocumentDetailsRoute.routeName,
|
||||||
|
arguments: DocumentDetailsRouteArguments(
|
||||||
|
document: document,
|
||||||
|
isLabelClickable: false,
|
||||||
|
),
|
||||||
|
) as DocumentModel?;
|
||||||
|
if (updatedDocument != document) {
|
||||||
|
context.read<LinkedDocumentsCubit>().reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class SavedViewList extends StatelessWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(view.name),
|
title: Text(view.name),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
"${view.filterRules.length} filter(s) set"), //TODO: INTL w/ placeholder
|
"${view.filterRules.length} filter(s) set",
|
||||||
|
), //TODO: INTL w/ placeholder
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:paperless_mobile/features/documents/view/widgets/view_actions.da
|
|||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_details_cubit.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_details_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
|
|
||||||
class SavedViewPage extends StatefulWidget {
|
class SavedViewPage extends StatefulWidget {
|
||||||
final Future<void> Function(SavedView savedView) onDelete;
|
final Future<void> Function(SavedView savedView) onDelete;
|
||||||
@@ -101,6 +102,12 @@ class _SavedViewPageState extends State<SavedViewPage> {
|
|||||||
onTap: _onOpenDocumentDetails,
|
onTap: _onOpenDocumentDetails,
|
||||||
viewType: _viewType,
|
viewType: _viewType,
|
||||||
),
|
),
|
||||||
|
if (state.hasLoaded && state.isLoading)
|
||||||
|
const SliverToBoxAdapter(
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -111,20 +118,14 @@ class _SavedViewPageState extends State<SavedViewPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onOpenDocumentDetails(DocumentModel document) async {
|
void _onOpenDocumentDetails(DocumentModel document) async {
|
||||||
final updatedDocument = await Navigator.push<DocumentModel>(
|
final updatedDocument = await Navigator.pushNamed(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
DocumentDetailsRoute.routeName,
|
||||||
builder: (_) => BlocProvider(
|
arguments: DocumentDetailsRouteArguments(
|
||||||
create: (context) => DocumentDetailsCubit(
|
document: document,
|
||||||
context.read<PaperlessDocumentsApi>(),
|
isLabelClickable: false,
|
||||||
document,
|
|
||||||
),
|
|
||||||
child: const LabelRepositoriesProvider(
|
|
||||||
child: DocumentDetailsPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
) as DocumentModel?;
|
||||||
if (updatedDocument != document) {
|
if (updatedDocument != document) {
|
||||||
// Reload in case document was edited and might not fulfill filter criteria of saved view anymore
|
// Reload in case document was edited and might not fulfill filter criteria of saved view anymore
|
||||||
context.read<SavedViewDetailsCubit>().reload();
|
context.read<SavedViewDetailsCubit>().reload();
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ import 'package:paperless_mobile/core/repository/state/impl/document_type_reposi
|
|||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
||||||
|
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_search/view/document_search_page.dart';
|
||||||
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/app_drawer.dart';
|
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/view/widgets/scanned_image_item.dart';
|
import 'package:paperless_mobile/features/scan/view/widgets/scanned_image_item.dart';
|
||||||
|
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/file_helpers.dart';
|
import 'package:paperless_mobile/helpers/file_helpers.dart';
|
||||||
@@ -47,20 +49,100 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
builder: (context, connectedState) {
|
builder: (context, connectedState) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
drawer: const AppDrawer(),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () => _openDocumentScanner(context),
|
onPressed: () => _openDocumentScanner(context),
|
||||||
child: const Icon(Icons.add_a_photo_outlined),
|
child: const Icon(Icons.add_a_photo_outlined),
|
||||||
),
|
),
|
||||||
appBar: _buildAppBar(context, connectedState.isConnected),
|
//appBar: _buildAppBar(context, connectedState.isConnected),
|
||||||
body: Padding(
|
// body: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
// padding: const EdgeInsets.all(8.0),
|
||||||
child: _buildBody(connectedState.isConnected),
|
// child: _buildBody(connectedState.isConnected),
|
||||||
|
// ),
|
||||||
|
body: BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return NestedScrollView(
|
||||||
|
floatHeaderSlivers: false,
|
||||||
|
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||||
|
SearchAppBar(
|
||||||
|
hintText: "Search documents", //TODO: INTL
|
||||||
|
onOpenSearch: showDocumentSearchPage,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
child: _buildActions(connectedState.isConnected),
|
||||||
|
preferredSize: const Size.fromHeight(kTextTabBarHeight),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
body: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
if (state.isEmpty)
|
||||||
|
SliverFillRemaining(
|
||||||
|
child: _buildEmptyState(connectedState.isConnected),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
_buildImageGrid(state)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildActions(bool isConnected) {
|
||||||
|
return SizedBox(
|
||||||
|
height: kTextTabBarHeight,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextButton.icon(
|
||||||
|
label: Text("Preview"), //TODO: INTL
|
||||||
|
onPressed: state.isNotEmpty
|
||||||
|
? () => Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => DocumentView(
|
||||||
|
documentBytes: _assembleFileBytes(
|
||||||
|
state,
|
||||||
|
forcePdf: true,
|
||||||
|
).then((file) => file.bytes),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.visibility_outlined),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextButton.icon(
|
||||||
|
label: Text("Clear all"), //TODO: INTL
|
||||||
|
onPressed: state.isEmpty ? null : () => _reset(context),
|
||||||
|
icon: const Icon(Icons.delete_sweep_outlined),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextButton.icon(
|
||||||
|
label: Text("Upload"), //TODO: INTL
|
||||||
|
onPressed: state.isEmpty || !isConnected
|
||||||
|
? null
|
||||||
|
: () => _onPrepareDocumentUpload(context),
|
||||||
|
icon: const Icon(Icons.upload_outlined),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
AppBar _buildAppBar(BuildContext context, bool isConnected) {
|
AppBar _buildAppBar(BuildContext context, bool isConnected) {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
title: Text(S.of(context).documentScannerPageTitle),
|
title: Text(S.of(context).documentScannerPageTitle),
|
||||||
@@ -170,7 +252,7 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(bool isConnected) {
|
Widget _buildEmptyState(bool isConnected) {
|
||||||
return BlocBuilder<DocumentScannerCubit, List<File>>(
|
return BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||||
builder: (context, scans) {
|
builder: (context, scans) {
|
||||||
if (scans.isNotEmpty) {
|
if (scans.isNotEmpty) {
|
||||||
@@ -207,7 +289,7 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildImageGrid(List<File> scans) {
|
Widget _buildImageGrid(List<File> scans) {
|
||||||
return GridView.builder(
|
return SliverGrid.builder(
|
||||||
itemCount: scans.length,
|
itemCount: scans.length,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/material/search/m3_search_bar.dart';
|
import 'package:paperless_mobile/core/widgets/material/search/m3_search_bar.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/features/settings/view/dialogs/account_settings_dialog.dart';
|
||||||
|
|
||||||
typedef OpenSearchCallback = void Function(BuildContext context);
|
typedef OpenSearchCallback = void Function(BuildContext context);
|
||||||
|
|
||||||
@@ -8,11 +9,13 @@ class SearchAppBar extends StatefulWidget with PreferredSizeWidget {
|
|||||||
final PreferredSizeWidget? bottom;
|
final PreferredSizeWidget? bottom;
|
||||||
final OpenSearchCallback onOpenSearch;
|
final OpenSearchCallback onOpenSearch;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
final String hintText;
|
||||||
const SearchAppBar({
|
const SearchAppBar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.onOpenSearch,
|
required this.onOpenSearch,
|
||||||
this.bottom,
|
this.bottom,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
required this.hintText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,25 +29,29 @@ class _SearchAppBarState extends State<SearchAppBar> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliverAppBar(
|
return SliverAppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
floating: true,
|
floating: true,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
snap: true,
|
snap: true,
|
||||||
backgroundColor: widget.backgroundColor,
|
backgroundColor: widget.backgroundColor,
|
||||||
title: SearchBar(
|
title: SearchBar(
|
||||||
height: kToolbarHeight - 8,
|
height: kToolbarHeight - 8,
|
||||||
supportingText: "Search documents",
|
supportingText: widget.hintText,
|
||||||
onTap: () => widget.onOpenSearch(context),
|
onTap: () => widget.onOpenSearch(context),
|
||||||
leadingIcon: IconButton(
|
leadingIcon: IconButton(
|
||||||
icon: const Icon(Icons.menu),
|
icon: const Icon(Icons.menu),
|
||||||
onPressed: () {
|
onPressed: Scaffold.of(context).openDrawer,
|
||||||
Scaffold.of(context).openDrawer();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
trailingIcon: IconButton(
|
trailingIcon: IconButton(
|
||||||
icon: const CircleAvatar(
|
icon: const CircleAvatar(
|
||||||
child: Text("A"),
|
child: Text("A"),
|
||||||
),
|
),
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AccountSettingsDialog(),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
).paddedOnly(top: 4, bottom: 4),
|
).paddedOnly(top: 4, bottom: 4),
|
||||||
bottom: widget.bottom,
|
bottom: widget.bottom,
|
||||||
|
|||||||
103
lib/features/settings/view/dialogs/account_settings_dialog.dart
Normal file
103
lib/features/settings/view/dialogs/account_settings_dialog.dart
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
|
||||||
|
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
|
|
||||||
|
class AccountSettingsDialog extends StatelessWidget {
|
||||||
|
const AccountSettingsDialog({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
scrollable: true,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
icon: const PaperlessLogo.green(),
|
||||||
|
title: const Text(" Your Accounts"),
|
||||||
|
content: BlocBuilder<PaperlessServerInformationCubit,
|
||||||
|
PaperlessServerInformationState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
ExpansionTile(
|
||||||
|
leading: CircleAvatar(
|
||||||
|
child: Text(state.information?.username
|
||||||
|
?.toUpperCase()
|
||||||
|
.substring(0, 1) ??
|
||||||
|
''),
|
||||||
|
),
|
||||||
|
title: Text(state.information?.username ?? ''),
|
||||||
|
subtitle: Text(state.information?.host ?? ''),
|
||||||
|
children: const [
|
||||||
|
HintCard(
|
||||||
|
hintText: "WIP: Coming soon with multi user support!",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: const Icon(Icons.person_add_rounded),
|
||||||
|
title: const Text("Add another account"), //TODO: INTL
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
OutlinedButton(
|
||||||
|
child: Text(
|
||||||
|
S.of(context).appDrawerLogoutLabel,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
await _onLogout(context);
|
||||||
|
Navigator.of(context).maybePop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text(S.of(context).genericActionCloseLabel),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onLogout(BuildContext context) async {
|
||||||
|
try {
|
||||||
|
await context.read<AuthenticationCubit>().logout();
|
||||||
|
await context.read<ApplicationSettingsCubit>().clear();
|
||||||
|
await context.read<LabelRepository<Tag, TagRepositoryState>>().clear();
|
||||||
|
await context
|
||||||
|
.read<LabelRepository<Correspondent, CorrespondentRepositoryState>>()
|
||||||
|
.clear();
|
||||||
|
await context
|
||||||
|
.read<LabelRepository<DocumentType, DocumentTypeRepositoryState>>()
|
||||||
|
.clear();
|
||||||
|
await context
|
||||||
|
.read<LabelRepository<StoragePath, StoragePathRepositoryState>>()
|
||||||
|
.clear();
|
||||||
|
await context.read<SavedViewRepository>().clear();
|
||||||
|
await HydratedBloc.storage.clear();
|
||||||
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
showErrorMessage(context, error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,16 +26,6 @@ class SettingsPage extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(S.of(context).appDrawerSettingsLabel),
|
title: Text(S.of(context).appDrawerSettingsLabel),
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.logout),
|
|
||||||
color: Theme.of(context).colorScheme.error,
|
|
||||||
onPressed: () async {
|
|
||||||
await _onLogout(context);
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
bottomNavigationBar: BlocBuilder<PaperlessServerInformationCubit,
|
bottomNavigationBar: BlocBuilder<PaperlessServerInformationCubit,
|
||||||
PaperlessServerInformationState>(
|
PaperlessServerInformationState>(
|
||||||
@@ -48,14 +38,16 @@ class SettingsPage extends StatelessWidget {
|
|||||||
" " +
|
" " +
|
||||||
(info.username ?? 'unknown') +
|
(info.username ?? 'unknown') +
|
||||||
"@${info.host}",
|
"@${info.host}",
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
S.of(context).serverInformationPaperlessVersionText +
|
S.of(context).serverInformationPaperlessVersionText +
|
||||||
' ' +
|
' ' +
|
||||||
info.version.toString() +
|
info.version.toString() +
|
||||||
' (API v${info.apiVersion})',
|
' (API v${info.apiVersion})',
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -100,25 +92,4 @@ class SettingsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLogout(BuildContext context) async {
|
|
||||||
try {
|
|
||||||
await context.read<AuthenticationCubit>().logout();
|
|
||||||
await context.read<ApplicationSettingsCubit>().clear();
|
|
||||||
await context.read<LabelRepository<Tag, TagRepositoryState>>().clear();
|
|
||||||
await context
|
|
||||||
.read<LabelRepository<Correspondent, CorrespondentRepositoryState>>()
|
|
||||||
.clear();
|
|
||||||
await context
|
|
||||||
.read<LabelRepository<DocumentType, DocumentTypeRepositoryState>>()
|
|
||||||
.clear();
|
|
||||||
await context
|
|
||||||
.read<LabelRepository<StoragePath, StoragePathRepositoryState>>()
|
|
||||||
.clear();
|
|
||||||
await context.read<SavedViewRepository>().clear();
|
|
||||||
await HydratedBloc.storage.clear();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -627,5 +627,6 @@
|
|||||||
"verifyIdentityPageTitle": "Ověř svou identitu",
|
"verifyIdentityPageTitle": "Ověř svou identitu",
|
||||||
"@verifyIdentityPageTitle": {},
|
"@verifyIdentityPageTitle": {},
|
||||||
"verifyIdentityPageVerifyIdentityButtonLabel": "Ověřit identitu",
|
"verifyIdentityPageVerifyIdentityButtonLabel": "Ověřit identitu",
|
||||||
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
|
"@verifyIdentityPageVerifyIdentityButtonLabel": {},
|
||||||
|
"genericActionCloseLabel": "Close"
|
||||||
}
|
}
|
||||||
@@ -627,5 +627,6 @@
|
|||||||
"verifyIdentityPageTitle": "Verifiziere deine Identität",
|
"verifyIdentityPageTitle": "Verifiziere deine Identität",
|
||||||
"@verifyIdentityPageTitle": {},
|
"@verifyIdentityPageTitle": {},
|
||||||
"verifyIdentityPageVerifyIdentityButtonLabel": "Identität verifizieren",
|
"verifyIdentityPageVerifyIdentityButtonLabel": "Identität verifizieren",
|
||||||
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
|
"@verifyIdentityPageVerifyIdentityButtonLabel": {},
|
||||||
|
"genericActionCloseLabel": "Close"
|
||||||
}
|
}
|
||||||
@@ -627,5 +627,6 @@
|
|||||||
"verifyIdentityPageTitle": "Verify your identity",
|
"verifyIdentityPageTitle": "Verify your identity",
|
||||||
"@verifyIdentityPageTitle": {},
|
"@verifyIdentityPageTitle": {},
|
||||||
"verifyIdentityPageVerifyIdentityButtonLabel": "Verify Identity",
|
"verifyIdentityPageVerifyIdentityButtonLabel": "Verify Identity",
|
||||||
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
|
"@verifyIdentityPageVerifyIdentityButtonLabel": {},
|
||||||
|
"genericActionCloseLabel": "Close"
|
||||||
}
|
}
|
||||||
@@ -627,5 +627,6 @@
|
|||||||
"verifyIdentityPageTitle": "Kimliğinizi doğrulayın",
|
"verifyIdentityPageTitle": "Kimliğinizi doğrulayın",
|
||||||
"@verifyIdentityPageTitle": {},
|
"@verifyIdentityPageTitle": {},
|
||||||
"verifyIdentityPageVerifyIdentityButtonLabel": "Kimliği Doğrula",
|
"verifyIdentityPageVerifyIdentityButtonLabel": "Kimliği Doğrula",
|
||||||
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
|
"@verifyIdentityPageVerifyIdentityButtonLabel": {},
|
||||||
|
"genericActionCloseLabel": "Close"
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,7 @@ import 'package:paperless_mobile/features/settings/bloc/application_settings_sta
|
|||||||
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
import 'package:paperless_mobile/theme.dart';
|
import 'package:paperless_mobile/theme.dart';
|
||||||
import 'package:paperless_mobile/constants.dart';
|
import 'package:paperless_mobile/constants.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
@@ -254,6 +255,10 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
|
|||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
FormBuilderLocalizations.delegate,
|
FormBuilderLocalizations.delegate,
|
||||||
],
|
],
|
||||||
|
routes: {
|
||||||
|
DocumentDetailsRoute.routeName: (context) =>
|
||||||
|
const DocumentDetailsRoute(),
|
||||||
|
},
|
||||||
home: const AuthenticationWrapper(),
|
home: const AuthenticationWrapper(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
46
lib/routes/document_details_route.dart
Normal file
46
lib/routes/document_details_route.dart
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
||||||
|
|
||||||
|
class DocumentDetailsRoute extends StatelessWidget {
|
||||||
|
static const String routeName = "/documentDetails";
|
||||||
|
const DocumentDetailsRoute({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final args = ModalRoute.of(context)!.settings.arguments
|
||||||
|
as DocumentDetailsRouteArguments;
|
||||||
|
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => DocumentDetailsCubit(
|
||||||
|
context.read<PaperlessDocumentsApi>(),
|
||||||
|
args.document,
|
||||||
|
),
|
||||||
|
child: LabelRepositoriesProvider(
|
||||||
|
child: DocumentDetailsPage(
|
||||||
|
allowEdit: args.allowEdit,
|
||||||
|
isLabelClickable: args.isLabelClickable,
|
||||||
|
titleAndContentQueryString: args.titleAndContentQueryString,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocumentDetailsRouteArguments {
|
||||||
|
final DocumentModel document;
|
||||||
|
final bool isLabelClickable;
|
||||||
|
final bool allowEdit;
|
||||||
|
final String? titleAndContentQueryString;
|
||||||
|
|
||||||
|
DocumentDetailsRouteArguments({
|
||||||
|
required this.document,
|
||||||
|
this.isLabelClickable = true,
|
||||||
|
this.allowEdit = true,
|
||||||
|
this.titleAndContentQueryString,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -84,9 +84,11 @@ class DocumentModel extends Equatable {
|
|||||||
id: id,
|
id: id,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
documentType: documentType?.call() ?? this.documentType,
|
documentType:
|
||||||
correspondent: correspondent?.call() ?? this.correspondent,
|
documentType != null ? documentType.call() : this.documentType,
|
||||||
storagePath: storagePath?.call() ?? this.storagePath,
|
correspondent:
|
||||||
|
correspondent != null ? correspondent.call() : this.correspondent,
|
||||||
|
storagePath: storagePath != null ? storagePath.call() : this.storagePath,
|
||||||
tags: tags ?? this.tags,
|
tags: tags ?? this.tags,
|
||||||
created: created ?? this.created,
|
created: created ?? this.created,
|
||||||
modified: modified ?? this.modified,
|
modified: modified ?? this.modified,
|
||||||
|
|||||||
16
pubspec.lock
16
pubspec.lock
@@ -65,6 +65,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.0"
|
version: "2.10.0"
|
||||||
|
auto_route:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: auto_route
|
||||||
|
sha256: "12047baeca0e01df93165ef33275b32119d72699ab9a49dc64c20e78f586f96d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.4"
|
||||||
|
auto_route_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: auto_route_generator
|
||||||
|
sha256: de5bfbc02ae4eebb339dd90d325749ae7536e903f6513ef72b88954072d72b0e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.3"
|
||||||
badges:
|
badges:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ dependencies:
|
|||||||
responsive_builder: ^0.4.3
|
responsive_builder: ^0.4.3
|
||||||
open_filex: ^4.3.2
|
open_filex: ^4.3.2
|
||||||
dynamic_color: ^1.5.4
|
dynamic_color: ^1.5.4
|
||||||
|
auto_route: ^5.0.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
@@ -102,6 +103,7 @@ dev_dependencies:
|
|||||||
flutter_lints: ^1.0.0
|
flutter_lints: ^1.0.0
|
||||||
json_serializable: ^6.5.4
|
json_serializable: ^6.5.4
|
||||||
dart_code_metrics: ^5.4.0
|
dart_code_metrics: ^5.4.0
|
||||||
|
auto_route_generator: ^5.0.3
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|||||||
Reference in New Issue
Block a user