relatica/lib/screens/user_profile_screen.dart

375 wiersze
13 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../controls/html_text_viewer_control.dart';
import '../controls/login_aware_cached_network_image.dart';
import '../controls/padding.dart';
import '../globals.dart';
import '../models/auth/profile.dart';
import '../models/connection.dart';
import '../models/timeline_grouping_list_data.dart';
import '../riverpod_controllers/account_services.dart';
import '../riverpod_controllers/blocks_services.dart';
import '../riverpod_controllers/circles_repo_services.dart';
import '../riverpod_controllers/connection_manager_services.dart';
import '../routes.dart';
import '../utils/snackbar_builder.dart';
import '../utils/url_opening_utils.dart';
class UserProfileScreen extends ConsumerStatefulWidget {
final String userId;
const UserProfileScreen({super.key, required this.userId});
@override
ConsumerState<UserProfileScreen> createState() => _UserProfileScreenState();
}
class _UserProfileScreenState extends ConsumerState<UserProfileScreen> {
var isUpdating = false;
@override
void initState() {
super.initState();
final profile = ref.read(activeProfileProvider);
ref.read(connectionByIdProvider(profile, widget.userId, forceUpdate: true));
}
@override
Widget build(BuildContext context) {
final profile = ref.watch(activeProfileProvider);
ref.watch(blocksManagerProvider(profile));
final body = ref.watch(connectionByIdProvider(profile, widget.userId)).fold(
onSuccess: (connectionProfile) {
final notMyProfile = profile.userId != connectionProfile.id;
return RefreshIndicator(
onRefresh: () async {
await ref
.read(blocksManagerProvider(profile).notifier)
.updateBlock(connectionProfile);
setState(() {});
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
LoginAwareCachedNetworkImage(
imageUrl: connectionProfile.avatarUrl.toString()),
const Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [],
),
Text(
notMyProfile
? '${connectionProfile.name} (${connectionProfile.handle})'
: '${connectionProfile.name} (Your Account)',
softWrap: true,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
const VerticalPadding(),
if (connectionProfile.status != ConnectionStatus.unknown)
Text('( ${connectionProfile.status.label()} )'),
const VerticalPadding(),
Wrap(
spacing: 10.0,
runSpacing: 10.0,
children: [
ElevatedButton(
onPressed: () => context.pushNamed(
ScreenPaths.userPosts,
pathParameters: {'id': connectionProfile.id},
),
child: const Text('Posts'),
),
ElevatedButton(
onPressed: () => context.pushNamed(
ScreenPaths.userPostsAndComments,
pathParameters: {'id': connectionProfile.id},
),
child: const Text('Posts & Comments'),
),
ElevatedButton(
onPressed: () => context.pushNamed(
ScreenPaths.userMedia,
pathParameters: {'id': connectionProfile.id},
),
child: const Text('Media'),
),
ElevatedButton(
onPressed: () async =>
await openProfileExternal(context, connectionProfile),
child: const Text('Open In Browser'),
),
],
),
const VerticalPadding(),
Wrap(
spacing: 10.0,
runSpacing: 10.0,
children: [
if (notMyProfile) ...[
buildConnectionStatusToggle(
context,
profile,
connectionProfile,
),
buildBlockToggle(context, profile, connectionProfile),
],
],
),
const VerticalPadding(),
HtmlTextViewerControl(
content: connectionProfile.note,
onTapUrl: (url) async {
return await openUrlStringInSystembrowser(
context, url, 'link');
},
),
const VerticalPadding(),
Text(
'#Followers: ${connectionProfile.followerCount} followers, #Following, ${connectionProfile.followingCount}, #Statuses: ${connectionProfile.statusesCount}'),
const VerticalPadding(),
Text('Last Status: ${connectionProfile.lastStatus ?? "Unknown"}'),
const VerticalPadding(),
if (connectionProfile.status == ConnectionStatus.mutual ||
connectionProfile.status == ConnectionStatus.youFollowThem)
buildCircles(context, profile, connectionProfile),
],
),
),
);
}, onError: (error) {
return Text('Error getting profile: $error');
});
return Scaffold(
appBar: AppBar(
title: const Text('Profile'),
leading: context.canPop()
? null
: IconButton(
onPressed: () => context.go(ScreenPaths.timelines),
icon: const Icon(Icons.arrow_back),
),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: body,
),
),
);
}
Widget buildCircles(
BuildContext context,
Profile profile,
Connection connectionProfile,
) {
final myCircles =
ref.watch(timelineGroupingListProvider(profile, GroupingType.circle));
final usersCircles = ref
.watch(circlesProvider(profile).notifier)
.getCirclesForUser(connectionProfile.id)
.fold(
onSuccess: (circles) => circles.toSet(),
onError: (error) {
buildSnackbar(context, 'Error getting circle data: $error');
return <TimelineGroupingListData>{};
});
if (myCircles.isNotEmpty) {
myCircles.sort((g1, g2) => g1.name.compareTo(g2.name));
}
final circlesWidgets = myCircles.map((g) {
return CheckboxListTile(
title: Text(g.name),
value: usersCircles.contains(g),
onChanged: isUpdating
? null
: (bool? value) async {
if (isUpdating) {
return;
}
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 ref
.watch(circlesProvider(profile).notifier)
.addConnectionToCircle(g, connectionProfile);
} else {
await ref
.watch(circlesProvider(profile).notifier)
.removeConnectionFromCircle(g, connectionProfile);
}
if (context.mounted) {
buildSnackbar(context, "User's Circles Updated");
}
setState(() {
isUpdating = false;
});
},
);
}).toList();
return Column(
children: [
Text(
'Circles: ',
style: Theme.of(context).textTheme.titleMedium,
),
const VerticalPadding(),
...circlesWidgets,
],
);
}
Widget buildBlockToggle(
BuildContext context,
Profile profile,
Connection connection,
) {
late Widget blockToggleButton;
switch (connection.status) {
case ConnectionStatus.blocked:
blockToggleButton = ElevatedButton(
onPressed: isUpdating
? null
: () async {
final confirm = await showYesNoDialog(
context, 'Unblock ${connection.name}');
if (confirm != true) {
return;
}
setState(() {
isUpdating = true;
});
await ref
.read(blocksManagerProvider(profile).notifier)
.unblockConnection(connection);
setState(() {
isUpdating = false;
});
},
child: const Text('Unblock'),
);
break;
case ConnectionStatus.mutual:
case ConnectionStatus.theyFollowYou:
case ConnectionStatus.youFollowThem:
case ConnectionStatus.none:
case ConnectionStatus.unknown:
blockToggleButton = ElevatedButton(
onPressed: isUpdating
? null
: () async {
final confirm = await showYesNoDialog(
context, 'Block ${connection.name}');
if (confirm != true) {
return;
}
setState(() {
isUpdating = true;
});
await ref
.read(blocksManagerProvider(profile).notifier)
.blockConnection(connection);
setState(() {
isUpdating = false;
});
},
child: const Text('Block'),
);
break;
case ConnectionStatus.you:
blockToggleButton = const SizedBox();
break;
}
return blockToggleButton;
}
Widget buildConnectionStatusToggle(
BuildContext context,
Profile profile,
Connection connectionProfile,
) {
late Widget followToggleButton;
switch (connectionProfile.status) {
case ConnectionStatus.mutual:
case ConnectionStatus.youFollowThem:
followToggleButton = ElevatedButton(
onPressed: isUpdating
? null
: () async {
final confirm = await showYesNoDialog(
context, 'Unfollow ${connectionProfile.name}');
if (confirm != true) {
return;
}
setState(() {
isUpdating = true;
});
await ref
.read(
connectionModifierProvider(profile, connectionProfile)
.notifier)
.unfollow();
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 ${connectionProfile.name}');
if (confirm != true) {
return;
}
setState(() {
isUpdating = true;
});
await ref
.read(
connectionModifierProvider(profile, connectionProfile)
.notifier)
.follow();
setState(() {
isUpdating = false;
});
},
child: const Text('Follow'),
);
break;
case ConnectionStatus.blocked:
case ConnectionStatus.you:
case ConnectionStatus.unknown:
followToggleButton = const SizedBox();
break;
}
return followToggleButton;
}
}