mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 02:07:58 -06:00
feat: rework account management page, add tooltip to settings
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class BlocChangesObserver extends BlocObserver {
|
||||
@override
|
||||
void onChange(BlocBase bloc, Change change) {
|
||||
super.onChange(bloc, change);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hive_flutter/adapters.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/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart';
|
||||
import 'package:paperless_mobile/core/widgets/material/search/m3_search_bar.dart' as s;
|
||||
import 'package:paperless_mobile/features/document_search/view/document_search_page.dart';
|
||||
import 'package:paperless_mobile/features/login/model/user_account.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/dialogs/account_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class SliverSearchBar extends StatelessWidget {
|
||||
@@ -37,10 +42,14 @@ class SliverSearchBar extends StatelessWidget {
|
||||
onPressed: Scaffold.of(context).openDrawer,
|
||||
),
|
||||
trailingIcon: IconButton(
|
||||
icon: BlocBuilder<PaperlessServerInformationCubit, PaperlessServerInformationState>(
|
||||
builder: (context, state) {
|
||||
return CircleAvatar(
|
||||
child: Text(state.information?.userInitials ?? ''),
|
||||
icon: GlobalSettingsBuilder(
|
||||
builder: (context, settings) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(),
|
||||
builder: (context, box, _) {
|
||||
final account = box.get(settings.currentLoggedInUser!)!;
|
||||
return UserAvatar(userId: settings.currentLoggedInUser!, account: account);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/items/document_item.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
|
||||
class DocumentDetailedItem extends DocumentItem {
|
||||
final String? highlights;
|
||||
|
||||
@@ -89,8 +89,9 @@ class InboxCubit extends HydratedCubit<InboxState> with DocumentPagingBlocMixin
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (!isClosed) {
|
||||
emit(state.copyWith(inboxTags: inboxTags));
|
||||
|
||||
updateFilter(
|
||||
filter: DocumentFilter(
|
||||
sortField: SortField.added,
|
||||
@@ -99,6 +100,7 @@ class InboxCubit extends HydratedCubit<InboxState> with DocumentPagingBlocMixin
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Fetches inbox tag ids and loads the inbox items (documents).
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
@@ -72,14 +73,10 @@ class _InboxItemState extends State<InboxItem> {
|
||||
_buildTextWithLeadingIcon(
|
||||
Icon(
|
||||
Icons.person_outline,
|
||||
size: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.fontSize,
|
||||
size: Theme.of(context).textTheme.bodyMedium?.fontSize,
|
||||
),
|
||||
LabelText<Correspondent>(
|
||||
label: state.labels.correspondents[
|
||||
widget.document.correspondent],
|
||||
label: state.labels.correspondents[widget.document.correspondent],
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
placeholder: "-",
|
||||
),
|
||||
@@ -87,14 +84,10 @@ class _InboxItemState extends State<InboxItem> {
|
||||
_buildTextWithLeadingIcon(
|
||||
Icon(
|
||||
Icons.description_outlined,
|
||||
size: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.fontSize,
|
||||
size: Theme.of(context).textTheme.bodyMedium?.fontSize,
|
||||
),
|
||||
LabelText<DocumentType>(
|
||||
label: state.labels.documentTypes[
|
||||
widget.document.documentType],
|
||||
label: state.labels.documentTypes[widget.document.documentType],
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
placeholder: "-",
|
||||
),
|
||||
@@ -102,8 +95,10 @@ class _InboxItemState extends State<InboxItem> {
|
||||
const Spacer(),
|
||||
TagsWidget(
|
||||
tags: widget.document.tags
|
||||
.map((e) => state.labels.tags[e]!)
|
||||
.toList(),
|
||||
.map((e) => state.labels.tags[e])
|
||||
.whereNot((element) => element == null)
|
||||
.toList()
|
||||
.cast<Tag>(),
|
||||
isMultiLine: false,
|
||||
isClickable: false,
|
||||
showShortNames: true,
|
||||
@@ -142,8 +137,7 @@ class _InboxItemState extends State<InboxItem> {
|
||||
onPressed: () async {
|
||||
final shouldDelete = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => DeleteDocumentConfirmationDialog(
|
||||
document: widget.document),
|
||||
builder: (context) => DeleteDocumentConfirmationDialog(document: widget.document),
|
||||
) ??
|
||||
false;
|
||||
if (shouldDelete) {
|
||||
@@ -237,10 +231,7 @@ class _InboxItemState extends State<InboxItem> {
|
||||
setState(() {
|
||||
_isAsnAssignLoading = true;
|
||||
});
|
||||
context
|
||||
.read<InboxCubit>()
|
||||
.assignAsn(widget.document)
|
||||
.whenComplete(
|
||||
context.read<InboxCubit>().assignAsn(widget.document).whenComplete(
|
||||
() => setState(() => _isAsnAssignLoading = false),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,6 +170,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
}) async {
|
||||
assert(credentials.password != null && credentials.username != null);
|
||||
final userId = "${credentials.username}@$serverUrl";
|
||||
|
||||
final userAccountsBox = Hive.box<UserAccount>(HiveBoxes.userAccount);
|
||||
final userSettingsBox = Hive.box<UserSettings>(HiveBoxes.userSettings);
|
||||
|
||||
|
||||
@@ -28,11 +28,13 @@ class LoginPage extends StatefulWidget {
|
||||
) onSubmit;
|
||||
|
||||
final String submitText;
|
||||
final String titleString;
|
||||
|
||||
const LoginPage({
|
||||
Key? key,
|
||||
required this.onSubmit,
|
||||
required this.submitText,
|
||||
required this.titleString,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -47,7 +49,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false, // appBar: AppBar(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: FormBuilder(
|
||||
key: _formKey,
|
||||
child: PageView(
|
||||
@@ -55,6 +57,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
scrollBehavior: NeverScrollableScrollBehavior(),
|
||||
children: [
|
||||
ServerConnectionPage(
|
||||
titleString: widget.titleString,
|
||||
formBuilderKey: _formKey,
|
||||
onContinue: () {
|
||||
_pageController.nextPage(
|
||||
|
||||
@@ -15,11 +15,13 @@ import 'package:provider/provider.dart';
|
||||
class ServerConnectionPage extends StatefulWidget {
|
||||
final GlobalKey<FormBuilderState> formBuilderKey;
|
||||
final void Function() onContinue;
|
||||
final String titleString;
|
||||
|
||||
const ServerConnectionPage({
|
||||
super.key,
|
||||
required this.formBuilderKey,
|
||||
required this.onContinue,
|
||||
required this.titleString,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -35,7 +37,7 @@ class _ServerConnectionPageState extends State<ServerConnectionPage> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
toolbarHeight: kToolbarHeight - 4,
|
||||
title: Text(S.of(context)!.connectToPaperless),
|
||||
title: Text(widget.titleString),
|
||||
bottom: PreferredSize(
|
||||
child:
|
||||
_isCheckingConnection ? const LinearProgressIndicator() : const SizedBox(height: 4.0),
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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/widgets/material/search/m3_search_bar.dart' as s;
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/dialogs/account_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart';
|
||||
|
||||
typedef OpenSearchCallback = void Function(BuildContext context);
|
||||
|
||||
class SearchAppBar extends StatefulWidget implements PreferredSizeWidget {
|
||||
final PreferredSizeWidget? bottom;
|
||||
final OpenSearchCallback onOpenSearch;
|
||||
final Color? backgroundColor;
|
||||
final String hintText;
|
||||
const SearchAppBar({
|
||||
super.key,
|
||||
required this.onOpenSearch,
|
||||
this.bottom,
|
||||
this.backgroundColor,
|
||||
required this.hintText,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SearchAppBar> createState() => _SearchAppBarState();
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
|
||||
class _SearchAppBarState extends State<SearchAppBar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverAppBar(
|
||||
floating: true,
|
||||
pinned: true,
|
||||
snap: true,
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
title: s.SearchBar(
|
||||
height: kToolbarHeight - 12,
|
||||
supportingText: widget.hintText,
|
||||
onTap: () => widget.onOpenSearch(context),
|
||||
leadingIcon: IconButton(
|
||||
icon: const Icon(Icons.menu),
|
||||
onPressed: Scaffold.of(context).openDrawer,
|
||||
),
|
||||
trailingIcon: IconButton(
|
||||
icon: BlocBuilder<PaperlessServerInformationCubit, PaperlessServerInformationState>(
|
||||
builder: (context, state) {
|
||||
return CircleAvatar(
|
||||
child: Text(state.information?.userInitials ?? ''),
|
||||
);
|
||||
},
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => BlocProvider.value(
|
||||
value: context.read<PaperlessServerInformationCubit>(),
|
||||
child: const ManageAccountsPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
).paddedOnly(top: 8, bottom: 4),
|
||||
bottom: widget.bottom,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/login/model/login_form_credentials.dart';
|
||||
import 'package:paperless_mobile/features/login/model/user_account.dart';
|
||||
@@ -10,6 +13,7 @@ import 'package:paperless_mobile/features/settings/model/global_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/dialogs/switch_account_dialog.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/pages/switching_accounts_page.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart';
|
||||
|
||||
class ManageAccountsPage extends StatelessWidget {
|
||||
const ManageAccountsPage({super.key});
|
||||
@@ -19,15 +23,162 @@ class ManageAccountsPage extends StatelessWidget {
|
||||
return Dialog.fullscreen(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: CloseButton(),
|
||||
title: Text("Manage Accounts"), //TODO: INTL
|
||||
leading: const CloseButton(),
|
||||
title: const Text("Accounts"), //TODO: INTL
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
body: GlobalSettingsBuilder(
|
||||
builder: (context, globalSettings) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(),
|
||||
builder: (context, box, _) {
|
||||
final userIds = box.keys.toList().cast<String>();
|
||||
final otherAccounts = userIds
|
||||
.whereNot((element) => element == globalSettings.currentLoggedInUser)
|
||||
.toList();
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Your account", //TODO: INTL
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
).padded(16),
|
||||
_buildAccountTile(
|
||||
context,
|
||||
globalSettings.currentLoggedInUser!,
|
||||
box.get(globalSettings.currentLoggedInUser!)!,
|
||||
globalSettings,
|
||||
),
|
||||
if (otherAccounts.isNotEmpty) const Divider(),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (otherAccounts.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: Text(
|
||||
"Other accounts", //TODO: INTL
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
).padded(16),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) => _buildAccountTile(
|
||||
context,
|
||||
otherAccounts[index],
|
||||
box.get(otherAccounts[index])!,
|
||||
globalSettings,
|
||||
),
|
||||
childCount: otherAccounts.length,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: const Text("Add account"),
|
||||
leading: const Icon(Icons.person_add),
|
||||
onTap: () {
|
||||
_onAddAccount(context);
|
||||
},
|
||||
),
|
||||
// FilledButton.tonalIcon(
|
||||
// icon: Icon(Icons.person_add),
|
||||
// label: Text("Add account"),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAccountTile(
|
||||
BuildContext context,
|
||||
String userId,
|
||||
UserAccount account,
|
||||
GlobalSettings settings,
|
||||
) {
|
||||
final theme = Theme.of(context);
|
||||
return ListTile(
|
||||
title: Text(account.username),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (account.fullName != null) Text(account.fullName!),
|
||||
Text(account.serverUrl),
|
||||
],
|
||||
),
|
||||
isThreeLine: true,
|
||||
leading: UserAvatar(
|
||||
account: account,
|
||||
userId: userId,
|
||||
),
|
||||
trailing: PopupMenuButton(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
if (settings.currentLoggedInUser != userId)
|
||||
const PopupMenuItem(
|
||||
child: ListTile(
|
||||
title: Text("Switch"), //TODO: INTL
|
||||
leading: Icon(Icons.switch_account_outlined),
|
||||
),
|
||||
value: 0,
|
||||
),
|
||||
const PopupMenuItem(
|
||||
child: ListTile(
|
||||
title: Text("Remove"), // TODO: INTL
|
||||
leading: Icon(
|
||||
Icons.remove_circle_outline,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
value: 1,
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) async {
|
||||
if (value == 0) {
|
||||
// Switch
|
||||
final navigator = Navigator.of(context);
|
||||
if (settings.currentLoggedInUser == userId) return;
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SwitchingAccountsPage(),
|
||||
),
|
||||
);
|
||||
await context.read<AuthenticationCubit>().switchAccount(userId);
|
||||
navigator.popUntil((route) => route.isFirst);
|
||||
} else if (value == 1) {
|
||||
// Remove
|
||||
final shouldPop = userId == settings.currentLoggedInUser;
|
||||
await context.read<AuthenticationCubit>().removeAccount(userId);
|
||||
if (shouldPop) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onAddAccount(BuildContext context) {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LoginPage(
|
||||
titleString: "Add account", //TODO: INTL
|
||||
onSubmit: (context, username, password, serverUrl, clientCertificate) async {
|
||||
final userId = await context.read<AuthenticationCubit>().addAccount(
|
||||
credentials: LoginFormCredentials(
|
||||
@@ -49,88 +200,9 @@ class ManageAccountsPage extends StatelessWidget {
|
||||
context.read<AuthenticationCubit>().switchAccount(userId);
|
||||
}
|
||||
},
|
||||
submitText: "Add account",
|
||||
submitText: "Add account", //TODO: INTL
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
label: Text("Add account"),
|
||||
icon: Icon(Icons.person_add),
|
||||
),
|
||||
body: GlobalSettingsBuilder(
|
||||
builder: (context, globalSettings) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(),
|
||||
builder: (context, box, _) {
|
||||
final userIds = box.keys.toList().cast<String>();
|
||||
return ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
return _buildAccountTile(
|
||||
context,
|
||||
userIds[index],
|
||||
box.get(userIds[index])!,
|
||||
globalSettings,
|
||||
);
|
||||
},
|
||||
itemCount: userIds.length,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAccountTile(
|
||||
BuildContext context, String userId, UserAccount account, GlobalSettings settings) {
|
||||
final theme = Theme.of(context);
|
||||
return ListTile(
|
||||
selected: userId == settings.currentLoggedInUser,
|
||||
title: Text(account.username),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (account.fullName != null) Text(account.fullName!),
|
||||
Text(account.serverUrl),
|
||||
],
|
||||
),
|
||||
isThreeLine: true,
|
||||
leading: CircleAvatar(
|
||||
child: Text((account.fullName ?? account.username)
|
||||
.split(" ")
|
||||
.take(2)
|
||||
.map((e) => e.substring(0, 1))
|
||||
.map((e) => e.toUpperCase())
|
||||
.join(" ")),
|
||||
),
|
||||
onTap: () async {
|
||||
final navigator = Navigator.of(context);
|
||||
if (settings.currentLoggedInUser == userId) return;
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SwitchingAccountsPage(),
|
||||
),
|
||||
);
|
||||
await context.read<AuthenticationCubit>().switchAccount(userId);
|
||||
navigator.popUntil((route) => route.isFirst);
|
||||
},
|
||||
trailing: TextButton(
|
||||
child: Text(
|
||||
"Remove",
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
final shouldPop = userId == settings.currentLoggedInUser;
|
||||
await context.read<AuthenticationCubit>().removeAccount(userId);
|
||||
if (shouldPop) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,16 @@ class ApplicationSettingsPage extends StatelessWidget {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context)!.applicationSettings),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Tooltip(
|
||||
triggerMode: TooltipTriggerMode.tap,
|
||||
message: "These settings apply to all accounts", //TODO: INTL
|
||||
child: Icon(Icons.info_outline),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: const [
|
||||
|
||||
@@ -8,7 +8,19 @@ class SecuritySettingsPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(S.of(context)!.security)),
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context)!.security),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Tooltip(
|
||||
triggerMode: TooltipTriggerMode.tap,
|
||||
message: "These settings apply to the current user only", //TODO: INTL
|
||||
child: Icon(Icons.info_outline),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: const [
|
||||
BiometricAuthenticationSetting(),
|
||||
|
||||
28
lib/features/settings/view/widgets/user_avatar.dart
Normal file
28
lib/features/settings/view/widgets/user_avatar.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/features/login/model/user_account.dart';
|
||||
|
||||
class UserAvatar extends StatelessWidget {
|
||||
final String userId;
|
||||
final UserAccount account;
|
||||
const UserAvatar({
|
||||
super.key,
|
||||
required this.userId,
|
||||
required this.account,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backgroundColor = Colors.primaries[userId.hashCode % Colors.primaries.length];
|
||||
final foregroundColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
|
||||
return CircleAvatar(
|
||||
child: Text((account.fullName ?? account.username)
|
||||
.split(" ")
|
||||
.take(2)
|
||||
.map((e) => e.substring(0, 1))
|
||||
.map((e) => e.toUpperCase())
|
||||
.join("")),
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: foregroundColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -324,6 +324,7 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
||||
return const VerifyIdentityPage();
|
||||
}
|
||||
return LoginPage(
|
||||
titleString: S.of(context)!.connectToPaperless,
|
||||
submitText: S.of(context)!.signIn,
|
||||
onSubmit: _onLogin,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user