kopia lustrzana https://gitlab.com/mysocialportal/relatica
Initial ActiveProfileSelector implementation using Notifications system
rodzic
854c142c20
commit
b01ea9b95e
|
@ -1,10 +1,12 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:relatica/utils/snackbar_builder.dart';
|
import 'package:relatica/utils/snackbar_builder.dart';
|
||||||
|
|
||||||
import '../routes.dart';
|
import '../routes.dart';
|
||||||
import '../services/notifications_manager.dart';
|
import '../services/notifications_manager.dart';
|
||||||
|
import '../utils/active_profile_selector.dart';
|
||||||
|
|
||||||
enum NavBarButtons {
|
enum NavBarButtons {
|
||||||
timelines,
|
timelines,
|
||||||
|
@ -14,15 +16,26 @@ enum NavBarButtons {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppBottomNavBar extends StatelessWidget {
|
class AppBottomNavBar extends StatelessWidget {
|
||||||
|
static final _logger = Logger('$AppBottomNavBar');
|
||||||
final NavBarButtons currentButton;
|
final NavBarButtons currentButton;
|
||||||
|
|
||||||
const AppBottomNavBar({super.key, required this.currentButton});
|
const AppBottomNavBar({super.key, required this.currentButton});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final notificationManager = context.watch<NotificationsManager>();
|
final nmResult = context
|
||||||
final hasNotifications =
|
.watch<ActiveProfileSelector<NotificationsManager>>()
|
||||||
notificationManager.notifications.where((n) => !n.dismissed).isNotEmpty;
|
.activeEntry;
|
||||||
|
final hasNotifications = nmResult.fold(
|
||||||
|
onSuccess: (manager) =>
|
||||||
|
manager.notifications
|
||||||
|
.where((n) => !n.dismissed)
|
||||||
|
.isNotEmpty,
|
||||||
|
onError: (error) {
|
||||||
|
_logger.info('Error getting notifications manager: $error');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
return BottomNavigationBar(
|
return BottomNavigationBar(
|
||||||
onTap: (index) {
|
onTap: (index) {
|
||||||
final newButton = _indexToButton(index);
|
final newButton = _indexToButton(index);
|
||||||
|
@ -89,8 +102,8 @@ class AppBottomNavBar extends StatelessWidget {
|
||||||
throw ArgumentError('$index has no button type');
|
throw ArgumentError('$index has no button type');
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BottomNavigationBarItem> _menuItems(
|
List<BottomNavigationBarItem> _menuItems(BuildContext context,
|
||||||
BuildContext context, bool hasNotifications) {
|
bool hasNotifications) {
|
||||||
return [
|
return [
|
||||||
const BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
label: 'Timelines',
|
label: 'Timelines',
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:relatica/utils/active_profile_selector.dart';
|
||||||
|
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../models/user_notification.dart';
|
import '../models/user_notification.dart';
|
||||||
|
@ -14,6 +16,7 @@ import '../utils/snackbar_builder.dart';
|
||||||
import 'image_control.dart';
|
import 'image_control.dart';
|
||||||
|
|
||||||
class NotificationControl extends StatelessWidget {
|
class NotificationControl extends StatelessWidget {
|
||||||
|
static final _logger = Logger('$NotificationControl');
|
||||||
final UserNotification notification;
|
final UserNotification notification;
|
||||||
|
|
||||||
const NotificationControl({
|
const NotificationControl({
|
||||||
|
@ -41,7 +44,17 @@ class NotificationControl extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
const iconSize = 50.0;
|
const iconSize = 50.0;
|
||||||
final manager = context.watch<NotificationsManager>();
|
final manager = context
|
||||||
|
.watch<ActiveProfileSelector<NotificationsManager>>()
|
||||||
|
.activeEntry
|
||||||
|
.fold(
|
||||||
|
onSuccess: (manager) => manager,
|
||||||
|
onError: (error) {
|
||||||
|
_logger.severe('Error getting notification manager: $error');
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
final fromIcon =
|
final fromIcon =
|
||||||
getIt<ConnectionsManager>().getById(notification.fromId).fold(
|
getIt<ConnectionsManager>().getById(notification.fromId).fold(
|
||||||
onSuccess: (connection) => ImageControl(
|
onSuccess: (connection) => ImageControl(
|
||||||
|
@ -120,11 +133,13 @@ class NotificationControl extends StatelessWidget {
|
||||||
notification.type == NotificationType.direct_message
|
notification.type == NotificationType.direct_message
|
||||||
? null
|
? null
|
||||||
: IconButton(
|
: IconButton(
|
||||||
onPressed: () async {
|
onPressed: manager == null
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
final result = await manager.markSeen(notification);
|
final result = await manager.markSeen(notification);
|
||||||
if (result.isFailure) {
|
if (result.isFailure) {
|
||||||
buildSnackbar(
|
buildSnackbar(context,
|
||||||
context, 'Error marking notification: ${result.error}');
|
'Error marking notification: ${result.error}');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: Icon(Icons.close_rounded)),
|
icon: Icon(Icons.close_rounded)),
|
||||||
|
|
|
@ -17,7 +17,9 @@ class StandardAppDrawer extends StatelessWidget {
|
||||||
(p) => ListTile(
|
(p) => ListTile(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await getIt<AccountsService>().setActiveProfile(p);
|
await getIt<AccountsService>().setActiveProfile(p);
|
||||||
clearCaches();
|
if (context.mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
child: CachedNetworkImage(imageUrl: p.avatar)),
|
child: CachedNetworkImage(imageUrl: p.avatar)),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:relatica/utils/active_profile_selector.dart';
|
||||||
|
|
||||||
import 'data/interfaces/connections_repo_intf.dart';
|
import 'data/interfaces/connections_repo_intf.dart';
|
||||||
import 'data/interfaces/groups_repo.intf.dart';
|
import 'data/interfaces/groups_repo.intf.dart';
|
||||||
|
@ -57,8 +58,8 @@ Future<void> dependencyInjectionInitialization() async {
|
||||||
getIt.registerSingleton<TimelineManager>(timelineManager);
|
getIt.registerSingleton<TimelineManager>(timelineManager);
|
||||||
getIt.registerLazySingleton<MediaUploadAttachmentHelper>(
|
getIt.registerLazySingleton<MediaUploadAttachmentHelper>(
|
||||||
() => MediaUploadAttachmentHelper());
|
() => MediaUploadAttachmentHelper());
|
||||||
getIt.registerLazySingleton<NotificationsManager>(
|
getIt.registerLazySingleton<ActiveProfileSelector<NotificationsManager>>(
|
||||||
() => NotificationsManager());
|
() => ActiveProfileSelector((_) => NotificationsManager()));
|
||||||
getIt.registerLazySingleton<DirectMessageService>(
|
getIt.registerLazySingleton<DirectMessageService>(
|
||||||
() => DirectMessageService());
|
() => DirectMessageService());
|
||||||
getIt.registerLazySingleton<InteractionsManager>(() => InteractionsManager());
|
getIt.registerLazySingleton<InteractionsManager>(() => InteractionsManager());
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart';
|
import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:relatica/utils/active_profile_selector.dart';
|
||||||
|
|
||||||
import 'app_theme.dart';
|
import 'app_theme.dart';
|
||||||
import 'di_initialization.dart';
|
import 'di_initialization.dart';
|
||||||
|
@ -75,8 +76,10 @@ class App extends StatelessWidget {
|
||||||
ChangeNotifierProvider<TimelineManager>(
|
ChangeNotifierProvider<TimelineManager>(
|
||||||
create: (_) => getIt<TimelineManager>(),
|
create: (_) => getIt<TimelineManager>(),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider<NotificationsManager>(
|
ChangeNotifierProvider<
|
||||||
create: (_) => getIt<NotificationsManager>(),
|
ActiveProfileSelector<NotificationsManager>>(
|
||||||
|
create: (_) =>
|
||||||
|
getIt<ActiveProfileSelector<NotificationsManager>>(),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider<DirectMessageService>(
|
ChangeNotifierProvider<DirectMessageService>(
|
||||||
create: (_) => getIt<DirectMessageService>(),
|
create: (_) => getIt<DirectMessageService>(),
|
||||||
|
|
|
@ -10,6 +10,7 @@ import '../controls/status_and_refresh_button.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../services/network_status_service.dart';
|
import '../services/network_status_service.dart';
|
||||||
import '../services/notifications_manager.dart';
|
import '../services/notifications_manager.dart';
|
||||||
|
import '../utils/active_profile_selector.dart';
|
||||||
import '../utils/snackbar_builder.dart';
|
import '../utils/snackbar_builder.dart';
|
||||||
|
|
||||||
class NotificationsScreen extends StatelessWidget {
|
class NotificationsScreen extends StatelessWidget {
|
||||||
|
@ -21,17 +22,32 @@ class NotificationsScreen extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_logger.finest('Building');
|
_logger.finest('Building');
|
||||||
final nss = getIt<NetworkStatusService>();
|
final nss = getIt<NetworkStatusService>();
|
||||||
final manager = context.watch<NotificationsManager>();
|
final managerResult = context
|
||||||
final notifications = manager.notifications;
|
.watch<ActiveProfileSelector<NotificationsManager>>()
|
||||||
|
.activeEntry;
|
||||||
late final String title;
|
late final String title;
|
||||||
late final Widget body;
|
late final Widget body;
|
||||||
|
late final List<Widget> actions;
|
||||||
|
managerResult.match(onSuccess: (manager) {
|
||||||
|
final notifications = manager.notifications;
|
||||||
|
actions = [
|
||||||
|
StatusAndRefreshButton(
|
||||||
|
valueListenable: nss.notificationsUpdateStatus,
|
||||||
|
refreshFunction: () async => manager.updateNotifications(),
|
||||||
|
busyColor: Theme.of(context).colorScheme.background,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async => _clearAllNotifications(context, manager),
|
||||||
|
icon: const Icon(Icons.cleaning_services),
|
||||||
|
),
|
||||||
|
];
|
||||||
if (notifications.isEmpty) {
|
if (notifications.isEmpty) {
|
||||||
manager.updateNotifications();
|
manager.updateNotifications();
|
||||||
title = 'Notifications';
|
title = 'Notifications';
|
||||||
body = Center(
|
body = Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: const [
|
||||||
const Center(child: Text('No notifications')),
|
Center(child: Text('No notifications')),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
@ -83,23 +99,23 @@ class NotificationsScreen extends StatelessWidget {
|
||||||
itemCount: notifications.length + 2),
|
itemCount: notifications.length + 2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}, onError: (error) {
|
||||||
|
title = 'Notifications';
|
||||||
|
actions = [];
|
||||||
|
body = Center(
|
||||||
|
child: Column(
|
||||||
|
children: const [
|
||||||
|
Center(child: Text('Error getting notifications')),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: StandardAppBar.build(
|
appBar: StandardAppBar.build(
|
||||||
context,
|
context,
|
||||||
title,
|
title,
|
||||||
withDrawer: true,
|
withDrawer: true,
|
||||||
actions: [
|
actions: actions,
|
||||||
StatusAndRefreshButton(
|
|
||||||
valueListenable: nss.notificationsUpdateStatus,
|
|
||||||
refreshFunction: () async => manager.updateNotifications(),
|
|
||||||
busyColor: Theme.of(context).colorScheme.background,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () async => _clearAllNotifications(context, manager),
|
|
||||||
icon: const Icon(Icons.cleaning_services),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
drawer: StandardAppDrawer(),
|
drawer: StandardAppDrawer(),
|
||||||
body: body,
|
body: body,
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:relatica/services/auth_service.dart';
|
||||||
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
|
import '../globals.dart';
|
||||||
|
import '../models/auth/profile.dart';
|
||||||
|
import '../models/exec_error.dart';
|
||||||
|
|
||||||
|
class ActiveProfileSelector<T extends ChangeNotifier> extends ChangeNotifier {
|
||||||
|
final _entries = <Profile, T>{};
|
||||||
|
|
||||||
|
final T Function(Profile p) _entryBuilder;
|
||||||
|
|
||||||
|
ActiveProfileSelector(T Function(Profile p) entryBuilder)
|
||||||
|
: _entryBuilder = entryBuilder;
|
||||||
|
|
||||||
|
Result<T, ExecError> get activeEntry {
|
||||||
|
final service = getIt<AccountsService>();
|
||||||
|
if (!service.loggedIn) {
|
||||||
|
return buildErrorResult(
|
||||||
|
type: ErrorType.localError,
|
||||||
|
message: 'No Logged In User',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final p = service.currentProfile;
|
||||||
|
return runCatching(() {
|
||||||
|
final entry = _entries.putIfAbsent(p, () => _buildNewEntry(p));
|
||||||
|
return Result.ok(entry).execErrorCast();
|
||||||
|
}).execErrorCast();
|
||||||
|
}
|
||||||
|
|
||||||
|
T _buildNewEntry(Profile p) {
|
||||||
|
final newEntry = _entryBuilder(p);
|
||||||
|
newEntry.addListener(() => notifyListeners());
|
||||||
|
return newEntry;
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue