@@ -12,6 +12,7 @@ import 'package:openvine/blocs/email_verification/email_verification_cubit.dart'
1212import 'package:openvine/blocs/my_profile/my_profile_bloc.dart' ;
1313import 'package:openvine/providers/app_providers.dart' ;
1414import 'package:openvine/providers/nip05_verification_provider.dart' ;
15+ import 'package:openvine/providers/shared_preferences_provider.dart' ;
1516import 'package:openvine/providers/user_profile_providers.dart' ;
1617import 'package:openvine/screens/auth/secure_account_screen.dart' ;
1718import 'package:openvine/screens/auth/welcome_screen.dart' ;
@@ -27,6 +28,9 @@ import 'package:openvine/widgets/user_name.dart';
2728
2829/// Profile header widget displaying avatar, stats, name, and bio.
2930class ProfileHeaderWidget extends ConsumerWidget {
31+ static const _dismissedDivineLoginBannerPrefix =
32+ 'dismissed_divine_login_banner_' ;
33+
3034 const ProfileHeaderWidget ({
3135 required this .userIdHex,
3236 required this .isOwnProfile,
@@ -89,6 +93,11 @@ class ProfileHeaderWidget extends ConsumerWidget {
8993 ref.watch (currentAuthStateProvider);
9094 final isAnonymous = authService.isAnonymous;
9195 final hasExpiredSession = authService.hasExpiredOAuthSession;
96+ final prefs = ref.watch (sharedPreferencesProvider);
97+ final dismissedDivineLoginBannerKey =
98+ '$_dismissedDivineLoginBannerPrefix $userIdHex ' ;
99+ final isDivineLoginBannerDismissed =
100+ prefs.getBool (dismissedDivineLoginBannerKey) ?? false ;
92101
93102 // Use profile color as header background (like original Vine)
94103 // Color covers avatar/stats, then fades to dark for name/bio readability
@@ -128,8 +137,12 @@ class ProfileHeaderWidget extends ConsumerWidget {
128137
129138 // Session expired banner for divineOAuth users (only on own
130139 // profile) — prompts re-login instead of "Secure Your Account"
131- if (isOwnProfile && hasExpiredSession)
132- const _SessionExpiredBanner ()
140+ if (isOwnProfile &&
141+ hasExpiredSession &&
142+ ! isDivineLoginBannerDismissed)
143+ _SessionExpiredBanner (
144+ dismissedPreferenceKey: dismissedDivineLoginBannerKey,
145+ )
133146 // Secure account banner for anonymous users (only on own
134147 // profile)
135148 else if (isOwnProfile && isAnonymous)
@@ -420,7 +433,9 @@ class _IdentityNotRecoverableBanner extends StatelessWidget {
420433/// Prompts the user to sign in again instead of showing "Secure Your Account".
421434/// Attempts a silent token refresh first; navigates to login only if that fails.
422435class _SessionExpiredBanner extends ConsumerStatefulWidget {
423- const _SessionExpiredBanner ();
436+ const _SessionExpiredBanner ({required this .dismissedPreferenceKey});
437+
438+ final String dismissedPreferenceKey;
424439
425440 @override
426441 ConsumerState <_SessionExpiredBanner > createState () =>
@@ -429,6 +444,7 @@ class _SessionExpiredBanner extends ConsumerStatefulWidget {
429444
430445class _SessionExpiredBannerState extends ConsumerState <_SessionExpiredBanner > {
431446 bool _isRefreshing = false ;
447+ bool _isDismissed = false ;
432448
433449 Future <void > _onSignIn () async {
434450 setState (() => _isRefreshing = true );
@@ -444,8 +460,16 @@ class _SessionExpiredBannerState extends ConsumerState<_SessionExpiredBanner> {
444460 }
445461 }
446462
463+ Future <void > _dismissBanner () async {
464+ setState (() => _isDismissed = true );
465+ final prefs = ref.read (sharedPreferencesProvider);
466+ await prefs.setBool (widget.dismissedPreferenceKey, true );
467+ }
468+
447469 @override
448470 Widget build (BuildContext context) {
471+ if (_isDismissed) return const SizedBox .shrink ();
472+
449473 return Container (
450474 margin: const EdgeInsets .only (bottom: 16 ),
451475 padding: const EdgeInsets .all (16 ),
@@ -499,6 +523,11 @@ class _SessionExpiredBannerState extends ConsumerState<_SessionExpiredBanner> {
499523 ),
500524 ),
501525 ),
526+ IconButton (
527+ onPressed: _dismissBanner,
528+ icon: const Icon (Icons .close, color: VineTheme .whiteText, size: 20 ),
529+ tooltip: 'Dismiss' ,
530+ ),
502531 ],
503532 ),
504533 );
0 commit comments