Skip to content
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
23 changes: 2 additions & 21 deletions lib/community/widgets/community_drawer.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:thunder/feed/utils/community.dart';

import 'package:uuid/uuid.dart';
import 'package:lemmy_api_client/v3.dart';
Expand Down Expand Up @@ -431,27 +432,7 @@ class CommunityItem extends StatelessWidget {
),
showFavoriteAction
? IconButton(
onPressed: () async {
if (isFavorite) {
await Favorite.deleteFavorite(communityId: community.id);
if (context.mounted) context.read<AccountBloc>().add(GetFavoritedCommunities());
return;
}

Account? account = await fetchActiveProfileAccount();

Uuid uuid = const Uuid();
String id = uuid.v4().replaceAll('-', '').substring(0, 13);

Favorite favorite = Favorite(
id: id,
communityId: community.id,
accountId: account!.id,
);

await Favorite.insertFavorite(favorite);
if (context.mounted) context.read<AccountBloc>().add(GetFavoritedCommunities());
},
onPressed: () async => await toggleFavoriteCommunity(context, community, isFavorite),
icon: Icon(
isFavorite ? Icons.star_rounded : Icons.star_border_rounded,
semanticLabel: isFavorite ? l10n.addToFavorites : l10n.removeFromFavorites,
Expand Down
27 changes: 27 additions & 0 deletions lib/feed/utils/community.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:thunder/account/bloc/account_bloc.dart';
import 'package:thunder/account/models/account.dart';
import 'package:thunder/account/models/favourite.dart';
import 'package:thunder/core/auth/helpers/fetch_account.dart';
import 'package:thunder/core/singletons/lemmy_client.dart';
import 'package:uuid/uuid.dart';

/// Logic to block a community
Future<BlockCommunityResponse> blockCommunity(int communityId, bool block) async {
Expand Down Expand Up @@ -48,3 +53,25 @@ Future<GetCommunityResponse> fetchCommunityInformation({int? id, String? name})

return fullCommunityView;
}

Future<void> toggleFavoriteCommunity(BuildContext context, Community community, bool isFavorite) async {
if (isFavorite) {
await Favorite.deleteFavorite(communityId: community.id);
if (context.mounted) context.read<AccountBloc>().add(GetFavoritedCommunities());
return;
}

Account? account = await fetchActiveProfileAccount();

Uuid uuid = const Uuid();
String id = uuid.v4().replaceAll('-', '').substring(0, 13);

Favorite favorite = Favorite(
id: id,
communityId: community.id,
accountId: account!.id,
);

await Favorite.insertFavorite(favorite);
if (context.mounted) context.read<AccountBloc>().add(GetFavoritedCommunities());
}
81 changes: 57 additions & 24 deletions lib/feed/widgets/feed_page_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:thunder/account/bloc/account_bloc.dart';

import 'package:thunder/community/bloc/anonymous_subscriptions_bloc.dart';
import 'package:thunder/community/bloc/community_bloc.dart';
import 'package:thunder/community/enums/community_action.dart';
import 'package:thunder/core/auth/bloc/auth_bloc.dart';
import 'package:thunder/feed/bloc/feed_bloc.dart';
import 'package:thunder/feed/utils/community.dart';
import 'package:thunder/feed/utils/utils.dart';
import 'package:thunder/feed/view/feed_page.dart';
import 'package:thunder/shared/snackbar.dart';
Expand Down Expand Up @@ -52,7 +54,7 @@ class FeedPageAppBar extends StatelessWidget {
},
),
actions: [
if (feedState.feedType == FeedType.community)
if (feedState.feedType == FeedType.community) ...[
BlocListener<CommunityBloc, CommunityState>(
listener: (context, state) {
if (state.status == CommunityStatus.success && state.communityView != null) {
Expand Down Expand Up @@ -82,32 +84,56 @@ class FeedPageAppBar extends StatelessWidget {
},
),
),
],
if (feedState.feedType != FeedType.community)
IconButton(
onPressed: () {
HapticFeedback.mediumImpact();
triggerRefresh(context);
},
icon: Icon(Icons.refresh_rounded, semanticLabel: l10n.refresh)),
IconButton(
onPressed: () {
HapticFeedback.mediumImpact();
triggerRefresh(context);
},
icon: Icon(Icons.refresh_rounded, semanticLabel: l10n.refresh)),
Container(
margin: const EdgeInsets.only(right: 8.0),
child: IconButton(
icon: Icon(Icons.sort, semanticLabel: l10n.sortBy),
onPressed: () {
HapticFeedback.mediumImpact();

showModalBottomSheet<void>(
showDragHandle: true,
context: context,
isScrollControlled: true,
builder: (builderContext) => SortPicker(
title: l10n.sortOptions,
onSelect: (selected) => feedBloc.add(FeedChangeSortTypeEvent(selected.payload)),
previouslySelected: feedBloc.state.sortType,
),
);
icon: Icon(Icons.sort, semanticLabel: l10n.sortBy),
onPressed: () {
HapticFeedback.mediumImpact();

showModalBottomSheet<void>(
showDragHandle: true,
context: context,
isScrollControlled: true,
builder: (builderContext) => SortPicker(
title: l10n.sortOptions,
onSelect: (selected) => feedBloc.add(FeedChangeSortTypeEvent(selected.payload)),
previouslySelected: feedBloc.state.sortType,
),
);
},
),
if (feedState.feedType == FeedType.community)
PopupMenuButton(
onSelected: (value) async {
switch (value) {
case 'refresh':
triggerRefresh(context);
break;
case 'favorite':
final Community community = context.read<FeedBloc>().state.fullCommunityView!.communityView.community;
bool isFavorite = _getFavoriteStatus(context);
await toggleFavoriteCommunity(context, community, isFavorite);
break;
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'refresh',
child: Text(l10n.refresh),
),
PopupMenuItem(
value: 'favorite',
child: Text(_getFavoriteStatus(context) ? l10n.removeFromFavorites : l10n.addToFavorites),
),
],
),
),
],
);
}
Expand Down Expand Up @@ -160,6 +186,13 @@ SubscribedType? _getSubscriptionStatus(BuildContext context) {
return anonymousSubscriptionsBloc.state.ids.contains(feedBloc.state.fullCommunityView?.communityView.community.id) ? SubscribedType.subscribed : SubscribedType.notSubscribed;
}

/// Checks whether the current community is a favorite of the current user
bool _getFavoriteStatus(BuildContext context) {
final AccountState accountState = context.read<AccountBloc>().state;
final FeedBloc feedBloc = context.read<FeedBloc>();
return accountState.favorites.any((communityView) => communityView.community.id == feedBloc.state.fullCommunityView!.communityView.community.id);
}

void _onSubscribeIconPressed(BuildContext context) {
final AuthBloc authBloc = context.read<AuthBloc>();
final FeedBloc feedBloc = context.read<FeedBloc>();
Expand Down
9 changes: 8 additions & 1 deletion lib/search/bloc/search_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,14 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> {

return emit(state.copyWith(
status: SearchStatus.success,
communities: searchResponse.communities,
communities: searchResponse.communities.toList()
..sort(
(a, b) => event.favoriteCommunities?.any((c) => c.community.id == a.community.id) == true
? -1
: event.favoriteCommunities?.any((c) => c.community.id == b.community.id) == true
? 1
: b.counts.subscribers.compareTo(a.counts.subscribers),
),
users: searchResponse.users,
comments: searchResponse.comments,
posts: await parsePostViews(searchResponse.posts),
Expand Down
4 changes: 4 additions & 0 deletions lib/search/bloc/search_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class StartSearchEvent extends SearchEvent {
final SearchType searchType;
final int? communityId;
final int? creatorId;
final List<CommunityView>? favoriteCommunities;

const StartSearchEvent({
required this.query,
Expand All @@ -22,6 +23,7 @@ class StartSearchEvent extends SearchEvent {
required this.searchType,
this.communityId,
this.creatorId,
this.favoriteCommunities,
});
}

Expand All @@ -42,6 +44,7 @@ class ContinueSearchEvent extends SearchEvent {
final SearchType searchType;
final int? communityId;
final int? creatorId;
final List<CommunityView>? favoriteCommunities;

const ContinueSearchEvent({
required this.query,
Expand All @@ -50,6 +53,7 @@ class ContinueSearchEvent extends SearchEvent {
required this.searchType,
this.communityId,
this.creatorId,
this.favoriteCommunities,
});
}

Expand Down
41 changes: 38 additions & 3 deletions lib/search/pages/search_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
int _previousFocusSearchId = 0;
final searchTextFieldFocus = FocusNode();
int? _previousUserId;
int? _previousFavoritesCount;

SearchType _currentSearchType = SearchType.communities;
ListingType _currentFeedType = ListingType.all;
Expand Down Expand Up @@ -123,6 +124,7 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
searchType: _getSearchTypeToUse(),
communityId: _currentCommunityFilter,
creatorId: _currentCreatorFilter,
favoriteCommunities: context.read<AccountBloc>().state.favorites,
));
}
}
Expand Down Expand Up @@ -171,11 +173,13 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi

// When account changes, that means our instance most likely changed, so reset search.
if (state.status == AccountStatus.success &&
((activeProfile?.userId == null && _previousUserId != null) || state.personView?.person.id == activeProfile?.userId && _previousUserId != state.personView?.person.id)) {
((activeProfile?.userId == null && _previousUserId != null) || state.personView?.person.id == activeProfile?.userId && _previousUserId != state.personView?.person.id) ||
state.favorites.length != _previousFavoritesCount) {
_controller.clear();
context.read<SearchBloc>().add(ResetSearch());
if (context.mounted) context.read<SearchBloc>().add(ResetSearch());
setState(() {});
_previousUserId = activeProfile?.userId;
_previousFavoritesCount = state.favorites.length;
}
}),
BlocListener<ThunderBloc, ThunderState>(
Expand Down Expand Up @@ -532,6 +536,26 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (context.read<AccountBloc>().state.favorites.isNotEmpty) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Text(
l10n.favorites,
style: theme.textTheme.titleLarge,
),
),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: context.read<AccountBloc>().state.favorites.length,
itemBuilder: (BuildContext context, int index) {
CommunityView communityView = context.read<AccountBloc>().state.favorites[index];
final Set<int> currentSubscriptions = context.read<AnonymousSubscriptionsBloc>().state.ids;
return _buildCommunityEntry(communityView, isUserLoggedIn, currentSubscriptions, indicateFavorites: false);
},
),
const SizedBox(height: 20),
],
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Text(
Expand All @@ -540,6 +564,7 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
),
),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: state.trendingCommunities!.length,
itemBuilder: (BuildContext context, int index) {
Expand Down Expand Up @@ -690,7 +715,7 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
}
}

Widget _buildCommunityEntry(CommunityView communityView, bool isUserLoggedIn, Set<int> currentSubscriptions) {
Widget _buildCommunityEntry(CommunityView communityView, bool isUserLoggedIn, Set<int> currentSubscriptions, {bool indicateFavorites = true}) {
final AppLocalizations l10n = AppLocalizations.of(context)!;

return Tooltip(
Expand All @@ -716,6 +741,10 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
),
const SizedBox(width: 4),
const Icon(Icons.people_rounded, size: 16.0),
if (indicateFavorites && _getFavoriteStatus(context, communityView.community)) ...const [
Text(' · '),
Icon(Icons.star_rounded, size: 15),
]
]),
trailing: IconButton(
onPressed: () {
Expand Down Expand Up @@ -745,6 +774,11 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
);
}

bool _getFavoriteStatus(BuildContext context, Community community) {
final AccountState accountState = context.read<AccountBloc>().state;
return accountState.favorites.any((communityView) => communityView.community.id == community.id);
}

Widget _buildUserEntry(PersonView personView) {
return Tooltip(
excludeFromSemantics: true,
Expand Down Expand Up @@ -932,6 +966,7 @@ class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMi
searchType: _getSearchTypeToUse(),
communityId: _currentCommunityFilter,
creatorId: _currentCreatorFilter,
favoriteCommunities: context.read<AccountBloc>().state.favorites,
));
}
}
Expand Down
Loading