Skip to content

Commit 1fedabe

Browse files
authored
Added community icon indicator when posting is restricted to mods (#1027)
* added indicator on community header when posting is restricted to mods * updated changelog * added semantic labels to the lock icon * added lock to fab when attempting to post to a locked community * removed lock when logged out * adjusted background for lock in community avatar * conventions
1 parent 15d2ae7 commit 1fedabe

File tree

9 files changed

+115
-73
lines changed

9 files changed

+115
-73
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22
### Added
3+
- Added indicator when community posting is locked to moderators
34
- Added additional font scale option for medium
45
- Added ability to subscribe/unsubscribe to community from long press action on posts
56
- Added option to hide top app bar on scroll

ios/Flutter/AppFrameworkInfo.plist

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
<key>CFBundleVersion</key>
2222
<string>1.0</string>
2323
<key>MinimumOSVersion</key>
24-
<string>11.0</string>
24+
<string>12.0</string>
2525
</dict>
2626
</plist>

ios/Podfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Uncomment this line to define a global platform for your project
2-
# platform :ios, '11.0'
2+
# platform :ios, '12.0'
33

44
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
55
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -100,4 +100,4 @@ post_install do |installer|
100100
end
101101
# End of the permission_handler configuration
102102
end
103-
end
103+
end

ios/Podfile.lock

+14-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PODS:
2-
- background_fetch (1.2.1):
2+
- background_fetch (1.2.2):
33
- Flutter
44
- device_info_plus (0.0.1):
55
- Flutter
@@ -16,9 +16,6 @@ PODS:
1616
- Flutter
1717
- flutter_native_splash (0.0.1):
1818
- Flutter
19-
- FMDB (2.7.5):
20-
- FMDB/standard (= 2.7.5)
21-
- FMDB/standard (2.7.5)
2219
- gal (1.0.0):
2320
- Flutter
2421
- FlutterMacOS
@@ -40,7 +37,7 @@ PODS:
4037
- FlutterMacOS
4138
- sqflite (0.0.3):
4239
- Flutter
43-
- FMDB (>= 2.7.5)
40+
- FlutterMacOS
4441
- uni_links (0.0.1):
4542
- Flutter
4643
- url_launcher_ios (0.0.1):
@@ -66,15 +63,11 @@ DEPENDENCIES:
6663
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
6764
- share_plus (from `.symlinks/plugins/share_plus/ios`)
6865
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
69-
- sqflite (from `.symlinks/plugins/sqflite/ios`)
66+
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
7067
- uni_links (from `.symlinks/plugins/uni_links/ios`)
7168
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
7269
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
7370

74-
SPEC REPOS:
75-
trunk:
76-
- FMDB
77-
7871
EXTERNAL SOURCES:
7972
background_fetch:
8073
:path: ".symlinks/plugins/background_fetch/ios"
@@ -111,7 +104,7 @@ EXTERNAL SOURCES:
111104
shared_preferences_foundation:
112105
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
113106
sqflite:
114-
:path: ".symlinks/plugins/sqflite/ios"
107+
:path: ".symlinks/plugins/sqflite/darwin"
115108
uni_links:
116109
:path: ".symlinks/plugins/uni_links/ios"
117110
url_launcher_ios:
@@ -120,29 +113,28 @@ EXTERNAL SOURCES:
120113
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
121114

122115
SPEC CHECKSUMS:
123-
background_fetch: 896944864b038d2837fc750d470e9841e1e6a363
116+
background_fetch: ec64adecd504f2d0d333b4b11d31f47c8ee23d12
124117
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
125-
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
118+
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
126119
flutter_custom_tabs_ios: 62439c843b2691aae516fd50119a01eb9755fff7
127120
flutter_file_dialog: 4c014a45b105709a27391e266c277d7e588e9299
128121
flutter_icmp_ping: 2b159955eee0c487c766ad83fec224ae35e7c935
129122
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
130-
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
123+
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
131124
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
132-
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
133125
gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
134-
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
126+
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
135127
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
136-
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
128+
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
137129
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
138130
receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
139131
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
140-
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
141-
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
132+
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
133+
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
142134
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
143-
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
144-
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
135+
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
136+
webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36
145137

146-
PODFILE CHECKSUM: 0b10e1f269736e810e149457dba01ee3b062862b
138+
PODFILE CHECKSUM: 8d23d5c4d896af3a5f2a08e0206462ca9882e556
147139

148140
COCOAPODS: 1.14.3

ios/Runner.xcodeproj/project.pbxproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@
569569
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
570570
GCC_WARN_UNUSED_FUNCTION = YES;
571571
GCC_WARN_UNUSED_VARIABLE = YES;
572-
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
572+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
573573
MTL_ENABLE_DEBUG_INFO = NO;
574574
SDKROOT = iphoneos;
575575
SUPPORTED_PLATFORMS = iphoneos;
@@ -704,7 +704,7 @@
704704
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
705705
GCC_WARN_UNUSED_FUNCTION = YES;
706706
GCC_WARN_UNUSED_VARIABLE = YES;
707-
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
707+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
708708
MTL_ENABLE_DEBUG_INFO = YES;
709709
ONLY_ACTIVE_ARCH = YES;
710710
SDKROOT = iphoneos;
@@ -753,7 +753,7 @@
753753
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
754754
GCC_WARN_UNUSED_FUNCTION = YES;
755755
GCC_WARN_UNUSED_VARIABLE = YES;
756-
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
756+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
757757
MTL_ENABLE_DEBUG_INFO = NO;
758758
SDKROOT = iphoneos;
759759
SUPPORTED_PLATFORMS = iphoneos;

lib/community/widgets/community_header.dart

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class _CommunityHeaderState extends State<CommunityHeader> {
9494
CommunityAvatar(
9595
community: widget.getCommunityResponse.communityView.community,
9696
radius: 45.0,
97+
showCommunityStatus: true,
9798
),
9899
const SizedBox(width: 20.0),
99100
Expanded(

lib/feed/widgets/feed_fab.dart

+60-39
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
55

66
import 'package:flutter_bloc/flutter_bloc.dart';
77
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
8+
import 'package:lemmy_api_client/v3.dart';
89
import 'package:swipeable_page_route/swipeable_page_route.dart';
910

1011
import 'package:thunder/account/bloc/account_bloc.dart';
@@ -29,8 +30,11 @@ class FeedFAB extends StatelessWidget {
2930

3031
@override
3132
build(BuildContext context) {
33+
final theme = Theme.of(context);
3234
final ThunderState state = context.watch<ThunderBloc>().state;
3335
final FeedState feedState = context.watch<FeedBloc>().state;
36+
final AuthState authState = context.read<AuthBloc>().state;
37+
final AccountState accountState = context.read<AccountBloc>().state;
3438

3539
// A list of actions that are not supported through the general feed
3640
List<FeedFabAction> unsupportedGeneralFeedFabActions = [];
@@ -46,9 +50,18 @@ class FeedFAB extends StatelessWidget {
4650
// Check to see if we are in the general feeds
4751
bool isGeneralFeed = feedState.status != FeedStatus.initial && feedState.feedType == FeedType.general;
4852
bool isCommunityFeed = feedState.status != FeedStatus.initial && feedState.feedType == FeedType.community;
49-
5053
bool isNavigatedFeed = Navigator.canPop(context);
5154

55+
bool isPostLocked = false;
56+
57+
if (authState.isLoggedIn && isCommunityFeed) {
58+
final CommunityView communityView = feedState.fullCommunityView!.communityView;
59+
60+
if (communityView.community.postingRestrictedToMods && !accountState.moderates.any((CommunityModeratorView cmv) => cmv.community.id == communityView.community.id)) {
61+
isPostLocked = true;
62+
}
63+
}
64+
5265
// Check single-press action
5366
if (isGeneralFeed && unsupportedGeneralFeedFabActions.contains(singlePressAction)) {
5467
singlePressAction = FeedFabAction.openFab; // Default to open fab on unsupported actions
@@ -77,8 +90,9 @@ class FeedFAB extends StatelessWidget {
7790
? GestureFab(
7891
heroTag: heroTag,
7992
distance: 60,
93+
fabBackgroundColor: (singlePressAction == FeedFabAction.newPost && isPostLocked) ? theme.colorScheme.errorContainer : null,
8094
icon: Icon(
81-
singlePressAction.icon,
95+
(singlePressAction == FeedFabAction.newPost && isPostLocked) ? Icons.lock : singlePressAction.icon,
8296
semanticLabel: singlePressAction.title,
8397
size: 35,
8498
),
@@ -105,7 +119,7 @@ class FeedFAB extends StatelessWidget {
105119
triggerScrollToTop(context);
106120
break;
107121
case FeedFabAction.newPost:
108-
triggerNewPost(context);
122+
triggerNewPost(context, isPostingLocked: isPostLocked);
109123
break;
110124
default:
111125
break;
@@ -134,13 +148,13 @@ class FeedFAB extends StatelessWidget {
134148
triggerScrollToTop(context);
135149
break;
136150
case FeedFabAction.newPost:
137-
triggerNewPost(context);
151+
triggerNewPost(context, isPostingLocked: isPostLocked);
138152
break;
139153
default:
140154
break;
141155
}
142156
},
143-
children: getEnabledActions(context),
157+
children: getEnabledActions(context, isPostingLocked: isPostLocked),
144158
)
145159
: Stack(
146160
// This creates an invisible touch target to summon the FAB
@@ -162,7 +176,8 @@ class FeedFAB extends StatelessWidget {
162176
);
163177
}
164178

165-
List<ActionButton> getEnabledActions(BuildContext context) {
179+
List<ActionButton> getEnabledActions(BuildContext context, {bool isPostingLocked = false}) {
180+
final theme = Theme.of(context);
166181
final ThunderState state = context.watch<ThunderBloc>().state;
167182

168183
bool enableBackToTop = state.enableBackToTop;
@@ -221,10 +236,11 @@ class FeedFAB extends StatelessWidget {
221236
if (enableNewPost)
222237
ActionButton(
223238
title: FeedFabAction.newPost.title,
224-
icon: Icon(FeedFabAction.newPost.icon),
239+
icon: Icon(isPostingLocked ? Icons.lock : FeedFabAction.newPost.icon),
240+
backgroundColor: isPostingLocked ? theme.colorScheme.errorContainer : null,
225241
onPressed: () {
226242
HapticFeedback.lightImpact();
227-
triggerNewPost(context);
243+
triggerNewPost(context, isPostingLocked: isPostingLocked);
228244
},
229245
),
230246
];
@@ -263,39 +279,44 @@ class FeedFAB extends StatelessWidget {
263279
context.read<FeedBloc>().add(ScrollToTopEvent());
264280
}
265281

266-
Future<void> triggerNewPost(BuildContext context) async {
267-
FeedBloc feedBloc = context.read<FeedBloc>();
282+
Future<void> triggerNewPost(BuildContext context, {bool isPostingLocked = false}) async {
283+
final l10n = AppLocalizations.of(context)!;
268284

269285
if (!context.read<AuthBloc>().state.isLoggedIn) {
270-
showSnackbar(context, AppLocalizations.of(context)!.mustBeLoggedInPost);
271-
} else {
272-
ThunderBloc thunderBloc = context.read<ThunderBloc>();
273-
AccountBloc accountBloc = context.read<AccountBloc>();
274-
275-
final ThunderState thunderState = context.read<ThunderBloc>().state;
276-
final bool reduceAnimations = thunderState.reduceAnimations;
277-
278-
Navigator.of(context).push(
279-
SwipeablePageRoute(
280-
transitionDuration: reduceAnimations ? const Duration(milliseconds: 100) : null,
281-
canOnlySwipeFromEdge: true,
282-
backGestureDetectionWidth: 45,
283-
builder: (context) {
284-
return MultiBlocProvider(
285-
providers: [
286-
BlocProvider<FeedBloc>.value(value: feedBloc),
287-
BlocProvider<ThunderBloc>.value(value: thunderBloc),
288-
BlocProvider<AccountBloc>.value(value: accountBloc),
289-
],
290-
child: CreatePostPage(
291-
communityId: feedBloc.state.communityId,
292-
communityView: feedBloc.state.fullCommunityView?.communityView,
293-
scaffoldMessengerKey: scaffoldMessengerKey,
294-
),
295-
);
296-
},
297-
),
298-
);
286+
return showSnackbar(context, l10n.mustBeLoggedInPost);
287+
}
288+
289+
if (isPostingLocked) {
290+
return showSnackbar(context, l10n.onlyModsCanPostInCommunity);
299291
}
292+
293+
FeedBloc feedBloc = context.read<FeedBloc>();
294+
ThunderBloc thunderBloc = context.read<ThunderBloc>();
295+
AccountBloc accountBloc = context.read<AccountBloc>();
296+
297+
final ThunderState thunderState = context.read<ThunderBloc>().state;
298+
final bool reduceAnimations = thunderState.reduceAnimations;
299+
300+
Navigator.of(context).push(
301+
SwipeablePageRoute(
302+
transitionDuration: reduceAnimations ? const Duration(milliseconds: 100) : null,
303+
canOnlySwipeFromEdge: true,
304+
backGestureDetectionWidth: 45,
305+
builder: (context) {
306+
return MultiBlocProvider(
307+
providers: [
308+
BlocProvider<FeedBloc>.value(value: feedBloc),
309+
BlocProvider<ThunderBloc>.value(value: thunderBloc),
310+
BlocProvider<AccountBloc>.value(value: accountBloc),
311+
],
312+
child: CreatePostPage(
313+
communityId: feedBloc.state.communityId,
314+
communityView: feedBloc.state.fullCommunityView?.communityView,
315+
scaffoldMessengerKey: scaffoldMessengerKey,
316+
),
317+
);
318+
},
319+
),
320+
);
300321
}
301322
}

lib/shared/avatars/community_avatar.dart

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/material.dart';
22

33
import 'package:lemmy_api_client/v3.dart';
4+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
45
import 'package:cached_network_image/cached_network_image.dart';
56

67
/// A community avatar. Displays the associated community icon if available.
@@ -14,11 +15,15 @@ class CommunityAvatar extends StatelessWidget {
1415
/// The radius of the avatar. Defaults to 12
1516
final double radius;
1617

17-
const CommunityAvatar({super.key, this.community, this.radius = 12.0});
18+
/// Whether to show the community status (locked)
19+
final bool showCommunityStatus;
20+
21+
const CommunityAvatar({super.key, this.community, this.radius = 12.0, this.showCommunityStatus = false});
1822

1923
@override
2024
Widget build(BuildContext context) {
2125
final theme = Theme.of(context);
26+
final l10n = AppLocalizations.of(context)!;
2227

2328
CircleAvatar placeholderIcon = CircleAvatar(
2429
backgroundColor: theme.colorScheme.secondaryContainer,
@@ -39,10 +44,27 @@ class CommunityAvatar extends StatelessWidget {
3944
return CachedNetworkImage(
4045
imageUrl: community!.icon!,
4146
imageBuilder: (context, imageProvider) {
42-
return CircleAvatar(
43-
backgroundColor: Colors.transparent,
44-
foregroundImage: imageProvider,
45-
maxRadius: radius,
47+
return Stack(
48+
children: [
49+
CircleAvatar(
50+
backgroundColor: Colors.transparent,
51+
foregroundImage: imageProvider,
52+
maxRadius: radius,
53+
),
54+
if (community?.postingRestrictedToMods == true && showCommunityStatus)
55+
Positioned(
56+
bottom: -2.0,
57+
right: -2.0,
58+
child: Tooltip(
59+
message: l10n.onlyModsCanPostInCommunity,
60+
child: Container(
61+
padding: const EdgeInsets.all(4.0),
62+
decoration: BoxDecoration(color: theme.colorScheme.surface, shape: BoxShape.circle),
63+
child: Icon(Icons.lock, color: theme.colorScheme.error, size: 18.0, semanticLabel: l10n.onlyModsCanPostInCommunity),
64+
),
65+
),
66+
),
67+
],
4668
);
4769
},
4870
placeholder: (context, url) => placeholderIcon,

0 commit comments

Comments
 (0)