Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue where drawer icon does not trigger the drawer to open on the feed page #1026

Merged
merged 2 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions lib/feed/view/feed_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class FeedPage extends StatefulWidget {
this.communityName,
this.userId,
this.username,
this.scaffoldStateKey,
});

/// The type of feed to display.
Expand Down Expand Up @@ -80,6 +81,9 @@ class FeedPage extends StatefulWidget {
/// This is useful if we want to keep the user on the "same" page
final bool useGlobalFeedBloc;

/// The scaffold key which holds the drawer
final GlobalKey<ScaffoldState>? scaffoldStateKey;

@override
State<FeedPage> createState() => _FeedPageState();
}
Expand Down Expand Up @@ -123,7 +127,7 @@ class _FeedPageState extends State<FeedPage> with AutomaticKeepAliveClientMixin<

return BlocProvider.value(
value: bloc,
child: const FeedView(),
child: FeedView(scaffoldStateKey: widget.scaffoldStateKey),
);
}

Expand All @@ -139,13 +143,16 @@ class _FeedPageState extends State<FeedPage> with AutomaticKeepAliveClientMixin<
username: widget.username,
reset: true,
)),
child: const FeedView(),
child: FeedView(scaffoldStateKey: widget.scaffoldStateKey),
);
}
}

class FeedView extends StatefulWidget {
const FeedView({super.key});
const FeedView({super.key, this.scaffoldStateKey});

/// The scaffold key which holds the drawer
final GlobalKey<ScaffoldState>? scaffoldStateKey;

@override
State<FeedView> createState() => _FeedViewState();
Expand Down Expand Up @@ -291,7 +298,10 @@ class _FeedViewState extends State<FeedView> {
physics: showCommunitySidebar ? const NeverScrollableScrollPhysics() : null, // Disable scrolling on the feed page when the community sidebar is open
controller: _scrollController,
slivers: <Widget>[
FeedPageAppBar(showAppBarTitle: (state.feedType == FeedType.general && state.status != FeedStatus.initial) ? true : showAppBarTitle),
FeedPageAppBar(
showAppBarTitle: (state.feedType == FeedType.general && state.status != FeedStatus.initial) ? true : showAppBarTitle,
scaffoldStateKey: widget.scaffoldStateKey,
),
// Display loading indicator until the feed is fetched
if (state.status == FeedStatus.initial)
const SliverFillRemaining(
Expand Down
10 changes: 7 additions & 3 deletions lib/feed/widgets/feed_page_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ import 'package:thunder/shared/sort_picker.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';

class FeedPageAppBar extends StatelessWidget {
const FeedPageAppBar({super.key, this.showAppBarTitle = true});
const FeedPageAppBar({super.key, this.showAppBarTitle = true, this.scaffoldStateKey});

/// Whether to show the app bar title
final bool showAppBarTitle;

/// The scaffold key of the parent scaffold holding the drawer.
final GlobalKey<ScaffoldState>? scaffoldStateKey;

@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
Expand All @@ -40,7 +44,7 @@ class FeedPageAppBar extends StatelessWidget {
toolbarHeight: 70.0,
title: FeedAppBarTitle(visible: showAppBarTitle),
leading: IconButton(
icon: Navigator.of(context).canPop() && feedBloc.state.feedType == FeedType.community
icon: scaffoldStateKey == null
? (!kIsWeb && Platform.isIOS
? Icon(
Icons.arrow_back_ios_new_rounded,
Expand All @@ -50,7 +54,7 @@ class FeedPageAppBar extends StatelessWidget {
: Icon(Icons.menu, semanticLabel: MaterialLocalizations.of(context).openAppDrawerTooltip),
onPressed: () {
HapticFeedback.mediumImpact();
(Navigator.of(context).canPop() && feedBloc.state.feedType == FeedType.community) ? Navigator.of(context).maybePop() : Scaffold.of(context).openDrawer();
(scaffoldStateKey == null && feedBloc.state.feedType == FeedType.community) ? Navigator.of(context).maybePop() : scaffoldStateKey?.currentState?.openDrawer();
},
),
actions: feedState.status != FeedStatus.failureLoadingCommunity
Expand Down
247 changes: 129 additions & 118 deletions lib/thunder/pages/thunder_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class _ThunderState extends State<Thunder> {

bool reduceAnimations = false;

final GlobalKey<ScaffoldState> scaffoldStateKey = GlobalKey<ScaffoldState>();

final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();

late final StreamSubscription mediaIntentDataStreamSubscription;
Expand Down Expand Up @@ -404,129 +406,138 @@ class _ThunderState extends State<Thunder> {
// Update the variable so that it can be used in _handleBackButtonPress
_isFabOpen = thunderBlocState.isFabOpen;

return Scaffold(
return ScaffoldMessenger(
key: scaffoldMessengerKey,
drawer: selectedPageIndex == 0
? CommunityDrawer(
navigateToAccount: () {
Navigator.of(context).pop();

if (reduceAnimations) {
pageController.jumpToPage(2);
} else {
pageController.animateToPage(2, duration: const Duration(milliseconds: 500), curve: Curves.ease);
}
},
)
: null,
floatingActionButton: thunderBlocState.enableFeedsFab
? AnimatedOpacity(
opacity: selectedPageIndex == 0 ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
curve: Curves.easeIn,
child: const FeedFAB(),
)
: null,
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
bottomNavigationBar: CustomBottomNavigationBar(
selectedPageIndex: selectedPageIndex,
onPageChange: (int index) {
setState(() {
selectedPageIndex = index;

if (reduceAnimations) {
pageController.jumpToPage(index);
} else {
pageController.animateToPage(index, duration: const Duration(milliseconds: 500), curve: Curves.ease);
child: Scaffold(
key: scaffoldStateKey,
drawer: selectedPageIndex == 0
? CommunityDrawer(
navigateToAccount: () {
Navigator.of(context).pop();

if (reduceAnimations) {
pageController.jumpToPage(2);
} else {
pageController.animateToPage(2, duration: const Duration(milliseconds: 500), curve: Curves.ease);
}
},
)
: null,
floatingActionButton: thunderBlocState.enableFeedsFab
? AnimatedOpacity(
opacity: selectedPageIndex == 0 ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
curve: Curves.easeIn,
child: const FeedFAB(),
)
: null,
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
bottomNavigationBar: CustomBottomNavigationBar(
selectedPageIndex: selectedPageIndex,
onPageChange: (int index) {
setState(() {
selectedPageIndex = index;

if (reduceAnimations) {
pageController.jumpToPage(index);
} else {
pageController.animateToPage(index, duration: const Duration(milliseconds: 500), curve: Curves.ease);
}
});
},
),
body: BlocConsumer<AuthBloc, AuthState>(
listenWhen: (AuthState previous, AuthState current) {
if (previous.isLoggedIn != current.isLoggedIn || previous.status == AuthStatus.initial) return true;
return false;
},
buildWhen: (previous, current) => current.status != AuthStatus.failure && current.status != AuthStatus.loading,
listener: (context, state) {
context.read<AccountBloc>().add(GetAccountInformation());

// Add a bit of artificial delay to allow preferences to set the proper active profile
Future.delayed(const Duration(milliseconds: 500), () => context.read<InboxBloc>().add(const GetInboxEvent(reset: true)));
if (context.read<FeedBloc>().state.status != FeedStatus.initial) {
context.read<FeedBloc>().add(
FeedFetchedEvent(
feedType: FeedType.general,
postListingType: thunderBlocState.defaultListingType,
sortType: thunderBlocState.defaultSortType,
reset: true,
),
);
}
});
},
),
body: BlocConsumer<AuthBloc, AuthState>(
listenWhen: (AuthState previous, AuthState current) {
if (previous.isLoggedIn != current.isLoggedIn || previous.status == AuthStatus.initial) return true;
return false;
},
buildWhen: (previous, current) => current.status != AuthStatus.failure && current.status != AuthStatus.loading,
listener: (context, state) {
context.read<AccountBloc>().add(GetAccountInformation());

// Add a bit of artificial delay to allow preferences to set the proper active profile
Future.delayed(const Duration(milliseconds: 500), () => context.read<InboxBloc>().add(const GetInboxEvent(reset: true)));
if (context.read<FeedBloc>().state.status != FeedStatus.initial) {
context.read<FeedBloc>().add(
FeedFetchedEvent(
feedType: FeedType.general,
postListingType: thunderBlocState.defaultListingType,
sortType: thunderBlocState.defaultSortType,
reset: true,
},
builder: (context, state) {
switch (state.status) {
case AuthStatus.initial:
context.read<AuthBloc>().add(CheckAuth());
return Scaffold(
appBar: AppBar(),
body: Center(
child: Container(),
),
);
}
},
builder: (context, state) {
switch (state.status) {
case AuthStatus.initial:
context.read<AuthBloc>().add(CheckAuth());
return Scaffold(
appBar: AppBar(),
body: Center(
child: Container(),
),
);
case AuthStatus.success:
Version? version = thunderBlocState.version;
bool showInAppUpdateNotification = thunderBlocState.showInAppUpdateNotification;

if (version?.hasUpdate == true && hasShownUpdateDialog == false && showInAppUpdateNotification == true) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showUpdateNotification(context, version);
setState(() => hasShownUpdateDialog = true);
});
}
case AuthStatus.success:
Version? version = thunderBlocState.version;
bool showInAppUpdateNotification = thunderBlocState.showInAppUpdateNotification;

if (version?.hasUpdate == true && hasShownUpdateDialog == false && showInAppUpdateNotification == true) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showUpdateNotification(context, version);
setState(() => hasShownUpdateDialog = true);
});
}

return PageView(
controller: pageController,
onPageChanged: (index) => setState(() => selectedPageIndex = index),
physics: const NeverScrollableScrollPhysics(),
children: <Widget>[
Stack(
children: [
FeedPage(useGlobalFeedBloc: true, feedType: FeedType.general, postListingType: thunderBlocState.defaultListingType, sortType: thunderBlocState.defaultSortType),
AnimatedOpacity(
opacity: _isFabOpen ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
child: _isFabOpen
? ModalBarrier(
color: theme.colorScheme.background.withOpacity(0.95),
dismissible: true,
onDismiss: () => context.read<ThunderBloc>().add(const OnFabToggle(false)),
)
: null,
),
],
),
const SearchPage(),
const AccountPage(),
const InboxPage(),
SettingsPage(),
],
);

// Should never hit these, they're handled by the login page
case AuthStatus.failure:
case AuthStatus.loading:
return Container();
case AuthStatus.failureCheckingInstance:
showSnackbar(context, state.errorMessage ?? AppLocalizations.of(context)!.missingErrorMessage);
return ErrorMessage(
title: AppLocalizations.of(context)!.unableToLoadInstance(LemmyClient.instance.lemmyApiV3.host),
message: AppLocalizations.of(context)!.internetOrInstanceIssues,
actionText: AppLocalizations.of(context)!.accountSettings,
action: () => showProfileModalSheet(context),
);
}
},
return PageView(
controller: pageController,
onPageChanged: (index) => setState(() => selectedPageIndex = index),
physics: const NeverScrollableScrollPhysics(),
children: <Widget>[
Stack(
children: [
FeedPage(
useGlobalFeedBloc: true,
feedType: FeedType.general,
postListingType: thunderBlocState.defaultListingType,
sortType: thunderBlocState.defaultSortType,
scaffoldStateKey: scaffoldStateKey,
),
AnimatedOpacity(
opacity: _isFabOpen ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
child: _isFabOpen
? ModalBarrier(
color: theme.colorScheme.background.withOpacity(0.95),
dismissible: true,
onDismiss: () => context.read<ThunderBloc>().add(const OnFabToggle(false)),
)
: null,
),
],
),
const SearchPage(),
const AccountPage(),
const InboxPage(),
SettingsPage(),
],
);

// Should never hit these, they're handled by the login page
case AuthStatus.failure:
case AuthStatus.loading:
return Container();
case AuthStatus.failureCheckingInstance:
showSnackbar(context, state.errorMessage ?? AppLocalizations.of(context)!.missingErrorMessage);
return ErrorMessage(
title: AppLocalizations.of(context)!.unableToLoadInstance(LemmyClient.instance.lemmyApiV3.host),
message: AppLocalizations.of(context)!.internetOrInstanceIssues,
actionText: AppLocalizations.of(context)!.accountSettings,
action: () => showProfileModalSheet(context),
);
}
},
),
),
);
case ThunderStatus.failure:
Expand Down
Loading