desktop notifications view

This commit is contained in:
julian 2022-11-14 10:40:31 -06:00
parent 7cf3a8efba
commit 60bdc6151b
5 changed files with 179 additions and 48 deletions

View file

@ -4,6 +4,8 @@ import 'package:stackwallet/models/notification_model.dart';
import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_container.dart'; import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -20,22 +22,33 @@ class NotificationCard extends StatelessWidget {
return Format.extractDateFrom(date.millisecondsSinceEpoch ~/ 1000); return Format.extractDateFrom(date.millisecondsSinceEpoch ~/ 1000);
} }
static const double mobileIconSize = 24;
static const double desktopIconSize = 30;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
return Stack( return Stack(
children: [ children: [
RoundedWhiteContainer( RoundedWhiteContainer(
padding: isDesktop
? const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
)
: const EdgeInsets.all(12),
child: Row( child: Row(
children: [ children: [
notification.changeNowId == null notification.changeNowId == null
? SvgPicture.asset( ? SvgPicture.asset(
notification.iconAssetName, notification.iconAssetName,
width: 24, width: isDesktop ? desktopIconSize : mobileIconSize,
height: 24, height: isDesktop ? desktopIconSize : mobileIconSize,
) )
: Container( : Container(
width: 24, width: isDesktop ? desktopIconSize : mobileIconSize,
height: 24, height: isDesktop ? desktopIconSize : mobileIconSize,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
@ -45,8 +58,8 @@ class NotificationCard extends StatelessWidget {
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.accentColorDark, .accentColorDark,
width: 24, width: isDesktop ? desktopIconSize : mobileIconSize,
height: 24, height: isDesktop ? desktopIconSize : mobileIconSize,
), ),
), ),
const SizedBox( const SizedBox(
@ -56,9 +69,35 @@ class NotificationCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Text( ConditionalParent(
notification.title, condition: isDesktop && !notification.read,
style: STextStyles.titleBold12(context), builder: (child) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
child,
Text(
"New",
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorGreen,
),
)
],
),
child: Text(
notification.title,
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
)
: STextStyles.titleBold12(context),
),
), ),
const SizedBox( const SizedBox(
height: 2, height: 2,
@ -68,11 +107,25 @@ class NotificationCard extends StatelessWidget {
children: [ children: [
Text( Text(
notification.description, notification.description,
style: STextStyles.label(context), style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.label(context),
), ),
Text( Text(
extractPrettyDateString(notification.date), extractPrettyDateString(notification.date),
style: STextStyles.label(context), style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.label(context),
), ),
], ],
), ),

View file

@ -7,6 +7,9 @@ import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_v
import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_notifications_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_notifications_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_about_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_about_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_support_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_support_view.dart';
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
import 'package:stackwallet/providers/global/notifications_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -20,7 +23,6 @@ class DesktopHomeView extends ConsumerStatefulWidget {
} }
class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> { class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
DesktopMenuItemId currentViewKey = DesktopMenuItemId.myStack;
final Map<DesktopMenuItemId, Widget> contentViews = { final Map<DesktopMenuItemId, Widget> contentViews = {
DesktopMenuItemId.myStack: const Navigator( DesktopMenuItemId.myStack: const Navigator(
key: Key("desktopStackHomeKey"), key: Key("desktopStackHomeKey"),
@ -58,10 +60,36 @@ class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
), ),
}; };
void onMenuSelectionChanged(DesktopMenuItemId newKey) { void onMenuSelectionWillChange(DesktopMenuItemId newKey) {
setState(() { // check for unread notifications and refresh provider before
currentViewKey = newKey; // showing notifications view
}); if (newKey == DesktopMenuItemId.notifications) {
ref.refresh(unreadNotificationsStateProvider);
}
// mark notifications as read if leaving notifications view
if (ref.read(currentDesktopMenuItemProvider.state).state ==
DesktopMenuItemId.notifications &&
newKey != DesktopMenuItemId.notifications) {
final Set<int> unreadNotificationIds =
ref.read(unreadNotificationsStateProvider.state).state;
if (unreadNotificationIds.isNotEmpty) {
List<Future<void>> futures = [];
for (int i = 0; i < unreadNotificationIds.length - 1; i++) {
futures.add(ref
.read(notificationsProvider)
.markAsRead(unreadNotificationIds.elementAt(i), false));
}
// wait for multiple to update if any
Future.wait(futures).then((_) {
// only notify listeners once
ref
.read(notificationsProvider)
.markAsRead(unreadNotificationIds.last, true);
});
}
}
} }
@override @override
@ -71,14 +99,16 @@ class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
child: Row( child: Row(
children: [ children: [
DesktopMenu( DesktopMenu(
onSelectionChanged: onMenuSelectionChanged, // onSelectionChanged: onMenuSelectionChanged,
onSelectionWillChange: onMenuSelectionWillChange,
), ),
Container( Container(
width: 1, width: 1,
color: Theme.of(context).extension<StackColors>()!.background, color: Theme.of(context).extension<StackColors>()!.background,
), ),
Expanded( Expanded(
child: contentViews[currentViewKey]!, child: contentViews[
ref.watch(currentDesktopMenuItemProvider.state).state]!,
), ),
], ],
), ),

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu_item.dart'; import 'package:stackwallet/pages_desktop_specific/home/desktop_menu_item.dart';
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -21,10 +22,12 @@ enum DesktopMenuItemId {
class DesktopMenu extends ConsumerStatefulWidget { class DesktopMenu extends ConsumerStatefulWidget {
const DesktopMenu({ const DesktopMenu({
Key? key, Key? key,
required this.onSelectionChanged, this.onSelectionChanged,
this.onSelectionWillChange,
}) : super(key: key); }) : super(key: key);
final void Function(DesktopMenuItemId)? onSelectionChanged; final void Function(DesktopMenuItemId)? onSelectionChanged;
final void Function(DesktopMenuItemId)? onSelectionWillChange;
@override @override
ConsumerState<DesktopMenu> createState() => _DesktopMenuState(); ConsumerState<DesktopMenu> createState() => _DesktopMenuState();
@ -35,12 +38,12 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
static const minimizedWidth = 72.0; static const minimizedWidth = 72.0;
double _width = expandedWidth; double _width = expandedWidth;
DesktopMenuItemId selectedMenuItem = DesktopMenuItemId.myStack;
void updateSelectedMenuItem(DesktopMenuItemId idKey) { void updateSelectedMenuItem(DesktopMenuItemId idKey) {
setState(() { widget.onSelectionWillChange?.call(idKey);
selectedMenuItem = idKey;
}); ref.read(currentDesktopMenuItemProvider.state).state = idKey;
widget.onSelectionChanged?.call(idKey); widget.onSelectionChanged?.call(idKey);
} }
@ -95,7 +98,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.walletDesktop, Assets.svg.walletDesktop,
width: 20, width: 20,
height: 20, height: 20,
color: DesktopMenuItemId.myStack == selectedMenuItem color: DesktopMenuItemId.myStack ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context) ? Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark .textDark
@ -106,7 +112,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
), ),
label: "My Stack", label: "My Stack",
value: DesktopMenuItemId.myStack, value: DesktopMenuItemId.myStack,
group: selectedMenuItem, group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem, onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth, iconOnly: _width == minimizedWidth,
), ),
@ -118,7 +125,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
// Assets.svg.exchangeDesktop, // Assets.svg.exchangeDesktop,
// width: 20, // width: 20,
// height: 20, // height: 20,
// color: DesktopMenuItemId.exchange == selectedMenuItem // color: DesktopMenuItemId.exchange == ref.watch(currentDesktopMenuItemProvider.state).state
// ? Theme.of(context) // ? Theme.of(context)
// .extension<StackColors>()! // .extension<StackColors>()!
// .textDark // .textDark
@ -129,7 +136,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
// ), // ),
// label: "Exchange", // label: "Exchange",
// value: DesktopMenuItemId.exchange, // value: DesktopMenuItemId.exchange,
// group: selectedMenuItem, // group: ref.watch(currentDesktopMenuItemProvider.state).state,
// onChanged: updateSelectedMenuItem, // onChanged: updateSelectedMenuItem,
// iconOnly: _width == minimizedWidth, // iconOnly: _width == minimizedWidth,
// ), // ),
@ -141,19 +148,22 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.bell, Assets.svg.bell,
width: 20, width: 20,
height: 20, height: 20,
color: color: DesktopMenuItemId.notifications ==
DesktopMenuItemId.notifications == selectedMenuItem ref
? Theme.of(context) .watch(currentDesktopMenuItemProvider.state)
.extension<StackColors>()! .state
.textDark ? Theme.of(context)
: Theme.of(context) .extension<StackColors>()!
.extension<StackColors>()! .textDark
.textDark : Theme.of(context)
.withOpacity(0.8), .extension<StackColors>()!
.textDark
.withOpacity(0.8),
), ),
label: "Notifications", label: "Notifications",
value: DesktopMenuItemId.notifications, value: DesktopMenuItemId.notifications,
group: selectedMenuItem, group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem, onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth, iconOnly: _width == minimizedWidth,
), ),
@ -165,7 +175,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.addressBookDesktop, Assets.svg.addressBookDesktop,
width: 20, width: 20,
height: 20, height: 20,
color: DesktopMenuItemId.addressBook == selectedMenuItem color: DesktopMenuItemId.addressBook ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context) ? Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark .textDark
@ -176,7 +189,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
), ),
label: "Address Book", label: "Address Book",
value: DesktopMenuItemId.addressBook, value: DesktopMenuItemId.addressBook,
group: selectedMenuItem, group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem, onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth, iconOnly: _width == minimizedWidth,
), ),
@ -188,7 +202,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.gear, Assets.svg.gear,
width: 20, width: 20,
height: 20, height: 20,
color: DesktopMenuItemId.settings == selectedMenuItem color: DesktopMenuItemId.settings ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context) ? Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark .textDark
@ -199,7 +216,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
), ),
label: "Settings", label: "Settings",
value: DesktopMenuItemId.settings, value: DesktopMenuItemId.settings,
group: selectedMenuItem, group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem, onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth, iconOnly: _width == minimizedWidth,
), ),
@ -211,7 +229,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.messageQuestion, Assets.svg.messageQuestion,
width: 20, width: 20,
height: 20, height: 20,
color: DesktopMenuItemId.support == selectedMenuItem color: DesktopMenuItemId.support ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context) ? Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark .textDark
@ -222,7 +243,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
), ),
label: "Support", label: "Support",
value: DesktopMenuItemId.support, value: DesktopMenuItemId.support,
group: selectedMenuItem, group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem, onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth, iconOnly: _width == minimizedWidth,
), ),
@ -234,7 +256,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.aboutDesktop, Assets.svg.aboutDesktop,
width: 20, width: 20,
height: 20, height: 20,
color: DesktopMenuItemId.about == selectedMenuItem color: DesktopMenuItemId.about ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context) ? Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark .textDark
@ -245,7 +270,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
), ),
label: "About", label: "About",
value: DesktopMenuItemId.about, value: DesktopMenuItemId.about,
group: selectedMenuItem, group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem, onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth, iconOnly: _width == minimizedWidth,
), ),
@ -262,7 +288,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
), ),
label: "Exit", label: "Exit",
value: 7, value: 7,
group: selectedMenuItem, group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: (_) { onChanged: (_) {
// todo: save stuff/ notify before exit? // todo: save stuff/ notify before exit?
exit(0); exit(0);

View file

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/notification_card.dart'; import 'package:stackwallet/notifications/notification_card.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
@ -47,10 +48,25 @@ class _DesktopNotificationsViewState
), ),
) )
: ListView.builder( : ListView.builder(
primary: false,
itemCount: notifications.length, itemCount: notifications.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return NotificationCard( final notification = notifications[index];
notification: notifications[index], if (notification.read == false) {
ref
.read(unreadNotificationsStateProvider.state)
.state
.add(notification.id);
}
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 5,
),
child: NotificationCard(
notification: notification,
),
); );
}, },
), ),

View file

@ -0,0 +1,5 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu.dart';
final currentDesktopMenuItemProvider =
StateProvider<DesktopMenuItemId>((ref) => DesktopMenuItemId.myStack);