Merge pull request #96 from krawieck/new-lint-rules
This commit is contained in:
commit
c3c6c05f7f
|
@ -3,9 +3,58 @@ include: package:effective_dart/analysis_options.yaml
|
|||
linter:
|
||||
rules:
|
||||
public_member_api_docs: false
|
||||
prefer_expression_function_bodies: true
|
||||
|
||||
prefer_single_quotes: true
|
||||
prefer_final_locals: true
|
||||
prefer_expression_function_bodies: true
|
||||
avoid_bool_literals_in_conditional_expressions: true
|
||||
exhaustive_cases: true
|
||||
prefer_for_elements_to_map_fromIterable: true
|
||||
prefer_if_null_operators: true
|
||||
prefer_is_not_operator: true
|
||||
use_is_even_rather_than_modulo: true
|
||||
unnecessary_string_escapes: true
|
||||
use_full_hex_values_for_flutter_colors: true
|
||||
sort_unnamed_constructors_first: true
|
||||
use_raw_strings: true
|
||||
unnecessary_string_interpolations: true
|
||||
void_checks: true
|
||||
unnecessary_null_in_if_null_operators: true
|
||||
unnecessary_raw_strings: true
|
||||
unnecessary_null_aware_assignments: true
|
||||
unnecessary_parenthesis: true
|
||||
prefer_if_elements_to_conditional_expressions: true
|
||||
unawaited_futures: true
|
||||
prefer_typing_uninitialized_variables: true
|
||||
sized_box_for_whitespace: true
|
||||
recursive_getters: true
|
||||
prefer_int_literals: true
|
||||
prefer_spread_collections: true
|
||||
prefer_null_aware_operators: true
|
||||
prefer_final_in_for_each: true
|
||||
prefer_contains: true
|
||||
prefer_constructors_over_static_methods: true
|
||||
prefer_conditional_assignment: true
|
||||
prefer_asserts_in_initializer_lists: true
|
||||
parameter_assignments: true
|
||||
avoid_unused_constructor_parameters: true
|
||||
empty_catches: true
|
||||
cascade_invocations: true
|
||||
await_only_futures: true
|
||||
avoid_void_async: true
|
||||
avoid_unnecessary_containers: true
|
||||
avoid_single_cascade_in_expression_statements: true
|
||||
avoid_returning_null_for_void: true
|
||||
avoid_redundant_argument_values: true
|
||||
avoid_escaping_inner_quotes: true
|
||||
sort_child_properties_last: true
|
||||
prefer_const_constructors: true
|
||||
prefer_const_declarations: true
|
||||
prefer_const_literals_to_create_immutables: true
|
||||
prefer_const_constructors_in_immutables: true
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- "**/*.g.dart"
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
|
|
|
@ -7,22 +7,20 @@ import 'ref.dart';
|
|||
|
||||
class Debounce {
|
||||
final bool loading;
|
||||
final void Function() callback;
|
||||
|
||||
void call() => callback();
|
||||
|
||||
// void dispose() {}
|
||||
final VoidCallback callback;
|
||||
|
||||
const Debounce({
|
||||
@required this.loading,
|
||||
@required this.callback,
|
||||
});
|
||||
|
||||
void call() => callback();
|
||||
}
|
||||
|
||||
/// will run `callback()` after debounce hook hasn't been called for the
|
||||
/// specified `delayDuration`
|
||||
Debounce useDebounce(
|
||||
Future<Null> Function() callback, [
|
||||
Future<void> Function() callback, [
|
||||
Duration delayDuration = const Duration(seconds: 1),
|
||||
]) {
|
||||
final loading = useState(false);
|
||||
|
|
|
@ -8,8 +8,8 @@ import 'ref.dart';
|
|||
class DelayedLoading {
|
||||
final bool pending;
|
||||
final bool loading;
|
||||
final void Function() start;
|
||||
final void Function() cancel;
|
||||
final VoidCallback start;
|
||||
final VoidCallback cancel;
|
||||
|
||||
const DelayedLoading({
|
||||
@required this.pending,
|
||||
|
|
|
@ -11,14 +11,15 @@ import 'stores.dart';
|
|||
/// Snackbar is rendered. If [any] is set to true, this check is performed for
|
||||
/// all instances and if any of them have an account, the wrapped action will be
|
||||
/// called with a null token.
|
||||
Function(
|
||||
Function(Jwt token) action, [
|
||||
|
||||
VoidCallback Function(
|
||||
void Function(Jwt token) action, [
|
||||
String message,
|
||||
]) useLoggedInAction(String instanceHost, {bool any = false}) {
|
||||
final context = useContext();
|
||||
final store = useAccountsStore();
|
||||
|
||||
return (Function(Jwt token) action, [message]) {
|
||||
return (action, [message]) {
|
||||
if (any && store.hasNoAccount ||
|
||||
!any && store.isAnonymousFor(instanceHost)) {
|
||||
return () {
|
||||
|
|
|
@ -33,12 +33,14 @@ Future<void> main() async {
|
|||
value: accountsStore,
|
||||
),
|
||||
],
|
||||
child: MyApp(),
|
||||
child: const MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyApp extends HookWidget {
|
||||
const MyApp();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final configStore = useConfigStore();
|
||||
|
@ -57,24 +59,26 @@ class MyApp extends HookWidget {
|
|||
theme: ThemeData(
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
home: MyHomePage(),
|
||||
home: const MyHomePage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TemporarySearchTab extends HookWidget {
|
||||
const TemporarySearchTab();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final accStore = useAccountsStore();
|
||||
return ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
const ListTile(
|
||||
title: Center(
|
||||
child: Text('🚧 this tab is still under construction 🚧\n'
|
||||
'but you can open your instances in a browser '
|
||||
' for missing functionality')),
|
||||
),
|
||||
Divider(),
|
||||
const Divider(),
|
||||
for (final inst in accStore.instances)
|
||||
ListTile(
|
||||
title: Text(inst),
|
||||
|
@ -86,7 +90,9 @@ class TemporarySearchTab extends HookWidget {
|
|||
}
|
||||
|
||||
class MyHomePage extends HookWidget {
|
||||
final List<Widget> pages = [
|
||||
const MyHomePage();
|
||||
|
||||
static const List<Widget> pages = [
|
||||
HomeTab(),
|
||||
CommunitiesTab(),
|
||||
TemporarySearchTab(), // TODO: search tab
|
||||
|
@ -126,20 +132,20 @@ class MyHomePage extends HookWidget {
|
|||
index: currentTab.value,
|
||||
children: pages,
|
||||
),
|
||||
floatingActionButton: CreatePostFab(),
|
||||
floatingActionButton: const CreatePostFab(),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||
bottomNavigationBar: BottomAppBar(
|
||||
shape: CircularNotchedRectangle(),
|
||||
shape: const CircularNotchedRectangle(),
|
||||
notchMargin: 7,
|
||||
child: Container(
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
tabButton(Icons.home),
|
||||
tabButton(Icons.list),
|
||||
SizedBox.shrink(),
|
||||
SizedBox.shrink(),
|
||||
const SizedBox.shrink(),
|
||||
const SizedBox.shrink(),
|
||||
tabButton(Icons.search),
|
||||
tabButton(Icons.person),
|
||||
],
|
||||
|
|
|
@ -58,11 +58,11 @@ class AddAccountPage extends HookWidget {
|
|||
title: Text(i),
|
||||
),
|
||||
ListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
leading: const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Icon(Icons.add),
|
||||
),
|
||||
title: Text('Add instance'),
|
||||
title: const Text('Add instance'),
|
||||
onTap: () async {
|
||||
final val = await showCupertinoModalPopup<String>(
|
||||
context: context,
|
||||
|
@ -99,13 +99,13 @@ class AddAccountPage extends HookWidget {
|
|||
return Scaffold(
|
||||
key: scaffoldKey,
|
||||
appBar: AppBar(
|
||||
leading: CloseButton(),
|
||||
leading: const CloseButton(),
|
||||
actionsIconTheme: theme.iconTheme,
|
||||
iconTheme: theme.iconTheme,
|
||||
textTheme: theme.textTheme,
|
||||
brightness: theme.brightness,
|
||||
centerTitle: true,
|
||||
title: Text('Add account'),
|
||||
title: const Text('Add account'),
|
||||
backgroundColor: theme.canvasColor,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
|
@ -114,7 +114,7 @@ class AddAccountPage extends HookWidget {
|
|||
child: ListView(
|
||||
children: [
|
||||
if (icon.value == null)
|
||||
SizedBox(height: 150)
|
||||
const SizedBox(height: 150)
|
||||
else
|
||||
SizedBox(
|
||||
height: 150,
|
||||
|
@ -122,22 +122,22 @@ class AddAccountPage extends HookWidget {
|
|||
url: icon.value,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: icon.value,
|
||||
errorWidget: (_, __, ___) => SizedBox.shrink(),
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: selectInstance,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(selectedInstance.value),
|
||||
Icon(Icons.arrow_drop_down),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
// TODO: add support for password managers
|
||||
TextField(
|
||||
|
@ -146,7 +146,7 @@ class AddAccountPage extends HookWidget {
|
|||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
|
@ -160,7 +160,7 @@ class AddAccountPage extends HookWidget {
|
|||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
|
@ -169,12 +169,18 @@ class AddAccountPage extends HookWidget {
|
|||
),
|
||||
RaisedButton(
|
||||
color: theme.accentColor,
|
||||
padding: EdgeInsets.all(0),
|
||||
padding: const EdgeInsets.all(0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
onPressed: usernameController.text.isEmpty ||
|
||||
passwordController.text.isEmpty
|
||||
? null
|
||||
: loading.pending
|
||||
? () {}
|
||||
: handleOnAdd,
|
||||
child: !loading.loading
|
||||
? Text('Sign in')
|
||||
? const Text('Sign in')
|
||||
: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
|
@ -183,21 +189,15 @@ class AddAccountPage extends HookWidget {
|
|||
AlwaysStoppedAnimation<Color>(theme.canvasColor),
|
||||
),
|
||||
),
|
||||
onPressed: usernameController.text.isEmpty ||
|
||||
passwordController.text.isEmpty
|
||||
? null
|
||||
: loading.pending
|
||||
? () {}
|
||||
: handleOnAdd,
|
||||
),
|
||||
FlatButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text('Register'),
|
||||
onPressed: () {
|
||||
ul.launch('https://${selectedInstance.value}/login');
|
||||
},
|
||||
child: const Text('Register'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -68,10 +68,10 @@ class AddInstancePage extends HookWidget {
|
|||
shadowColor: Colors.transparent,
|
||||
iconTheme: theme.iconTheme,
|
||||
centerTitle: true,
|
||||
leading: CloseButton(),
|
||||
leading: const CloseButton(),
|
||||
actionsIconTheme: theme.iconTheme,
|
||||
textTheme: theme.textTheme,
|
||||
title: Text('Add instance'),
|
||||
title: const Text('Add instance'),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
|
@ -79,26 +79,26 @@ class AddInstancePage extends HookWidget {
|
|||
SizedBox(
|
||||
height: 150,
|
||||
child: FullscreenableImage(
|
||||
url: icon.value,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: icon.value,
|
||||
errorWidget: (_, __, ___) => SizedBox.shrink(),
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
url: icon.value,
|
||||
))
|
||||
else if (isSite.value == false)
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
children: const [
|
||||
Icon(Icons.close, color: Colors.red),
|
||||
Text('instance not found')
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
SizedBox(height: 150),
|
||||
SizedBox(height: 15),
|
||||
const SizedBox(height: 150),
|
||||
const SizedBox(height: 15),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Padding(
|
||||
|
@ -117,7 +117,7 @@ class AddInstancePage extends HookWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
const SizedBox(height: 5),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: SizedBox(
|
||||
|
@ -127,8 +127,9 @@ class AddInstancePage extends HookWidget {
|
|||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
color: theme.accentColor,
|
||||
onPressed: isSite.value == true ? handleOnAdd : null,
|
||||
child: !debounce.loading
|
||||
? Text('Add')
|
||||
? const Text('Add')
|
||||
: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
|
@ -137,7 +138,6 @@ class AddInstancePage extends HookWidget {
|
|||
AlwaysStoppedAnimation<Color>(theme.canvasColor),
|
||||
),
|
||||
),
|
||||
onPressed: isSite.value == true ? handleOnAdd : null,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -35,7 +35,7 @@ class CommunitiesListPage extends StatelessWidget {
|
|||
fetcher: fetcher,
|
||||
builder: (community) => Column(
|
||||
children: [
|
||||
Divider(),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(community.name),
|
||||
subtitle: community.description != null
|
||||
|
@ -61,9 +61,9 @@ class CommunitiesListPage extends StatelessWidget {
|
|||
fit: BoxFit.cover, image: imageProvider),
|
||||
),
|
||||
),
|
||||
errorWidget: (_, __, ___) => SizedBox(width: 50),
|
||||
errorWidget: (_, __, ___) => const SizedBox(width: 50),
|
||||
)
|
||||
: SizedBox(width: 50),
|
||||
: const SizedBox(width: 50),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -16,7 +16,7 @@ import '../util/text_color.dart';
|
|||
|
||||
/// List of subscribed communities per instance
|
||||
class CommunitiesTab extends HookWidget {
|
||||
CommunitiesTab();
|
||||
const CommunitiesTab();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -64,9 +64,9 @@ class CommunitiesTab extends HookWidget {
|
|||
body: Center(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.error),
|
||||
const Icon(Icons.error),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
communitiesSnap.error?.toString() ??
|
||||
instancesSnap.error?.toString(),
|
||||
|
@ -79,7 +79,7 @@ class CommunitiesTab extends HookWidget {
|
|||
} else if (!communitiesSnap.hasData || !instancesSnap.hasData) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Center(
|
||||
body: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
|
@ -92,7 +92,7 @@ class CommunitiesTab extends HookWidget {
|
|||
|
||||
final filterIcon = () {
|
||||
if (filterController.text.isEmpty) {
|
||||
return Icon(Icons.filter_list);
|
||||
return const Icon(Icons.filter_list);
|
||||
}
|
||||
|
||||
return IconButton(
|
||||
|
@ -100,7 +100,7 @@ class CommunitiesTab extends HookWidget {
|
|||
filterController.clear();
|
||||
primaryFocus.unfocus();
|
||||
},
|
||||
icon: Icon(Icons.clear),
|
||||
icon: const Icon(Icons.clear),
|
||||
);
|
||||
}();
|
||||
|
||||
|
@ -121,7 +121,7 @@ class CommunitiesTab extends HookWidget {
|
|||
appBar: AppBar(
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.style),
|
||||
icon: const Icon(Icons.style),
|
||||
onPressed: () {}, // TODO: change styles?
|
||||
),
|
||||
],
|
||||
|
@ -131,14 +131,14 @@ class CommunitiesTab extends HookWidget {
|
|||
decoration: InputDecoration(
|
||||
suffixIcon: filterIcon,
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: 'Filter', // TODO: hint with an filter icon
|
||||
),
|
||||
),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
for (final i in Iterable.generate(amountOfDisplayInstances))
|
||||
for (var i = 0; i < amountOfDisplayInstances; i++)
|
||||
Column(
|
||||
children: [
|
||||
ListTile(
|
||||
|
@ -157,9 +157,10 @@ class CommunitiesTab extends HookWidget {
|
|||
fit: BoxFit.cover, image: imageProvider),
|
||||
),
|
||||
),
|
||||
errorWidget: (_, __, ___) => SizedBox(width: 50),
|
||||
errorWidget: (_, __, ___) =>
|
||||
const SizedBox(width: 50),
|
||||
)
|
||||
: SizedBox(width: 50),
|
||||
: const SizedBox(width: 50),
|
||||
title: Text(
|
||||
instances[i].name,
|
||||
style: theme.textTheme.headline6,
|
||||
|
@ -201,11 +202,11 @@ class CommunitiesTab extends HookWidget {
|
|||
),
|
||||
),
|
||||
errorWidget: (_, __, ___) =>
|
||||
SizedBox(width: 30),
|
||||
const SizedBox(width: 30),
|
||||
)
|
||||
else
|
||||
SizedBox(width: 30),
|
||||
SizedBox(width: 10),
|
||||
const SizedBox(width: 30),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'''!${comm.communityName}${comm.isLocal ? '' : '@${comm.originInstanceHost}'}''',
|
||||
),
|
||||
|
@ -229,7 +230,7 @@ class _CommunitySubscribeToggle extends HookWidget {
|
|||
final int communityId;
|
||||
final String instanceHost;
|
||||
|
||||
_CommunitySubscribeToggle(
|
||||
const _CommunitySubscribeToggle(
|
||||
{@required this.instanceHost, @required this.communityId})
|
||||
: assert(instanceHost != null),
|
||||
assert(communityId != null);
|
||||
|
@ -238,7 +239,7 @@ class _CommunitySubscribeToggle extends HookWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final subbed = useState(true);
|
||||
final delayed = useDelayedLoading(const Duration(milliseconds: 500));
|
||||
final delayed = useDelayedLoading();
|
||||
final accountsStore = useAccountsStore();
|
||||
|
||||
handleTap() async {
|
||||
|
@ -271,7 +272,7 @@ class _CommunitySubscribeToggle extends HookWidget {
|
|||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: delayed.loading
|
||||
? Container(
|
||||
? const SizedBox(
|
||||
width: 20, height: 20, child: CircularProgressIndicator())
|
||||
: Icon(
|
||||
subbed.value ? Icons.done : Icons.add,
|
||||
|
|
|
@ -29,14 +29,14 @@ class CommunityPage extends HookWidget {
|
|||
final String communityName;
|
||||
final int communityId;
|
||||
|
||||
CommunityPage.fromName({
|
||||
const CommunityPage.fromName({
|
||||
@required this.communityName,
|
||||
@required this.instanceHost,
|
||||
}) : assert(communityName != null),
|
||||
assert(instanceHost != null),
|
||||
communityId = null,
|
||||
_community = null;
|
||||
CommunityPage.fromId({
|
||||
const CommunityPage.fromId({
|
||||
@required this.communityId,
|
||||
@required this.instanceHost,
|
||||
}) : assert(communityId != null),
|
||||
|
@ -96,13 +96,13 @@ class CommunityPage extends HookWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (fullCommunitySnap.hasError) ...[
|
||||
Icon(Icons.error),
|
||||
const Icon(Icons.error),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text('ERROR: ${fullCommunitySnap.error}'),
|
||||
)
|
||||
] else
|
||||
CircularProgressIndicator(semanticsLabel: 'loading')
|
||||
const CircularProgressIndicator(semanticsLabel: 'loading')
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -121,16 +121,16 @@ class CommunityPage extends HookWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(Icons.open_in_browser),
|
||||
title: Text('Open in browser'),
|
||||
leading: const Icon(Icons.open_in_browser),
|
||||
title: const Text('Open in browser'),
|
||||
onTap: () async => await ul.canLaunch(community.actorId)
|
||||
? ul.launch(community.actorId)
|
||||
: Scaffold.of(context).showSnackBar(
|
||||
SnackBar(content: Text("can't open in browser"))),
|
||||
const SnackBar(content: Text("can't open in browser"))),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.info_outline),
|
||||
title: Text('Nerd stuff'),
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('Nerd stuff'),
|
||||
onTap: () {
|
||||
showInfoTablePopup(context, {
|
||||
'id': community.id,
|
||||
|
@ -155,7 +155,6 @@ class CommunityPage extends HookWidget {
|
|||
// TODO: change top section to be more flexible
|
||||
SliverAppBar(
|
||||
expandedHeight: 300,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
backgroundColor: theme.cardColor,
|
||||
|
@ -164,7 +163,7 @@ class CommunityPage extends HookWidget {
|
|||
title: Text('!${community.name}',
|
||||
style: TextStyle(color: colorOnCard)),
|
||||
actions: [
|
||||
IconButton(icon: Icon(Icons.share), onPressed: _share),
|
||||
IconButton(icon: const Icon(Icons.share), onPressed: _share),
|
||||
IconButton(icon: Icon(moreIcon), onPressed: _openMoreMenu),
|
||||
],
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
|
@ -177,7 +176,7 @@ class CommunityPage extends HookWidget {
|
|||
TabBar(
|
||||
labelColor: theme.textTheme.bodyText1.color,
|
||||
unselectedLabelColor: Colors.grey,
|
||||
tabs: [
|
||||
tabs: const [
|
||||
Tab(text: 'Posts'),
|
||||
Tab(text: 'Comments'),
|
||||
Tab(text: 'About'),
|
||||
|
@ -227,7 +226,7 @@ class _CommunityOverview extends StatelessWidget {
|
|||
final CommunityView community;
|
||||
final String instanceHost;
|
||||
|
||||
_CommunityOverview(
|
||||
const _CommunityOverview(
|
||||
this.community, {
|
||||
@required this.instanceHost,
|
||||
}) : assert(instanceHost != null),
|
||||
|
@ -253,7 +252,7 @@ class _CommunityOverview extends StatelessWidget {
|
|||
color: Colors.black.withOpacity(0.7), blurRadius: 3)
|
||||
]),
|
||||
),
|
||||
Container(
|
||||
SizedBox(
|
||||
width: 83,
|
||||
height: 83,
|
||||
child: FullscreenableImage(
|
||||
|
@ -269,7 +268,7 @@ class _CommunityOverview extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
errorWidget: (_, __, ___) => Icon(Icons.warning),
|
||||
errorWidget: (_, __, ___) => const Icon(Icons.warning),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -283,48 +282,45 @@ class _CommunityOverview extends StatelessWidget {
|
|||
url: community.banner,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: community.banner,
|
||||
errorWidget: (_, __, ___) => SizedBox.shrink(),
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 45),
|
||||
child: Column(children: [
|
||||
if (community.icon != null)
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 0),
|
||||
child: Center(child: icon),
|
||||
),
|
||||
),
|
||||
if (community.icon != null) icon,
|
||||
// NAME
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: RichText(
|
||||
overflow: TextOverflow.ellipsis, // TODO: fix overflowing
|
||||
text: TextSpan(
|
||||
style: theme.textTheme.subtitle1.copyWith(shadows: [shadow]),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '!',
|
||||
style: TextStyle(fontWeight: FontWeight.w200)),
|
||||
TextSpan(
|
||||
text: community.name,
|
||||
style: TextStyle(fontWeight: FontWeight.w600)),
|
||||
TextSpan(
|
||||
text: '@',
|
||||
style: TextStyle(fontWeight: FontWeight.w200)),
|
||||
TextSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: RichText(
|
||||
overflow: TextOverflow.ellipsis, // TODO: fix overflowing
|
||||
text: TextSpan(
|
||||
style:
|
||||
theme.textTheme.subtitle1.copyWith(shadows: [shadow]),
|
||||
children: [
|
||||
const TextSpan(
|
||||
text: '!',
|
||||
style: TextStyle(fontWeight: FontWeight.w200)),
|
||||
TextSpan(
|
||||
text: community.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600)),
|
||||
const TextSpan(
|
||||
text: '@',
|
||||
style: TextStyle(fontWeight: FontWeight.w200)),
|
||||
TextSpan(
|
||||
text: community.originInstanceHost,
|
||||
style: TextStyle(fontWeight: FontWeight.w600),
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => goToInstance(
|
||||
context, community.originInstanceHost)),
|
||||
],
|
||||
context, community.originInstanceHost),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
// TITLE/MOTTO
|
||||
Center(
|
||||
child: Padding(
|
||||
|
@ -345,21 +341,21 @@ class _CommunityOverview extends StatelessWidget {
|
|||
padding: const EdgeInsets.only(top: 5),
|
||||
child: Row(
|
||||
children: [
|
||||
Spacer(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 3),
|
||||
const Spacer(),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 3),
|
||||
child: Icon(Icons.people, size: 20),
|
||||
),
|
||||
Text(compactNumber(community.numberOfSubscribers)),
|
||||
Spacer(
|
||||
const Spacer(
|
||||
flex: 4,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 3),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 3),
|
||||
child: Icon(Icons.record_voice_over, size: 20),
|
||||
),
|
||||
Text('xx'), // TODO: display online users
|
||||
Spacer(),
|
||||
const Text('xx'), // TODO: display online users
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -375,10 +371,10 @@ class _CommunityOverview extends StatelessWidget {
|
|||
}
|
||||
|
||||
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
_SliverAppBarDelegate(this._tabBar);
|
||||
|
||||
final TabBar _tabBar;
|
||||
|
||||
const _SliverAppBarDelegate(this._tabBar);
|
||||
|
||||
@override
|
||||
double get minExtent => _tabBar.preferredSize.height;
|
||||
@override
|
||||
|
@ -388,7 +384,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
|||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
final theme = Theme.of(context);
|
||||
return Container(child: _tabBar, color: theme.cardColor);
|
||||
return Container(color: theme.cardColor, child: _tabBar);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -418,7 +414,7 @@ class _AboutTab extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
return ListView(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
children: [
|
||||
if (community.description != null) ...[
|
||||
Padding(
|
||||
|
@ -426,7 +422,7 @@ class _AboutTab extends StatelessWidget {
|
|||
child: MarkdownText(community.description,
|
||||
instanceHost: community.instanceHost),
|
||||
),
|
||||
_Divider(),
|
||||
const _Divider(),
|
||||
],
|
||||
SizedBox(
|
||||
height: 25,
|
||||
|
@ -434,8 +430,8 @@ class _AboutTab extends StatelessWidget {
|
|||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
// TODO: consider using Chips
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 7),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 7),
|
||||
child: _Badge('X users online'),
|
||||
),
|
||||
_Badge(
|
||||
|
@ -450,29 +446,29 @@ class _AboutTab extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
_Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 0),
|
||||
child: OutlineButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text('${community.categoryName}'),
|
||||
onPressed: goToCategories,
|
||||
),
|
||||
),
|
||||
_Divider(),
|
||||
const _Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: OutlineButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text('Modlog'),
|
||||
onPressed: goToModlog,
|
||||
onPressed: goToCategories,
|
||||
child: Text(community.categoryName),
|
||||
),
|
||||
),
|
||||
_Divider(),
|
||||
const _Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: OutlineButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
onPressed: goToModlog,
|
||||
child: const Text('Modlog'),
|
||||
),
|
||||
),
|
||||
const _Divider(),
|
||||
if (moderators != null && moderators.isNotEmpty) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
|
@ -493,7 +489,7 @@ class _Badge extends StatelessWidget {
|
|||
final String text;
|
||||
final bool noPad;
|
||||
|
||||
_Badge(this.text, {this.noPad = false});
|
||||
const _Badge(this.text, {this.noPad = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -513,9 +509,11 @@ class _Badge extends StatelessWidget {
|
|||
}
|
||||
|
||||
class _Divider extends StatelessWidget {
|
||||
const _Divider();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
Widget build(BuildContext context) => const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
child: Divider(),
|
||||
);
|
||||
}
|
||||
|
@ -523,14 +521,14 @@ class _Divider extends StatelessWidget {
|
|||
class _FollowButton extends HookWidget {
|
||||
final CommunityView community;
|
||||
|
||||
_FollowButton(this.community);
|
||||
const _FollowButton(this.community);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final isSubbed = useState(community.subscribed ?? false);
|
||||
final delayed = useDelayedLoading(const Duration(milliseconds: 500));
|
||||
final delayed = useDelayedLoading();
|
||||
final loggedInAction = useLoggedInAction(community.instanceHost);
|
||||
|
||||
final colorOnTopOfAccent = textColorBasedOnBackground(theme.accentColor);
|
||||
|
@ -548,8 +546,8 @@ class _FollowButton extends HookWidget {
|
|||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
Icon(Icons.warning),
|
||||
SizedBox(width: 10),
|
||||
const Icon(Icons.warning),
|
||||
const SizedBox(width: 10),
|
||||
Text("couldn't ${isSubbed.value ? 'un' : ''}sub :<"),
|
||||
],
|
||||
),
|
||||
|
@ -566,17 +564,18 @@ class _FollowButton extends HookWidget {
|
|||
child: delayed.loading
|
||||
? RaisedButton(
|
||||
onPressed: null,
|
||||
child: SizedBox(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: const SizedBox(
|
||||
height: 15,
|
||||
width: 15,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
)
|
||||
: RaisedButton.icon(
|
||||
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||
onPressed: loggedInAction(delayed.pending ? (_) {} : subscribe),
|
||||
icon: isSubbed.value
|
||||
? Icon(Icons.remove, size: 18, color: colorOnTopOfAccent)
|
||||
|
|
|
@ -12,19 +12,22 @@ import '../hooks/stores.dart';
|
|||
import '../util/extensions/spaced.dart';
|
||||
import '../util/goto.dart';
|
||||
import '../util/pictrs.dart';
|
||||
import '../util/unawaited.dart';
|
||||
import '../widgets/markdown_text.dart';
|
||||
import 'full_post.dart';
|
||||
|
||||
/// Fab that triggers the [CreatePost] modal
|
||||
class CreatePostFab extends HookWidget {
|
||||
const CreatePostFab();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loggedInAction = useLoggedInAction(null, any: true);
|
||||
|
||||
return FloatingActionButton(
|
||||
child: Icon(Icons.add),
|
||||
onPressed: loggedInAction((_) => showCupertinoModalPopup(
|
||||
context: context, builder: (_) => CreatePost())),
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -89,8 +92,8 @@ class CreatePost extends HookWidget {
|
|||
print(urlController.text);
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
scaffoldKey.currentState
|
||||
.showSnackBar(SnackBar(content: Text('Failed to upload image')));
|
||||
scaffoldKey.currentState.showSnackBar(
|
||||
const SnackBar(content: Text('Failed to upload image')));
|
||||
} finally {
|
||||
imageUploadLoading.value = false;
|
||||
}
|
||||
|
@ -133,7 +136,7 @@ class CreatePost extends HookWidget {
|
|||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
value: selectedCommunity.value?.name,
|
||||
hint: Text('Community'),
|
||||
hint: const Text('Community'),
|
||||
onChanged: (val) => selectedCommunity.value =
|
||||
allCommunitiesSnap.data.firstWhere((e) => e.name == val),
|
||||
items: allCommunitiesSnap.hasData
|
||||
|
@ -143,7 +146,7 @@ class CreatePost extends HookWidget {
|
|||
child: Text(e.name),
|
||||
))
|
||||
.toList()
|
||||
: [
|
||||
: const [
|
||||
DropdownMenuItem(
|
||||
value: '',
|
||||
child: CircularProgressIndicator(),
|
||||
|
@ -158,16 +161,16 @@ class CreatePost extends HookWidget {
|
|||
child: TextField(
|
||||
enabled: pictrsDeleteToken.value == null,
|
||||
controller: urlController,
|
||||
decoration: InputDecoration(
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'URL',
|
||||
suffixIcon: Icon(Icons.link)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
const SizedBox(width: 5),
|
||||
IconButton(
|
||||
icon: imageUploadLoading.value
|
||||
? CircularProgressIndicator()
|
||||
? const CircularProgressIndicator()
|
||||
: Icon(pictrsDeleteToken.value == null
|
||||
? Icons.add_photo_alternate
|
||||
: Icons.close),
|
||||
|
@ -182,8 +185,8 @@ class CreatePost extends HookWidget {
|
|||
controller: titleController,
|
||||
minLines: 1,
|
||||
maxLines: 2,
|
||||
decoration:
|
||||
InputDecoration(border: OutlineInputBorder(), labelText: 'Title'),
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(), labelText: 'Title'),
|
||||
);
|
||||
|
||||
final body = IndexedStack(
|
||||
|
@ -195,8 +198,8 @@ class CreatePost extends HookWidget {
|
|||
maxLines: null,
|
||||
minLines: 5,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
decoration:
|
||||
InputDecoration(border: OutlineInputBorder(), labelText: 'Body'),
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(), labelText: 'Body'),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
@ -210,7 +213,7 @@ class CreatePost extends HookWidget {
|
|||
|
||||
handleSubmit() async {
|
||||
if (selectedCommunity.value == null || titleController.text.isEmpty) {
|
||||
scaffoldKey.currentState.showSnackBar(SnackBar(
|
||||
scaffoldKey.currentState.showSnackBar(const SnackBar(
|
||||
content: Text('Choosing a community and a title is required'),
|
||||
));
|
||||
return;
|
||||
|
@ -229,12 +232,12 @@ class CreatePost extends HookWidget {
|
|||
name: titleController.text,
|
||||
communityId: selectedCommunity.value.id,
|
||||
auth: token.raw);
|
||||
goToReplace(context, (_) => FullPostPage.fromPostView(res));
|
||||
unawaited(goToReplace(context, (_) => FullPostPage.fromPostView(res)));
|
||||
return;
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
scaffoldKey.currentState
|
||||
.showSnackBar(SnackBar(content: Text('Failed to post')));
|
||||
.showSnackBar(const SnackBar(content: Text('Failed to post')));
|
||||
}
|
||||
delayed.cancel();
|
||||
}
|
||||
|
@ -243,7 +246,7 @@ class CreatePost extends HookWidget {
|
|||
key: scaffoldKey,
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
actions: [
|
||||
|
@ -255,7 +258,7 @@ class CreatePost extends HookWidget {
|
|||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(5),
|
||||
padding: const EdgeInsets.all(5),
|
||||
children: [
|
||||
instanceDropdown,
|
||||
communitiesDropdown,
|
||||
|
@ -273,15 +276,15 @@ class CreatePost extends HookWidget {
|
|||
value: nsfw.value,
|
||||
onChanged: (val) => nsfw.value = val,
|
||||
),
|
||||
Text('NSFW')
|
||||
const Text('NSFW')
|
||||
],
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: delayed.pending ? () {} : handleSubmit,
|
||||
child: delayed.loading
|
||||
? CircularProgressIndicator()
|
||||
: Text('post'),
|
||||
? const CircularProgressIndicator()
|
||||
: const Text('post'),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
@ -19,7 +19,7 @@ class FullPostPage extends HookWidget {
|
|||
final String instanceHost;
|
||||
final PostView post;
|
||||
|
||||
FullPostPage({@required this.id, @required this.instanceHost})
|
||||
const FullPostPage({@required this.id, @required this.instanceHost})
|
||||
: assert(id != null),
|
||||
assert(instanceHost != null),
|
||||
post = null;
|
||||
|
@ -77,9 +77,9 @@ class FullPostPage extends HookWidget {
|
|||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: BackButton(),
|
||||
leading: const BackButton(),
|
||||
actions: [
|
||||
IconButton(icon: Icon(Icons.share), onPressed: sharePost),
|
||||
IconButton(icon: const Icon(Icons.share), onPressed: sharePost),
|
||||
SavePostButton(post),
|
||||
IconButton(
|
||||
icon: Icon(moreIcon),
|
||||
|
@ -88,7 +88,7 @@ class FullPostPage extends HookWidget {
|
|||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: loggedInAction((_) => comment()),
|
||||
child: Icon(Icons.comment)),
|
||||
child: const Icon(Icons.comment)),
|
||||
body: ListView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
children: [
|
||||
|
@ -103,15 +103,15 @@ class FullPostPage extends HookWidget {
|
|||
const EdgeInsets.symmetric(horizontal: 10, vertical: 30),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(Icons.error),
|
||||
const Icon(Icons.error),
|
||||
Text('Error: ${fullPostSnap.error}')
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
Container(
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 40),
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
],
|
||||
));
|
||||
|
|
|
@ -20,6 +20,8 @@ import 'inbox.dart';
|
|||
/// First thing users sees when opening the app
|
||||
/// Shows list of posts from all or just specific instances
|
||||
class HomeTab extends HookWidget {
|
||||
const HomeTab();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: needs to be an observer? for accounts changes
|
||||
|
@ -31,15 +33,13 @@ class HomeTab extends HookWidget {
|
|||
final isc = useInfiniteScrollController();
|
||||
final theme = Theme.of(context);
|
||||
final instancesIcons = useMemoFuture(() async {
|
||||
final map = <String, String>{};
|
||||
final instances = accStore.instances.toList(growable: false);
|
||||
final sites = await Future.wait(instances
|
||||
.map((e) => LemmyApi(e).v1.getSite().catchError((e) => null)));
|
||||
for (var i in Iterable.generate(sites.length)) {
|
||||
map[instances[i]] = sites[i].site.icon;
|
||||
}
|
||||
|
||||
return map;
|
||||
return {
|
||||
for (var i = 0; i < sites.length; i++) instances[i]: sites[i].site.icon
|
||||
};
|
||||
});
|
||||
|
||||
handleListChange() async {
|
||||
|
@ -52,8 +52,8 @@ class HomeTab extends HookWidget {
|
|||
return BottomModal(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 5),
|
||||
ListTile(
|
||||
const SizedBox(height: 5),
|
||||
const ListTile(
|
||||
title: Text('EVERYTHING'),
|
||||
dense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
|
@ -62,20 +62,20 @@ class HomeTab extends HookWidget {
|
|||
leading: SizedBox.shrink(),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Subscribed'),
|
||||
leading: SizedBox(width: 20, height: 20),
|
||||
onTap: () => pop(
|
||||
_SelectedList(listingType: PostListingType.subscribed)),
|
||||
title: const Text('Subscribed'),
|
||||
leading: const SizedBox(width: 20, height: 20),
|
||||
onTap: () => pop(const _SelectedList(
|
||||
listingType: PostListingType.subscribed)),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('All'),
|
||||
leading: SizedBox(width: 20, height: 20),
|
||||
onTap: () =>
|
||||
pop(_SelectedList(listingType: PostListingType.all)),
|
||||
title: const Text('All'),
|
||||
leading: const SizedBox(width: 20, height: 20),
|
||||
onTap: () => pop(
|
||||
const _SelectedList(listingType: PostListingType.all)),
|
||||
),
|
||||
for (final instance in accStore.instances) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Divider(),
|
||||
),
|
||||
ListTile(
|
||||
|
@ -88,8 +88,8 @@ class HomeTab extends HookWidget {
|
|||
onTap: () => goToInstance(context, instance),
|
||||
dense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
visualDensity:
|
||||
VisualDensity(vertical: VisualDensity.minimumDensity),
|
||||
visualDensity: const VisualDensity(
|
||||
vertical: VisualDensity.minimumDensity),
|
||||
leading: (instancesIcons.hasData &&
|
||||
instancesIcons.data[instance] != null)
|
||||
? Padding(
|
||||
|
@ -104,7 +104,7 @@ class HomeTab extends HookWidget {
|
|||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(width: 30),
|
||||
: const SizedBox(width: 30),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
|
@ -123,15 +123,15 @@ class HomeTab extends HookWidget {
|
|||
listingType: PostListingType.subscribed,
|
||||
instanceHost: instance,
|
||||
)),
|
||||
leading: SizedBox(width: 20),
|
||||
leading: const SizedBox(width: 20),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('All'),
|
||||
title: const Text('All'),
|
||||
onTap: () => pop(_SelectedList(
|
||||
listingType: PostListingType.all,
|
||||
instanceHost: instance,
|
||||
)),
|
||||
leading: SizedBox(width: 20),
|
||||
leading: const SizedBox(width: 20),
|
||||
),
|
||||
]
|
||||
],
|
||||
|
@ -159,7 +159,7 @@ class HomeTab extends HookWidget {
|
|||
return Scaffold(
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
children: const [
|
||||
Center(child: Text('there needs to be at least one instance')),
|
||||
],
|
||||
),
|
||||
|
@ -171,16 +171,16 @@ class HomeTab extends HookWidget {
|
|||
appBar: AppBar(
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.notifications),
|
||||
onPressed: () => goTo(context, (_) => InboxPage()),
|
||||
icon: const Icon(Icons.notifications),
|
||||
onPressed: () => goTo(context, (_) => const InboxPage()),
|
||||
)
|
||||
],
|
||||
centerTitle: true,
|
||||
title: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
primary: theme.buttonColor,
|
||||
textStyle: theme.primaryTextTheme.headline6,
|
||||
),
|
||||
|
@ -217,7 +217,8 @@ class InfiniteHomeList extends HookWidget {
|
|||
final Function onStyleChange;
|
||||
final InfiniteScrollController controller;
|
||||
final _SelectedList selectedList;
|
||||
InfiniteHomeList({
|
||||
|
||||
const InfiniteHomeList({
|
||||
@required this.selectedList,
|
||||
this.onStyleChange,
|
||||
this.controller,
|
||||
|
@ -265,14 +266,16 @@ class InfiniteHomeList extends HookWidget {
|
|||
));
|
||||
final posts = await Future.wait(futures);
|
||||
final newPosts = <PostView>[];
|
||||
for (final i
|
||||
in Iterable.generate(posts.map((e) => e.length).reduce(max))) {
|
||||
final longest = posts.map((e) => e.length).reduce(max);
|
||||
|
||||
for (var i = 0; i < longest; i++) {
|
||||
for (final el in posts) {
|
||||
if (el.elementAt(i) != null) {
|
||||
newPosts.add(el[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newPosts;
|
||||
}
|
||||
|
||||
|
@ -291,7 +294,6 @@ class InfiniteHomeList extends HookWidget {
|
|||
children: [
|
||||
PostListOptions(
|
||||
onChange: changeSorting,
|
||||
defaultSort: SortType.active,
|
||||
styleButton: onStyleChange != null,
|
||||
),
|
||||
],
|
||||
|
@ -299,7 +301,7 @@ class InfiniteHomeList extends HookWidget {
|
|||
builder: (post) => Column(
|
||||
children: [
|
||||
Post(post),
|
||||
SizedBox(height: 20),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
|
@ -320,7 +322,8 @@ class InfiniteHomeList extends HookWidget {
|
|||
class _SelectedList {
|
||||
final String instanceHost;
|
||||
final PostListingType listingType;
|
||||
_SelectedList({
|
||||
|
||||
const _SelectedList({
|
||||
@required this.listingType,
|
||||
this.instanceHost,
|
||||
});
|
||||
|
|
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
|
||||
class InboxPage extends HookWidget {
|
||||
const InboxPage();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(),
|
||||
|
|
|
@ -52,13 +52,13 @@ class InstancePage extends HookWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (siteSnap.hasError) ...[
|
||||
Icon(Icons.error),
|
||||
const Icon(Icons.error),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text('ERROR: ${siteSnap.error}'),
|
||||
)
|
||||
] else
|
||||
CircularProgressIndicator(semanticsLabel: 'loading')
|
||||
const CircularProgressIndicator(semanticsLabel: 'loading')
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -87,7 +87,6 @@ class InstancePage extends HookWidget {
|
|||
SliverAppBar(
|
||||
brightness: theme.brightness,
|
||||
expandedHeight: 200,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
backgroundColor: theme.cardColor,
|
||||
|
@ -97,7 +96,7 @@ class InstancePage extends HookWidget {
|
|||
style: TextStyle(color: colorOnCard),
|
||||
),
|
||||
actions: [
|
||||
IconButton(icon: Icon(Icons.share), onPressed: _share),
|
||||
IconButton(icon: const Icon(Icons.share), onPressed: _share),
|
||||
IconButton(
|
||||
icon: Icon(moreIcon),
|
||||
onPressed: () => _openMoreMenu(context)),
|
||||
|
@ -109,7 +108,7 @@ class InstancePage extends HookWidget {
|
|||
url: site.site.banner,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: site.site.banner,
|
||||
errorWidget: (_, __, ___) => SizedBox.shrink(),
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
SafeArea(
|
||||
|
@ -125,7 +124,7 @@ class InstancePage extends HookWidget {
|
|||
height: 100,
|
||||
imageUrl: site.site.icon,
|
||||
errorWidget: (_, __, ___) =>
|
||||
Icon(Icons.warning),
|
||||
const Icon(Icons.warning),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -144,7 +143,7 @@ class InstancePage extends HookWidget {
|
|||
TabBar(
|
||||
labelColor: theme.textTheme.bodyText1.color,
|
||||
unselectedLabelColor: Colors.grey,
|
||||
tabs: [
|
||||
tabs: const [
|
||||
Tab(text: 'Posts'),
|
||||
Tab(text: 'Comments'),
|
||||
Tab(text: 'About'),
|
||||
|
@ -187,10 +186,10 @@ class InstancePage extends HookWidget {
|
|||
}
|
||||
|
||||
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
_SliverAppBarDelegate(this._tabBar);
|
||||
|
||||
final TabBar _tabBar;
|
||||
|
||||
const _SliverAppBarDelegate(this._tabBar);
|
||||
|
||||
@override
|
||||
double get minExtent => _tabBar.preferredSize.height;
|
||||
@override
|
||||
|
@ -200,7 +199,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
|||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
final theme = Theme.of(context);
|
||||
return Container(child: _tabBar, color: theme.cardColor);
|
||||
return Container(color: theme.cardColor, child: _tabBar);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -267,23 +266,23 @@ class _AboutTab extends HookWidget {
|
|||
instanceHost: instanceHost,
|
||||
),
|
||||
),
|
||||
_Divider(),
|
||||
const _Divider(),
|
||||
SizedBox(
|
||||
height: 25,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
SizedBox(width: 7),
|
||||
_Badge('X users online'),
|
||||
const SizedBox(width: 7),
|
||||
const _Badge('X users online'),
|
||||
_Badge('${site.site.numberOfUsers} users'),
|
||||
_Badge('${site.site.numberOfCommunities} communities'),
|
||||
_Badge('${site.site.numberOfPosts} posts'),
|
||||
_Badge('${site.site.numberOfComments} comments'),
|
||||
SizedBox(width: 15),
|
||||
const SizedBox(width: 15),
|
||||
],
|
||||
),
|
||||
),
|
||||
_Divider(),
|
||||
const _Divider(),
|
||||
ListTile(
|
||||
title: Center(
|
||||
child: Text(
|
||||
|
@ -303,7 +302,7 @@ class _AboutTab extends HookWidget {
|
|||
width: 50,
|
||||
imageUrl: e.icon,
|
||||
errorWidget: (_, __, ___) =>
|
||||
SizedBox(width: 50, height: 50),
|
||||
const SizedBox(width: 50, height: 50),
|
||||
imageBuilder: (context, imageProvider) => Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
|
@ -313,23 +312,23 @@ class _AboutTab extends HookWidget {
|
|||
),
|
||||
),
|
||||
))
|
||||
: SizedBox(width: 50),
|
||||
: const SizedBox(width: 50),
|
||||
))
|
||||
else if (commSnap.hasError)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text("Can't load communities, ${commSnap.error}"),
|
||||
)
|
||||
else
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
ListTile(
|
||||
title: Center(child: Text('See all')),
|
||||
title: const Center(child: Text('See all')),
|
||||
onTap: goToCommunities,
|
||||
),
|
||||
_Divider(),
|
||||
const _Divider(),
|
||||
ListTile(
|
||||
title: Center(
|
||||
child: Text(
|
||||
|
@ -353,7 +352,7 @@ class _AboutTab extends HookWidget {
|
|||
width: 50,
|
||||
imageUrl: e.avatar,
|
||||
errorWidget: (_, __, ___) =>
|
||||
SizedBox(width: 50, height: 50),
|
||||
const SizedBox(width: 50, height: 50),
|
||||
imageBuilder: (context, imageProvider) => Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
|
@ -361,18 +360,18 @@ class _AboutTab extends HookWidget {
|
|||
fit: BoxFit.cover, image: imageProvider),
|
||||
),
|
||||
))
|
||||
: SizedBox(width: 50),
|
||||
: const SizedBox(width: 50),
|
||||
)),
|
||||
_Divider(),
|
||||
const _Divider(),
|
||||
ListTile(
|
||||
title: Center(child: Text('Banned users')),
|
||||
title: const Center(child: Text('Banned users')),
|
||||
onTap: () => goToBannedUsers(context),
|
||||
),
|
||||
ListTile(
|
||||
title: Center(child: Text('Modlog')),
|
||||
title: const Center(child: Text('Modlog')),
|
||||
onTap: goToModLog,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -383,7 +382,7 @@ class _AboutTab extends HookWidget {
|
|||
class _Badge extends StatelessWidget {
|
||||
final String text;
|
||||
|
||||
_Badge(this.text);
|
||||
const _Badge(this.text);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -403,9 +402,11 @@ class _Badge extends StatelessWidget {
|
|||
}
|
||||
|
||||
class _Divider extends StatelessWidget {
|
||||
const _Divider();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
Widget build(BuildContext context) => const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
child: Divider(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class MediaViewPage extends HookWidget {
|
|||
final isZoomedOut = useState(true);
|
||||
|
||||
notImplemented() {
|
||||
_key.currentState.showSnackBar(SnackBar(
|
||||
_key.currentState.showSnackBar(const SnackBar(
|
||||
content: Text("this feature hasn't been implemented yet 😰")));
|
||||
}
|
||||
|
||||
|
@ -51,16 +51,16 @@ class MediaViewPage extends HookWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(Icons.link),
|
||||
title: Text('Share link'),
|
||||
leading: const Icon(Icons.link),
|
||||
title: const Text('Share link'),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
Share.text('Share image url', url, 'text/plain');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.image),
|
||||
title: Text('Share file'),
|
||||
leading: const Icon(Icons.image),
|
||||
title: const Text('Share file'),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
notImplemented();
|
||||
|
@ -81,15 +81,15 @@ class MediaViewPage extends HookWidget {
|
|||
? AppBar(
|
||||
backgroundColor: Colors.black38,
|
||||
shadowColor: Colors.transparent,
|
||||
leading: CloseButton(),
|
||||
leading: const CloseButton(),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
icon: const Icon(Icons.share),
|
||||
tooltip: 'share',
|
||||
onPressed: share,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.file_download),
|
||||
icon: const Icon(Icons.file_download),
|
||||
tooltip: 'download',
|
||||
onPressed: notImplemented,
|
||||
),
|
||||
|
@ -115,7 +115,7 @@ class MediaViewPage extends HookWidget {
|
|||
imageProvider: CachedNetworkImageProvider(url),
|
||||
heroAttributes: PhotoViewHeroAttributes(tag: url),
|
||||
loadingBuilder: (context, event) =>
|
||||
Center(child: CircularProgressIndicator()),
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ import 'settings.dart';
|
|||
/// Profile page for a logged in user. The difference between this and
|
||||
/// UserPage is that here you have access to settings
|
||||
class UserProfileTab extends HookWidget {
|
||||
UserProfileTab();
|
||||
const UserProfileTab();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -20,9 +20,9 @@ class UserProfileTab extends HookWidget {
|
|||
|
||||
final actions = [
|
||||
IconButton(
|
||||
icon: Icon(Icons.settings),
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
goTo(context, (_) => SettingsPage());
|
||||
goTo(context, (_) => const SettingsPage());
|
||||
},
|
||||
)
|
||||
];
|
||||
|
@ -39,13 +39,13 @@ class UserProfileTab extends HookWidget {
|
|||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('No account was added.'),
|
||||
const Text('No account was added.'),
|
||||
FlatButton.icon(
|
||||
onPressed: () {
|
||||
goTo(context, (_) => AccountsConfigPage());
|
||||
},
|
||||
icon: Icon(Icons.add),
|
||||
label: Text('Add account'),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Add account'),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -62,22 +62,6 @@ class UserProfileTab extends HookWidget {
|
|||
shadowColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: FlatButton(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
// TODO: fix overflow issues
|
||||
'@${accountsStore.defaultUsername}',
|
||||
style: theme.primaryTextTheme.headline6,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
Icon(
|
||||
Icons.expand_more,
|
||||
color: theme.primaryIconTheme.color,
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
|
@ -114,6 +98,22 @@ class UserProfileTab extends HookWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
// TODO: fix overflow issues
|
||||
'@${accountsStore.defaultUsername}',
|
||||
style: theme.primaryTextTheme.headline6,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
Icon(
|
||||
Icons.expand_more,
|
||||
color: theme.primaryIconTheme.color,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: actions,
|
||||
),
|
||||
|
|
|
@ -13,6 +13,8 @@ import 'add_instance.dart';
|
|||
|
||||
/// Page with a list of different settings sections
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
@ -26,26 +28,24 @@ class SettingsPage extends StatelessWidget {
|
|||
title: Text('Settings', style: theme.textTheme.headline6),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Container(
|
||||
child: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(Icons.person),
|
||||
title: Text('Accounts'),
|
||||
onTap: () {
|
||||
goTo(context, (_) => AccountsConfigPage());
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.color_lens),
|
||||
title: Text('Appearance'),
|
||||
onTap: () {
|
||||
goTo(context, (_) => AppearanceConfigPage());
|
||||
},
|
||||
),
|
||||
AboutTile()
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.person),
|
||||
title: const Text('Accounts'),
|
||||
onTap: () {
|
||||
goTo(context, (_) => AccountsConfigPage());
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.color_lens),
|
||||
title: const Text('Appearance'),
|
||||
onTap: () {
|
||||
goTo(context, (_) => const AppearanceConfigPage());
|
||||
},
|
||||
),
|
||||
const AboutTile()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ class SettingsPage extends StatelessWidget {
|
|||
|
||||
/// Settings for theme color, AMOLED switch
|
||||
class AppearanceConfigPage extends HookWidget {
|
||||
const AppearanceConfigPage();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
@ -69,7 +71,7 @@ class AppearanceConfigPage extends HookWidget {
|
|||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
_SectionHeading('Theme'),
|
||||
const _SectionHeading('Theme'),
|
||||
for (final theme in ThemeMode.values)
|
||||
RadioListTile<ThemeMode>(
|
||||
value: theme,
|
||||
|
@ -80,7 +82,7 @@ class AppearanceConfigPage extends HookWidget {
|
|||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text('AMOLED dark mode'),
|
||||
title: const Text('AMOLED dark mode'),
|
||||
value: configStore.amoledDarkMode,
|
||||
onChanged: (checked) {
|
||||
configStore.amoledDarkMode = checked;
|
||||
|
@ -104,16 +106,16 @@ class AccountsConfigPage extends HookWidget {
|
|||
if (await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Remove instance?'),
|
||||
title: const Text('Remove instance?'),
|
||||
content: Text('Are you sure you want to remove $instanceHost?'),
|
||||
actions: [
|
||||
FlatButton(
|
||||
child: Text('no'),
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('no'),
|
||||
),
|
||||
FlatButton(
|
||||
child: Text('yes'),
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('yes'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -127,17 +129,17 @@ class AccountsConfigPage extends HookWidget {
|
|||
if (await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Remove user?'),
|
||||
title: const Text('Remove user?'),
|
||||
content: Text(
|
||||
'Are you sure you want to remove $username@$instanceHost?'),
|
||||
actions: [
|
||||
FlatButton(
|
||||
child: Text('no'),
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('no'),
|
||||
),
|
||||
FlatButton(
|
||||
child: Text('yes'),
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('yes'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -159,14 +161,12 @@ class AccountsConfigPage extends HookWidget {
|
|||
),
|
||||
floatingActionButton: SpeedDial(
|
||||
animatedIcon: AnimatedIcons.menu_close, // TODO: change to + => x
|
||||
closeManually: false,
|
||||
curve: Curves.bounceIn,
|
||||
tooltip: 'Add account or instance',
|
||||
child: Icon(Icons.add),
|
||||
overlayColor: theme.canvasColor,
|
||||
children: [
|
||||
SpeedDialChild(
|
||||
child: Icon(Icons.person_add),
|
||||
child: const Icon(Icons.person_add),
|
||||
label: 'Add account',
|
||||
labelBackgroundColor: theme.canvasColor,
|
||||
onTap: () => showCupertinoModalPopup(
|
||||
|
@ -175,13 +175,14 @@ class AccountsConfigPage extends HookWidget {
|
|||
AddAccountPage(instanceHost: accountsStore.instances.last)),
|
||||
),
|
||||
SpeedDialChild(
|
||||
child: Icon(Icons.dns),
|
||||
child: const Icon(Icons.dns),
|
||||
labelBackgroundColor: theme.canvasColor,
|
||||
label: 'Add instance',
|
||||
onTap: () => showCupertinoModalPopup(
|
||||
context: context, builder: (_) => AddInstancePage()),
|
||||
),
|
||||
],
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
|
@ -199,18 +200,17 @@ class AccountsConfigPage extends HookWidget {
|
|||
context: context,
|
||||
builder: (_) => AddInstancePage(),
|
||||
),
|
||||
icon: Icon(Icons.add),
|
||||
label: Text('Add instance')),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Add instance')),
|
||||
),
|
||||
],
|
||||
),
|
||||
for (final entry in accountsStore.tokens.entries) ...[
|
||||
SizedBox(height: 40),
|
||||
const SizedBox(height: 40),
|
||||
Slidable(
|
||||
actionPane: SlidableBehindActionPane(),
|
||||
actionPane: const SlidableBehindActionPane(),
|
||||
secondaryActions: [
|
||||
IconSlideAction(
|
||||
closeOnTap: true,
|
||||
onTap: () => removeInstanceDialog(entry.key),
|
||||
icon: Icons.delete_sweep,
|
||||
color: Colors.red,
|
||||
|
@ -221,18 +221,17 @@ class AccountsConfigPage extends HookWidget {
|
|||
color: theme.canvasColor,
|
||||
child: ListTile(
|
||||
dense: true,
|
||||
contentPadding: EdgeInsets.only(left: 0, top: 0),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: _SectionHeading(entry.key),
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final username in entry.value.keys) ...[
|
||||
Slidable(
|
||||
actionPane: SlidableBehindActionPane(),
|
||||
actionPane: const SlidableBehindActionPane(),
|
||||
key: Key('$username@${entry.key}'),
|
||||
secondaryActions: [
|
||||
IconSlideAction(
|
||||
closeOnTap: true,
|
||||
onTap: () => removeUserDialog(entry.key, username),
|
||||
icon: Icons.delete_sweep,
|
||||
color: Colors.red,
|
||||
|
@ -259,8 +258,8 @@ class AccountsConfigPage extends HookWidget {
|
|||
],
|
||||
if (entry.value.keys.isEmpty)
|
||||
ListTile(
|
||||
leading: Icon(Icons.add),
|
||||
title: Text('Add account'),
|
||||
leading: const Icon(Icons.add),
|
||||
title: const Text('Add account'),
|
||||
onTap: () {
|
||||
showCupertinoModalPopup(
|
||||
context: context,
|
||||
|
@ -283,9 +282,9 @@ class _SectionHeading extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 20),
|
||||
child: Text(text.toUpperCase(),
|
||||
style: theme.textTheme.subtitle2.copyWith(color: theme.accentColor)),
|
||||
padding: EdgeInsets.only(left: 20, top: 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ class UserPage extends HookWidget {
|
|||
if (userDetailsSnap.hasData) {
|
||||
return UserProfile.fromUserDetails(userDetailsSnap.data);
|
||||
} else if (userDetailsSnap.hasError) {
|
||||
return Center(child: Text('Could not find that user.'));
|
||||
return const Center(child: Text('Could not find that user.'));
|
||||
} else {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
}();
|
||||
|
||||
|
@ -46,11 +46,11 @@ class UserPage extends HookWidget {
|
|||
actions: [
|
||||
if (userDetailsSnap.hasData) ...[
|
||||
IconButton(
|
||||
icon: Icon(Icons.email),
|
||||
icon: const Icon(Icons.email),
|
||||
onPressed: () {}, // TODO: go to messaging page
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
icon: const Icon(Icons.share),
|
||||
onPressed: () => Share.text('Share user',
|
||||
userDetailsSnap.data.user.actorId, 'text/plain'),
|
||||
)
|
||||
|
|
|
@ -52,7 +52,7 @@ class UsersListPage extends StatelessWidget {
|
|||
width: 50,
|
||||
imageUrl: users[i].avatar,
|
||||
errorWidget: (_, __, ___) =>
|
||||
SizedBox(height: 50, width: 50),
|
||||
const SizedBox(height: 50, width: 50),
|
||||
imageBuilder: (context, imageProvider) => Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
|
@ -60,7 +60,7 @@ class UsersListPage extends StatelessWidget {
|
|||
fit: BoxFit.cover, image: imageProvider),
|
||||
),
|
||||
))
|
||||
: SizedBox(width: 50),
|
||||
: const SizedBox(width: 50),
|
||||
),
|
||||
itemCount: users.length,
|
||||
));
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:lemmy_api_client/lemmy_api_client.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../util/unawaited.dart';
|
||||
import 'shared_pref_keys.dart';
|
||||
|
||||
/// Store that manages all accounts
|
||||
|
@ -46,11 +47,13 @@ class AccountsStore extends ChangeNotifier {
|
|||
);
|
||||
|
||||
// set saved settings or create defaults
|
||||
_tokens = nestedMapsCast((json) => Jwt(json['raw']));
|
||||
_tokens = nestedMapsCast((json) => Jwt(json['raw'] as String));
|
||||
_defaultAccount = prefs.getString(SharedPrefKeys.defaultAccount);
|
||||
_defaultAccounts = HashMap.of(Map.castFrom(
|
||||
jsonDecode(prefs.getString(SharedPrefKeys.defaultAccounts) ?? 'null') ??
|
||||
{}));
|
||||
jsonDecode(prefs.getString(SharedPrefKeys.defaultAccounts) ?? 'null')
|
||||
as Map<dynamic, dynamic> ??
|
||||
{},
|
||||
));
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
@ -211,7 +214,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
|
||||
_assignDefaultAccounts();
|
||||
notifyListeners();
|
||||
save();
|
||||
unawaited(save());
|
||||
}
|
||||
|
||||
/// adds a new instance with no accounts associated with it.
|
||||
|
@ -238,7 +241,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
|
||||
_assignDefaultAccounts();
|
||||
notifyListeners();
|
||||
save();
|
||||
unawaited(save());
|
||||
}
|
||||
|
||||
/// This also removes all accounts assigned to this instance
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
/// Strips protocol, 'www.', and trailing '/' from [url] aka. cleans it up
|
||||
String cleanUpUrl(String url) {
|
||||
if (url.startsWith('https://')) {
|
||||
url = url.substring(8);
|
||||
var newUrl = url;
|
||||
|
||||
if (newUrl.startsWith('https://')) {
|
||||
newUrl = newUrl.substring(8);
|
||||
}
|
||||
if (url.startsWith('www.')) {
|
||||
url = url.substring(4);
|
||||
if (newUrl.startsWith('www.')) {
|
||||
newUrl = newUrl.substring(4);
|
||||
}
|
||||
if (url.endsWith('/')) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
if (newUrl.endsWith('/')) {
|
||||
newUrl = newUrl.substring(0, newUrl.length - 1);
|
||||
}
|
||||
|
||||
return url;
|
||||
return newUrl;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
/// Explicitely indicate to the `unawaited_futures` lint
|
||||
/// that the future is not awaited for on purpose
|
||||
void unawaited<T>(Future<T> future) {}
|
|
@ -10,6 +10,8 @@ import 'bottom_modal.dart';
|
|||
/// Title that opens a dialog with information about Lemmur.
|
||||
/// Licenses, changelog, version etc.
|
||||
class AboutTile extends HookWidget {
|
||||
const AboutTile();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final packageInfoSnap = useMemoFuture(PackageInfo.fromPlatform);
|
||||
|
@ -26,11 +28,11 @@ class AboutTile extends HookWidget {
|
|||
|
||||
// TODO: add app icon
|
||||
return AboutListTile(
|
||||
icon: Icon(Icons.info),
|
||||
icon: const Icon(Icons.info),
|
||||
aboutBoxChildren: [
|
||||
FlatButton.icon(
|
||||
icon: Icon(Icons.subject),
|
||||
label: Text('changelog'),
|
||||
icon: const Icon(Icons.subject),
|
||||
label: const Text('changelog'),
|
||||
onPressed: () => showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => BottomModal(
|
||||
|
@ -39,13 +41,13 @@ class AboutTile extends HookWidget {
|
|||
),
|
||||
),
|
||||
FlatButton.icon(
|
||||
icon: Icon(Icons.code),
|
||||
label: Text('source code'),
|
||||
icon: const Icon(Icons.code),
|
||||
label: const Text('source code'),
|
||||
onPressed: () => openInBrowser('https://github.com/krawieck/lemmur'),
|
||||
),
|
||||
FlatButton.icon(
|
||||
icon: Icon(Icons.monetization_on),
|
||||
label: Text('support development'),
|
||||
icon: const Icon(Icons.monetization_on),
|
||||
label: const Text('support development'),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
|
@ -54,14 +56,14 @@ class AboutTile extends HookWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FlatButton(
|
||||
child: Text('Patreon'),
|
||||
onPressed: () =>
|
||||
openInBrowser('https://patreon.com/lemmur'),
|
||||
child: const Text('Patreon'),
|
||||
),
|
||||
FlatButton(
|
||||
child: Text('Buy Me a Coffee'),
|
||||
onPressed: () =>
|
||||
openInBrowser('https://buymeacoff.ee/lemmur'),
|
||||
child: const Text('Buy Me a Coffee'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -5,7 +5,7 @@ class Badge extends StatelessWidget {
|
|||
final Widget child;
|
||||
final BorderRadiusGeometry borderRadius;
|
||||
|
||||
Badge({
|
||||
const Badge({
|
||||
@required this.child,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(10)),
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ class BottomModal extends StatelessWidget {
|
|||
final Widget child;
|
||||
final String title;
|
||||
|
||||
BottomModal({@required this.child, this.title});
|
||||
const BottomModal({@required this.child, this.title});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -13,13 +13,13 @@ class BottomModal extends StatelessWidget {
|
|||
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: title != null ? const EdgeInsets.only(top: 10) : null,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.all(const Radius.circular(10.0)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -34,7 +34,7 @@ class BottomModal extends StatelessWidget {
|
|||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
const Divider(
|
||||
indent: 20,
|
||||
endIndent: 20,
|
||||
)
|
||||
|
|
|
@ -52,8 +52,7 @@ class Comment extends HookWidget {
|
|||
'upvotes': com.upvotes,
|
||||
'downvotes': com.downvotes,
|
||||
'score': com.score,
|
||||
'% of upvotes':
|
||||
'${(100 * (com.upvotes / (com.upvotes + com.downvotes)))}%',
|
||||
'% of upvotes': '${100 * (com.upvotes / (com.upvotes + com.downvotes))}%',
|
||||
'hotRank': com.hotRank,
|
||||
'hotRankActive': com.hotRankActive,
|
||||
'published': com.published,
|
||||
|
@ -92,7 +91,7 @@ class Comment extends HookWidget {
|
|||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed to delete/restore comment')));
|
||||
const SnackBar(content: Text('Failed to delete/restore comment')));
|
||||
return;
|
||||
}
|
||||
delayedDeletion.cancel();
|
||||
|
@ -109,22 +108,22 @@ class Comment extends HookWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(Icons.open_in_browser),
|
||||
title: Text('Open in browser'),
|
||||
leading: const Icon(Icons.open_in_browser),
|
||||
title: const Text('Open in browser'),
|
||||
onTap: () async => await ul.canLaunch(com.apId)
|
||||
? ul.launch(com.apId)
|
||||
: Scaffold.of(context).showSnackBar(
|
||||
SnackBar(content: Text("can't open in browser"))),
|
||||
const SnackBar(content: Text("can't open in browser"))),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.share),
|
||||
title: Text('Share url'),
|
||||
leading: const Icon(Icons.share),
|
||||
title: const Text('Share url'),
|
||||
onTap: () =>
|
||||
Share.text('Share comment url', com.apId, 'text/plain'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.share),
|
||||
title: Text('Share text'),
|
||||
leading: const Icon(Icons.share),
|
||||
title: const Text('Share text'),
|
||||
onTap: () =>
|
||||
Share.text('Share comment text', com.content, 'text/plain'),
|
||||
),
|
||||
|
@ -153,8 +152,8 @@ class Comment extends HookWidget {
|
|||
onTap: loggedInAction(handleDelete),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.info_outline),
|
||||
title: Text('Nerd stuff'),
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('Nerd stuff'),
|
||||
onTap: () => _showCommentInfo(context),
|
||||
),
|
||||
],
|
||||
|
@ -184,7 +183,7 @@ class Comment extends HookWidget {
|
|||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
Scaffold.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('voting failed :(')));
|
||||
.showSnackBar(const SnackBar(content: Text('voting failed :(')));
|
||||
return;
|
||||
}
|
||||
delayedVoting.cancel();
|
||||
|
@ -208,14 +207,14 @@ class Comment extends HookWidget {
|
|||
|
||||
final body = () {
|
||||
if (isDeleted.value) {
|
||||
return Flexible(
|
||||
return const Flexible(
|
||||
child: Text(
|
||||
'comment deleted by creator',
|
||||
style: TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
);
|
||||
} else if (comment.removed) {
|
||||
return Flexible(
|
||||
return const Flexible(
|
||||
child: Text(
|
||||
'comment deleted by moderator',
|
||||
style: TextStyle(fontStyle: FontStyle.italic),
|
||||
|
@ -250,7 +249,7 @@ class Comment extends HookWidget {
|
|||
}();
|
||||
|
||||
final actions = collapsed.value
|
||||
? SizedBox.shrink()
|
||||
? const SizedBox.shrink()
|
||||
: Row(children: [
|
||||
if (selectable.value && !isDeleted.value && !comment.removed)
|
||||
_CommentAction(
|
||||
|
@ -259,10 +258,11 @@ class Comment extends HookWidget {
|
|||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: commentTree.comment.content))
|
||||
.then((_) => Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text('comment copied to clipboard'))));
|
||||
.then((_) => Scaffold.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('comment copied to clipboard'))));
|
||||
}),
|
||||
Spacer(),
|
||||
const Spacer(),
|
||||
_CommentAction(
|
||||
icon: Icons.more_horiz,
|
||||
onPressed: () => _openMoreMenu(context),
|
||||
|
@ -303,6 +303,15 @@ class Comment extends HookWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: EdgeInsets.only(left: indent > 1 ? (indent - 1) * 5.0 : 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: indent > 0
|
||||
? BorderSide(
|
||||
color: colors[indent % colors.length], width: 5)
|
||||
: BorderSide.none,
|
||||
top: const BorderSide(width: 0.2))),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(children: [
|
||||
|
@ -325,27 +334,27 @@ class Comment extends HookWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
errorWidget: (_, __, ___) => SizedBox.shrink(),
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => goToUser.byId(
|
||||
context, comment.instanceHost, comment.creatorId),
|
||||
child: Text(username,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).accentColor,
|
||||
)),
|
||||
onTap: () => goToUser.byId(
|
||||
context, comment.instanceHost, comment.creatorId),
|
||||
),
|
||||
if (isOP) _CommentTag('OP', Theme.of(context).accentColor),
|
||||
if (comment.banned) _CommentTag('BANNED', Colors.red),
|
||||
if (comment.banned) const _CommentTag('BANNED', Colors.red),
|
||||
if (comment.bannedFromCommunity)
|
||||
_CommentTag('BANNED FROM COMMUNITY', Colors.red),
|
||||
Spacer(),
|
||||
const _CommentTag('BANNED FROM COMMUNITY', Colors.red),
|
||||
const Spacer(),
|
||||
if (collapsed.value && commentTree.children.length > 0) ...[
|
||||
_CommentTag('+${commentTree.children.length}',
|
||||
Theme.of(context).accentColor),
|
||||
SizedBox(width: 7),
|
||||
const SizedBox(width: 7),
|
||||
],
|
||||
InkWell(
|
||||
onTap: () => _showCommentInfo(context),
|
||||
|
@ -353,32 +362,23 @@ class Comment extends HookWidget {
|
|||
children: [
|
||||
if (delayedVoting.loading)
|
||||
SizedBox.fromSize(
|
||||
size: Size.square(16),
|
||||
child: CircularProgressIndicator())
|
||||
size: const Size.square(16),
|
||||
child: const CircularProgressIndicator())
|
||||
else
|
||||
Text(compactNumber(comment.score +
|
||||
(wasVoted ? 0 : myVote.value.value))),
|
||||
Text(' · '),
|
||||
const Text(' · '),
|
||||
Text(timeago.format(comment.published)),
|
||||
],
|
||||
),
|
||||
)
|
||||
]),
|
||||
SizedBox(height: 10),
|
||||
const SizedBox(height: 10),
|
||||
Row(children: [body]),
|
||||
SizedBox(height: 5),
|
||||
const SizedBox(height: 5),
|
||||
actions,
|
||||
],
|
||||
),
|
||||
padding: EdgeInsets.all(10),
|
||||
margin: EdgeInsets.only(left: indent > 1 ? (indent - 1) * 5.0 : 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: indent > 0
|
||||
? BorderSide(
|
||||
color: colors[indent % colors.length], width: 5)
|
||||
: BorderSide.none,
|
||||
top: BorderSide(width: 0.2))),
|
||||
),
|
||||
if (!collapsed.value)
|
||||
for (final c in newReplies.value.followedBy(commentTree.children))
|
||||
|
@ -396,13 +396,13 @@ class Comment extends HookWidget {
|
|||
class _SaveComment extends HookWidget {
|
||||
final CommentView comment;
|
||||
|
||||
_SaveComment(this.comment);
|
||||
const _SaveComment(this.comment);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loggedInAction = useLoggedInAction(comment.instanceHost);
|
||||
final isSaved = useState(comment.saved ?? false);
|
||||
final delayed = useDelayedLoading(const Duration(milliseconds: 500));
|
||||
final delayed = useDelayedLoading();
|
||||
|
||||
handleSave(Jwt token) async {
|
||||
final api = LemmyApi(comment.instanceHost).v1;
|
||||
|
@ -415,7 +415,7 @@ class _SaveComment extends HookWidget {
|
|||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
Scaffold.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('saving failed :(')));
|
||||
.showSnackBar(const SnackBar(content: Text('saving failed :(')));
|
||||
}
|
||||
delayed.cancel();
|
||||
}
|
||||
|
@ -440,10 +440,10 @@ class _CommentTag extends StatelessWidget {
|
|||
padding: const EdgeInsets.only(left: 5),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
color: bgColor,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 3, vertical: 2),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 2),
|
||||
child: Text(text,
|
||||
style: TextStyle(
|
||||
color: textColorBasedOnBackground(bgColor),
|
||||
|
@ -456,7 +456,7 @@ class _CommentTag extends StatelessWidget {
|
|||
|
||||
class _CommentAction extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final void Function() onPressed;
|
||||
final VoidCallback onPressed;
|
||||
final String tooltip;
|
||||
final bool loading;
|
||||
final Color iconColor;
|
||||
|
@ -472,10 +472,11 @@ class _CommentAction extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) => IconButton(
|
||||
constraints: BoxConstraints.tight(Size(32, 26)),
|
||||
constraints: BoxConstraints.tight(const Size(32, 26)),
|
||||
icon: loading
|
||||
? SizedBox.fromSize(
|
||||
size: Size.square(22), child: CircularProgressIndicator())
|
||||
size: const Size.square(22),
|
||||
child: const CircularProgressIndicator())
|
||||
: Icon(
|
||||
icon,
|
||||
color: iconColor ??
|
||||
|
@ -485,6 +486,6 @@ class _CommentAction extends StatelessWidget {
|
|||
onPressed: onPressed,
|
||||
iconSize: 22,
|
||||
tooltip: tooltip,
|
||||
padding: EdgeInsets.all(0),
|
||||
padding: const EdgeInsets.all(0),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -60,10 +60,10 @@ class CommentSection extends HookWidget {
|
|||
children: [
|
||||
for (final e in sortPairs.entries)
|
||||
ListTile(
|
||||
leading: Icon(e.value[0]),
|
||||
title: Text(e.value[1]),
|
||||
leading: Icon(e.value[0] as IconData),
|
||||
title: Text(e.value[1] as String),
|
||||
trailing: sorting.value == e.key
|
||||
? Icon(Icons.check)
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -79,18 +79,18 @@ class CommentSection extends HookWidget {
|
|||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(sortPairs[sorting.value][1]),
|
||||
Icon(Icons.arrow_drop_down),
|
||||
Text(sortPairs[sorting.value][1] as String),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
// sorting menu goes here
|
||||
if (comments.isEmpty)
|
||||
Padding(
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 50),
|
||||
child: Text(
|
||||
'no comments yet',
|
||||
|
@ -105,7 +105,7 @@ class CommentSection extends HookWidget {
|
|||
)
|
||||
else
|
||||
for (final com in comments) Comment(com, postCreatorId: postCreatorId),
|
||||
SizedBox(height: 50),
|
||||
const SizedBox(height: 50),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import '../hooks/ref.dart';
|
||||
|
||||
class InfiniteScrollController {
|
||||
Function() clear;
|
||||
VoidCallback clear;
|
||||
|
||||
InfiniteScrollController() {
|
||||
usedBeforeCreation() => throw Exception(
|
||||
|
@ -28,7 +28,7 @@ class InfiniteScroll<T> extends HookWidget {
|
|||
final Widget prepend;
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
InfiniteScroll({
|
||||
const InfiniteScroll({
|
||||
this.batchSize = 10,
|
||||
this.prepend = const SizedBox.shrink(),
|
||||
this.padding,
|
||||
|
@ -75,7 +75,7 @@ class InfiniteScroll<T> extends HookWidget {
|
|||
if (i == data.value.length) {
|
||||
// if there are no more, skip
|
||||
if (!hasMore.current) {
|
||||
return SizedBox.shrink();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// if it's already fetching more, skip
|
||||
|
|
|
@ -12,7 +12,7 @@ class MarkdownText extends StatelessWidget {
|
|||
final String text;
|
||||
final bool selectable;
|
||||
|
||||
MarkdownText(this.text,
|
||||
const MarkdownText(this.text,
|
||||
{@required this.instanceHost, this.selectable = false})
|
||||
: assert(instanceHost != null);
|
||||
|
||||
|
@ -26,7 +26,7 @@ class MarkdownText extends StatelessWidget {
|
|||
.catchError((e) => Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
Icon(Icons.warning),
|
||||
const Icon(Icons.warning),
|
||||
Text("couldn't open link, ${e.toString()}"),
|
||||
],
|
||||
),
|
||||
|
@ -38,7 +38,7 @@ class MarkdownText extends StatelessWidget {
|
|||
imageUrl: uri.toString(),
|
||||
errorWidget: (context, url, error) => Row(
|
||||
children: [
|
||||
Icon(Icons.warning),
|
||||
const Icon(Icons.warning),
|
||||
Text("couldn't load image, ${error.toString()}")
|
||||
],
|
||||
),
|
||||
|
|
|
@ -64,16 +64,16 @@ class Post extends HookWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(Icons.open_in_browser),
|
||||
title: Text('Open in browser'),
|
||||
leading: const Icon(Icons.open_in_browser),
|
||||
title: const Text('Open in browser'),
|
||||
onTap: () async => await ul.canLaunch(post.apId)
|
||||
? ul.launch(post.apId)
|
||||
: Scaffold.of(context).showSnackBar(
|
||||
SnackBar(content: Text("can't open in browser"))),
|
||||
const SnackBar(content: Text("can't open in browser"))),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.info_outline),
|
||||
title: Text('Nerd stuff'),
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('Nerd stuff'),
|
||||
onTap: () {
|
||||
showInfoTablePopup(context, {
|
||||
'id': post.id,
|
||||
|
@ -151,6 +151,7 @@ class Post extends HookWidget {
|
|||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
RichText(
|
||||
|
@ -160,21 +161,23 @@ class Post extends HookWidget {
|
|||
fontSize: 15,
|
||||
color: theme.textTheme.bodyText1.color),
|
||||
children: [
|
||||
TextSpan(
|
||||
const TextSpan(
|
||||
text: '!',
|
||||
style: TextStyle(fontWeight: FontWeight.w300)),
|
||||
TextSpan(
|
||||
text: post.communityName,
|
||||
style: TextStyle(fontWeight: FontWeight.w600),
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.w600),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => goToCommunity.byId(
|
||||
context, instanceHost, post.communityId)),
|
||||
TextSpan(
|
||||
const TextSpan(
|
||||
text: '@',
|
||||
style: TextStyle(fontWeight: FontWeight.w300)),
|
||||
TextSpan(
|
||||
text: post.originInstanceHost,
|
||||
style: TextStyle(fontWeight: FontWeight.w600),
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.w600),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => goToInstance(
|
||||
context, post.originInstanceHost)),
|
||||
|
@ -190,13 +193,14 @@ class Post extends HookWidget {
|
|||
fontSize: 13,
|
||||
color: theme.textTheme.bodyText1.color),
|
||||
children: [
|
||||
TextSpan(
|
||||
const TextSpan(
|
||||
text: 'by',
|
||||
style: TextStyle(fontWeight: FontWeight.w300)),
|
||||
TextSpan(
|
||||
text:
|
||||
''' ${post.creatorPreferredUsername ?? post.creatorName}''',
|
||||
style: TextStyle(fontWeight: FontWeight.w600),
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.w600),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => goToUser.byId(
|
||||
context, post.instanceHost, post.creatorId),
|
||||
|
@ -204,32 +208,32 @@ class Post extends HookWidget {
|
|||
TextSpan(
|
||||
text:
|
||||
''' · ${timeago.format(post.published, locale: 'en_short')}'''),
|
||||
if (post.locked) TextSpan(text: ' · 🔒'),
|
||||
if (post.stickied) TextSpan(text: ' · 📌'),
|
||||
if (post.nsfw) TextSpan(text: ' · '),
|
||||
if (post.locked) const TextSpan(text: ' · 🔒'),
|
||||
if (post.stickied) const TextSpan(text: ' · 📌'),
|
||||
if (post.nsfw) const TextSpan(text: ' · '),
|
||||
if (post.nsfw)
|
||||
TextSpan(
|
||||
const TextSpan(
|
||||
text: 'NSFW',
|
||||
style: TextStyle(color: Colors.red)),
|
||||
if (urlDomain != null)
|
||||
TextSpan(text: ' · $urlDomain'),
|
||||
if (post.removed) TextSpan(text: ' · REMOVED'),
|
||||
if (post.deleted) TextSpan(text: ' · DELETED'),
|
||||
if (post.removed)
|
||||
const TextSpan(text: ' · REMOVED'),
|
||||
if (post.deleted)
|
||||
const TextSpan(text: ' · DELETED'),
|
||||
],
|
||||
))
|
||||
]),
|
||||
],
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
),
|
||||
Spacer(),
|
||||
const Spacer(),
|
||||
if (!fullPost)
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () => showMoreMenu(context, post),
|
||||
icon: Icon(moreIcon),
|
||||
iconSize: 24,
|
||||
padding: EdgeInsets.all(0),
|
||||
padding: const EdgeInsets.all(0),
|
||||
visualDensity: VisualDensity.compact,
|
||||
)
|
||||
],
|
||||
|
@ -246,29 +250,31 @@ class Post extends HookWidget {
|
|||
Expanded(
|
||||
flex: 100,
|
||||
child: Text(
|
||||
'${post.name}',
|
||||
post.name,
|
||||
textAlign: TextAlign.left,
|
||||
softWrap: true,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
|
||||
style: const TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
if (whatType(post.url) == MediaType.other &&
|
||||
post.thumbnailUrl != null) ...[
|
||||
Spacer(),
|
||||
const Spacer(),
|
||||
InkWell(
|
||||
onTap: _openLink,
|
||||
child: Stack(children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: post.thumbnailUrl,
|
||||
width: 70,
|
||||
height: 70,
|
||||
fit: BoxFit.cover,
|
||||
errorWidget: (context, url, error) =>
|
||||
Text(error.toString()),
|
||||
)),
|
||||
Positioned(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: post.thumbnailUrl,
|
||||
width: 70,
|
||||
height: 70,
|
||||
fit: BoxFit.cover,
|
||||
errorWidget: (context, url, error) =>
|
||||
Text(error.toString()),
|
||||
),
|
||||
),
|
||||
const Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
child: Icon(
|
||||
|
@ -294,7 +300,6 @@ class Post extends HookWidget {
|
|||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Theme.of(context).iconTheme.color.withAlpha(170)),
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
child: Padding(
|
||||
|
@ -302,15 +307,15 @@ class Post extends HookWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
Row(children: [
|
||||
Spacer(),
|
||||
const Spacer(),
|
||||
Text('$urlDomain ',
|
||||
style: theme.textTheme.caption
|
||||
.apply(fontStyle: FontStyle.italic)),
|
||||
Icon(Icons.launch, size: 12),
|
||||
const Icon(Icons.launch, size: 12),
|
||||
]),
|
||||
Row(children: [
|
||||
Flexible(
|
||||
child: Text('${post.embedTitle ?? ''}',
|
||||
child: Text(post.embedTitle ?? '',
|
||||
style: theme.textTheme.subtitle1
|
||||
.apply(fontWeightDelta: 2)))
|
||||
]),
|
||||
|
@ -335,7 +340,7 @@ class Post extends HookWidget {
|
|||
url: post.url,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: post.url,
|
||||
errorWidget: (_, __, ___) => Icon(Icons.warning),
|
||||
errorWidget: (_, __, ___) => const Icon(Icons.warning),
|
||||
progressIndicatorBuilder: (context, url, progress) =>
|
||||
CircularProgressIndicator(value: progress.progress),
|
||||
),
|
||||
|
@ -347,7 +352,7 @@ class Post extends HookWidget {
|
|||
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.comment),
|
||||
const Icon(Icons.comment),
|
||||
Expanded(
|
||||
flex: 999,
|
||||
child: Text(
|
||||
|
@ -356,10 +361,10 @@ class Post extends HookWidget {
|
|||
softWrap: false,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
const Spacer(),
|
||||
if (!fullPost)
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
icon: const Icon(Icons.share),
|
||||
onPressed: () => Share.text('Share post url', post.apId,
|
||||
'text/plain')), // TODO: find a way to mark it as url
|
||||
if (!fullPost) SavePostButton(post),
|
||||
|
@ -370,9 +375,9 @@ class Post extends HookWidget {
|
|||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [BoxShadow(blurRadius: 10, color: Colors.black54)],
|
||||
boxShadow: const [BoxShadow(blurRadius: 10, color: Colors.black54)],
|
||||
color: theme.cardColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: fullPost
|
||||
|
@ -412,7 +417,7 @@ class _Voting extends HookWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final myVote = useState(post.myVote ?? VoteType.none);
|
||||
final loading = useDelayedLoading(Duration(milliseconds: 500));
|
||||
final loading = useDelayedLoading();
|
||||
final loggedInAction = useLoggedInAction(post.instanceHost);
|
||||
|
||||
vote(VoteType vote, Jwt token) async {
|
||||
|
@ -426,7 +431,7 @@ class _Voting extends HookWidget {
|
|||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
Scaffold.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('voting failed :(')));
|
||||
.showSnackBar(const SnackBar(content: Text('voting failed :(')));
|
||||
return;
|
||||
}
|
||||
loading.cancel();
|
||||
|
@ -446,7 +451,8 @@ class _Voting extends HookWidget {
|
|||
),
|
||||
)),
|
||||
if (loading.loading)
|
||||
SizedBox(child: CircularProgressIndicator(), width: 20, height: 20)
|
||||
const SizedBox(
|
||||
width: 20, height: 20, child: CircularProgressIndicator())
|
||||
else
|
||||
Text(NumberFormat.compact()
|
||||
.format(post.score + (wasVoted ? 0 : myVote.value.value))),
|
||||
|
|
|
@ -11,7 +11,7 @@ class PostListOptions extends HookWidget {
|
|||
final SortType defaultSort;
|
||||
final bool styleButton;
|
||||
|
||||
PostListOptions({
|
||||
const PostListOptions({
|
||||
@required this.onChange,
|
||||
this.styleButton = true,
|
||||
this.defaultSort = SortType.active,
|
||||
|
@ -60,14 +60,14 @@ class PostListOptions extends HookWidget {
|
|||
children: <Widget>[
|
||||
Text(sort.value.value),
|
||||
const SizedBox(width: 8),
|
||||
Icon(Icons.arrow_drop_down),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
const Spacer(),
|
||||
if (styleButton)
|
||||
IconButton(
|
||||
icon: Icon(Icons.view_stream),
|
||||
icon: const Icon(Icons.view_stream),
|
||||
// TODO: create compact post and dropdown for selecting
|
||||
onPressed: () => print('TBD'),
|
||||
),
|
||||
|
|
|
@ -10,13 +10,13 @@ import '../hooks/logged_in_action.dart';
|
|||
class SavePostButton extends HookWidget {
|
||||
final PostView post;
|
||||
|
||||
SavePostButton(this.post);
|
||||
const SavePostButton(this.post);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isSaved = useState(post.saved ?? false);
|
||||
final savedIcon = isSaved.value ? Icons.bookmark : Icons.bookmark_border;
|
||||
final loading = useDelayedLoading(Duration(milliseconds: 500));
|
||||
final loading = useDelayedLoading();
|
||||
final loggedInAction = useLoggedInAction(post.instanceHost);
|
||||
|
||||
savePost(Jwt token) async {
|
||||
|
@ -30,7 +30,7 @@ class SavePostButton extends HookWidget {
|
|||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
Scaffold.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('saving failed :(')));
|
||||
.showSnackBar(const SnackBar(content: Text('saving failed :(')));
|
||||
}
|
||||
loading.cancel();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class SortableInfiniteList<T> extends HookWidget {
|
|||
final Widget Function(T) builder;
|
||||
final Function onStyleChange;
|
||||
|
||||
SortableInfiniteList({
|
||||
const SortableInfiniteList({
|
||||
@required this.fetcher,
|
||||
@required this.builder,
|
||||
this.onStyleChange,
|
||||
|
@ -36,7 +36,6 @@ class SortableInfiniteList<T> extends HookWidget {
|
|||
return InfiniteScroll<T>(
|
||||
prepend: PostListOptions(
|
||||
onChange: changeSorting,
|
||||
defaultSort: SortType.active,
|
||||
styleButton: onStyleChange != null,
|
||||
),
|
||||
builder: builder,
|
||||
|
@ -52,14 +51,14 @@ class InfinitePostList extends StatelessWidget {
|
|||
final Future<List<PostView>> Function(
|
||||
int page, int batchSize, SortType sortType) fetcher;
|
||||
|
||||
InfinitePostList({@required this.fetcher}) : assert(fetcher != null);
|
||||
const InfinitePostList({@required this.fetcher}) : assert(fetcher != null);
|
||||
|
||||
Widget build(BuildContext context) => SortableInfiniteList<PostView>(
|
||||
onStyleChange: () {},
|
||||
builder: (post) => Column(
|
||||
children: [
|
||||
Post(post),
|
||||
SizedBox(height: 20),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
fetcher: fetcher,
|
||||
|
@ -70,7 +69,7 @@ class InfiniteCommentList extends StatelessWidget {
|
|||
final Future<List<CommentView>> Function(
|
||||
int page, int batchSize, SortType sortType) fetcher;
|
||||
|
||||
InfiniteCommentList({@required this.fetcher}) : assert(fetcher != null);
|
||||
const InfiniteCommentList({@required this.fetcher}) : assert(fetcher != null);
|
||||
|
||||
Widget build(BuildContext context) => SortableInfiniteList<CommentView>(
|
||||
builder: (comment) => Comment(
|
||||
|
|
|
@ -38,7 +38,7 @@ class UserProfile extends HookWidget {
|
|||
} else if (userDetailsSnap.hasError) {
|
||||
return Center(
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
Icon(Icons.error),
|
||||
const Icon(Icons.error),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text('ERROR: ${userDetailsSnap.error}'),
|
||||
|
@ -66,7 +66,7 @@ class UserProfile extends HookWidget {
|
|||
FlexibleSpaceBar(background: _UserOverview(userView)),
|
||||
bottom: TabBar(
|
||||
labelColor: theme.textTheme.bodyText1.color,
|
||||
tabs: [
|
||||
tabs: const [
|
||||
Tab(text: 'Posts'),
|
||||
Tab(text: 'Comments'),
|
||||
Tab(text: 'About'),
|
||||
|
@ -131,7 +131,7 @@ class _UserOverview extends HookWidget {
|
|||
url: userView.banner,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: userView.banner,
|
||||
errorWidget: (_, __, ___) => SizedBox.shrink(),
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
)
|
||||
else
|
||||
|
@ -142,7 +142,7 @@ class _UserOverview extends HookWidget {
|
|||
),
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: FractionalOffset.topCenter,
|
||||
end: FractionalOffset.bottomCenter,
|
||||
|
@ -161,7 +161,7 @@ class _UserOverview extends HookWidget {
|
|||
height: double.infinity,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(40),
|
||||
topLeft: Radius.circular(40),
|
||||
),
|
||||
|
@ -181,19 +181,19 @@ class _UserOverview extends HookWidget {
|
|||
child: Container(
|
||||
// clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(blurRadius: 6, color: Colors.black54)
|
||||
],
|
||||
borderRadius: BorderRadius.all(Radius.circular(15)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||
border: Border.all(color: Colors.white, width: 3),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: FullscreenableImage(
|
||||
url: userView.avatar,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: userView.avatar,
|
||||
errorWidget: (_, __, ___) => SizedBox.shrink(),
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -201,7 +201,7 @@ class _UserOverview extends HookWidget {
|
|||
),
|
||||
Padding(
|
||||
padding: userView.avatar != null
|
||||
? const EdgeInsets.only(top: 8.0)
|
||||
? const EdgeInsets.only(top: 8)
|
||||
: const EdgeInsets.only(top: 70),
|
||||
child: Padding(
|
||||
padding:
|
||||
|
@ -213,7 +213,7 @@ class _UserOverview extends HookWidget {
|
|||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
@ -225,7 +225,7 @@ class _UserOverview extends HookWidget {
|
|||
onTap: () =>
|
||||
goToInstance(context, userView.originInstanceHost),
|
||||
child: Text(
|
||||
'${userView.originInstanceHost}',
|
||||
userView.originInstanceHost,
|
||||
style: theme.textTheme.caption,
|
||||
),
|
||||
)
|
||||
|
@ -246,7 +246,7 @@ class _UserOverview extends HookWidget {
|
|||
color: colorOnTopOfAccentColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
padding: const EdgeInsets.only(left: 4),
|
||||
child: Text(
|
||||
'${compactNumber(userView.numberOfPosts)}'
|
||||
' Post${pluralS(userView.numberOfPosts)}',
|
||||
|
@ -257,7 +257,7 @@ class _UserOverview extends HookWidget {
|
|||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Badge(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -267,7 +267,7 @@ class _UserOverview extends HookWidget {
|
|||
color: colorOnTopOfAccentColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
padding: const EdgeInsets.only(left: 4),
|
||||
child: Text(
|
||||
'${compactNumber(userView.numberOfComments)}'
|
||||
' Comment${pluralS(userView.numberOfComments)}',
|
||||
|
@ -294,12 +294,12 @@ class _UserOverview extends HookWidget {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.cake,
|
||||
size: 13,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
padding: const EdgeInsets.only(left: 4),
|
||||
child: Text(
|
||||
DateFormat('MMM dd, yyyy').format(userView.published),
|
||||
style: theme.textTheme.bodyText1,
|
||||
|
@ -337,7 +337,7 @@ class _AboutTab extends HookWidget {
|
|||
final divider = Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: wallPadding.horizontal / 2, vertical: 10),
|
||||
child: Divider(),
|
||||
child: const Divider(),
|
||||
);
|
||||
|
||||
communityTile(String name, String icon, int id) => ListTile(
|
||||
|
@ -349,7 +349,8 @@ class _AboutTab extends HookWidget {
|
|||
height: 40,
|
||||
width: 40,
|
||||
imageUrl: icon,
|
||||
errorWidget: (_, __, ___) => SizedBox(width: 40, height: 40),
|
||||
errorWidget: (_, __, ___) =>
|
||||
const SizedBox(width: 40, height: 40),
|
||||
imageBuilder: (context, imageProvider) => Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
|
@ -359,17 +360,17 @@ class _AboutTab extends HookWidget {
|
|||
),
|
||||
),
|
||||
))
|
||||
: SizedBox(width: 40),
|
||||
: const SizedBox(width: 40),
|
||||
);
|
||||
|
||||
return ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 20),
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
children: [
|
||||
if (isOwnedAccount)
|
||||
ListTile(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
children: const [
|
||||
Icon(Icons.edit),
|
||||
SizedBox(width: 10),
|
||||
Text('edit profile'),
|
||||
|
@ -415,8 +416,8 @@ class _AboutTab extends HookWidget {
|
|||
communityTile(
|
||||
comm.communityName, comm.communityIcon, comm.communityId)
|
||||
else
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'this user does not subscribe to any community',
|
||||
|
|
|
@ -41,9 +41,9 @@ class WriteComment extends HookWidget {
|
|||
children: [
|
||||
Text(
|
||||
post.name,
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
const SizedBox(height: 4),
|
||||
body,
|
||||
],
|
||||
);
|
||||
|
@ -68,8 +68,8 @@ class WriteComment extends HookWidget {
|
|||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
print(e);
|
||||
scaffoldKey.currentState
|
||||
.showSnackBar(SnackBar(content: Text('Failed to post comment')));
|
||||
scaffoldKey.currentState.showSnackBar(
|
||||
const SnackBar(content: Text('Failed to post comment')));
|
||||
}
|
||||
delayed.cancel();
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class WriteComment extends HookWidget {
|
|||
key: scaffoldKey,
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
actions: [
|
||||
|
@ -98,7 +98,7 @@ class WriteComment extends HookWidget {
|
|||
child: preview,
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
const Divider(),
|
||||
IndexedStack(
|
||||
index: showFancy.value ? 1 : 0,
|
||||
children: [
|
||||
|
@ -108,7 +108,7 @@ class WriteComment extends HookWidget {
|
|||
minLines: 5,
|
||||
maxLines: null,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
decoration: InputDecoration(border: OutlineInputBorder()),
|
||||
decoration: const InputDecoration(border: OutlineInputBorder()),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
@ -125,8 +125,8 @@ class WriteComment extends HookWidget {
|
|||
FlatButton(
|
||||
onPressed: delayed.pending ? () {} : handleSubmit,
|
||||
child: delayed.loading
|
||||
? CircularProgressIndicator()
|
||||
: Text('post'),
|
||||
? const CircularProgressIndicator()
|
||||
: const Text('post'),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue