relatica/lib/screens/user_profile_screen.dart

244 wiersze
7.9 KiB
Dart

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../controls/padding.dart';
import '../globals.dart';
import '../models/connection.dart';
import '../models/group_data.dart';
import '../routes.dart';
import '../services/auth_service.dart';
import '../services/connections_manager.dart';
import '../utils/snackbar_builder.dart';
import '../utils/url_opening_utils.dart';
class UserProfileScreen extends StatefulWidget {
final String userId;
const UserProfileScreen({super.key, required this.userId});
@override
State<UserProfileScreen> createState() => _UserProfileScreenState();
}
class _UserProfileScreenState extends State<UserProfileScreen> {
Future<void> openProfileExternal(
BuildContext context,
Connection connection,
) async {
final openInBrowser =
await showYesNoDialog(context, 'Open profile in browser?');
if (openInBrowser == true) {
await openUrlStringInSystembrowser(
context, connection.profileUrl.toString(), 'Post');
}
}
var isUpdating = false;
@override
Widget build(BuildContext context) {
final manager = context.watch<ConnectionsManager>();
final body = manager.getById(widget.userId).fold(onSuccess: (profile) {
final notMyProfile =
getIt<AccountsService>().currentProfile.userId != profile.id;
return RefreshIndicator(
onRefresh: () async {
await manager.fullRefresh(profile);
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CachedNetworkImage(imageUrl: profile.avatarUrl.toString()),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [],
),
Text(
notMyProfile
? '${profile.name} (${profile.handle})'
: '${profile.name} (Your Account)',
softWrap: true,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
const VerticalPadding(),
Text('( ${profile.status.label()} )'),
const VerticalPadding(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (notMyProfile)
buildConnectionStatusToggle(context, profile, manager),
ElevatedButton(
onPressed: () => context.pushNamed(
ScreenPaths.userPosts,
params: {'id': profile.id},
),
child: const Text('Posts')),
ElevatedButton(
onPressed: () async =>
await openProfileExternal(context, profile),
child: const Text('Open In Browser'),
),
],
),
const VerticalPadding(),
HtmlWidget(
profile.note,
onTapUrl: (url) async {
return await openUrlStringInSystembrowser(
context, url, 'link');
},
),
const VerticalPadding(),
Text(
'#Followers: ${profile.followerCount} followers, #Following, ${profile.followingCount}, #Statuses: ${profile.statusesCount}'),
const VerticalPadding(),
Text('Last Status: ${profile.lastStatus}'),
const VerticalPadding(),
if (profile.status == ConnectionStatus.mutual ||
profile.status == ConnectionStatus.youFollowThem)
buildGroups(context, profile, manager),
],
),
),
);
}, onError: (error) {
return Text('Error getting profile: $error');
});
return Scaffold(
appBar: AppBar(
title: Text('Profile'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: body,
),
),
);
}
Widget buildGroups(
BuildContext context,
Connection profile,
ConnectionsManager manager,
) {
final myGroups = manager.getMyGroups();
final usersGroups = manager.getGroupsForUser(profile.id).fold(
onSuccess: (groups) => groups.toSet(),
onError: (error) {
buildSnackbar(context, 'Error getting group data: $error');
return <GroupData>{};
});
myGroups.sort((g1, g2) => g1.name.compareTo(g2.name));
final groupsWidgets = myGroups.map((g) {
return CheckboxListTile(
title: Text(g.name),
value: usersGroups.contains(g),
onChanged: isUpdating
? null
: (bool? value) async {
final isAdding = value == true;
final confirm = await showYesNoDialog(
context,
isAdding
? 'Add user to ${g.name}'
: 'Remove user from ${g.name}');
if (confirm != true) {
return;
}
setState(() {
isUpdating = true;
});
if (isAdding) {
await manager.addUserToGroup(g, profile);
} else {
await manager.removeUserFromGroup(g, profile);
}
setState(() {
isUpdating = false;
});
},
);
}).toList();
return Column(
children: [
Text(
'Groups: ',
style: Theme.of(context).textTheme.titleMedium,
),
const VerticalPadding(),
...groupsWidgets,
],
);
}
Widget buildConnectionStatusToggle(
BuildContext context,
Connection profile,
ConnectionsManager manager,
) {
late Widget followToggleButton;
switch (profile.status) {
case ConnectionStatus.mutual:
case ConnectionStatus.youFollowThem:
followToggleButton = ElevatedButton(
onPressed: isUpdating
? null
: () async {
final confirm = await showYesNoDialog(
context, 'Unfollow ${profile.name}');
if (confirm != true) {
return;
}
setState(() {
isUpdating = true;
});
await manager.unfollow(profile);
setState(() {
isUpdating = false;
});
},
child: const Text('Unfollow'),
);
break;
case ConnectionStatus.theyFollowYou:
case ConnectionStatus.none:
followToggleButton = ElevatedButton(
onPressed: isUpdating
? null
: () async {
final confirm =
await showYesNoDialog(context, 'Follow ${profile.name}');
if (confirm != true) {
return;
}
setState(() {
isUpdating = true;
});
await manager.follow(profile);
setState(() {
isUpdating = false;
});
},
child: const Text('Follow'),
);
break;
case ConnectionStatus.you:
case ConnectionStatus.unknown:
followToggleButton = const SizedBox();
break;
}
return followToggleButton;
}
}