import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:provider/provider.dart'; import 'package:solian/models/account.dart'; import 'package:solian/models/friendship.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/utils/service_url.dart'; import 'package:solian/widgets/indent_wrapper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class FriendScreen extends StatefulWidget { const FriendScreen({super.key}); @override State createState() => _FriendScreenState(); } class _FriendScreenState extends State { bool _isSubmitting = false; int _currentSideId = 0; List _friendships = List.empty(); Future fetchFriendships() async { final auth = context.read(); final prof = await auth.getProfiles(); if (!await auth.isAuthorized()) return; _currentSideId = prof['id']; var uri = getRequestUri('passport', '/api/users/me/friends'); var res = await auth.client!.get(uri); if (res.statusCode == 200) { final result = jsonDecode(utf8.decode(res.bodyBytes)) as List; setState(() { _friendships = result.map((x) => Friendship.fromJson(x)).toList(); }); } else { var message = utf8.decode(res.bodyBytes); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Something went wrong... $message")), ); } } Future updateFriendship(Friendship relation, int status) async { setState(() => _isSubmitting = true); final otherside = getOtherside(relation); final auth = context.read(); if (!await auth.isAuthorized()) { setState(() => _isSubmitting = false); return; } var res = await auth.client!.put( getRequestUri('passport', '/api/users/me/friends/${otherside.id}'), headers: { 'Content-Type': 'application/json', }, body: jsonEncode({ 'status': status, }), ); if (res.statusCode == 200) { await fetchFriendships(); } else { var message = utf8.decode(res.bodyBytes); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Something went wrong... $message")), ); } setState(() => _isSubmitting = false); } List filterWithStatus(int status) { return _friendships.where((x) => x.status == status).toList(); } DismissDirection getDismissDirection(Friendship relation) { if (relation.status == 2) return DismissDirection.endToStart; if (relation.status == 1) return DismissDirection.startToEnd; if (relation.status == 0 && relation.relatedId != _currentSideId) return DismissDirection.startToEnd; return DismissDirection.horizontal; } Account getOtherside(Friendship relation) { if (relation.accountId != _currentSideId) { return relation.account; } else { return relation.related; } } String getAvatarUrl(String uuid) { return getRequestUri('passport', '/api/avatar/$uuid').toString(); } @override void initState() { super.initState(); Future.delayed(Duration.zero, () { fetchFriendships(); }); } @override Widget build(BuildContext context) { Widget friendshipTileBuilder(context, index, status) { final element = filterWithStatus(status)[index]; final otherside = getOtherside(element); final randomId = DateTime.now().microsecondsSinceEpoch >> 10; return Dismissible( key: Key(randomId.toString()), background: Container( color: Colors.red, padding: const EdgeInsets.symmetric(horizontal: 20), alignment: Alignment.centerLeft, child: const Icon(Icons.close, color: Colors.white), ), secondaryBackground: Container( color: Colors.green, padding: const EdgeInsets.symmetric(horizontal: 20), alignment: Alignment.centerRight, child: const Icon(Icons.check, color: Colors.white), ), direction: getDismissDirection(element), child: ListTile( title: Text(otherside.nick), subtitle: Text(otherside.name), leading: CircleAvatar( backgroundImage: NetworkImage(getAvatarUrl(otherside.avatar)), ), ), onDismissed: (direction) { if (direction == DismissDirection.startToEnd) { updateFriendship(element, 2); } if (direction == DismissDirection.endToStart) { updateFriendship(element, 1); } }, ); } return IndentWrapper( title: AppLocalizations.of(context)!.friend, child: RefreshIndicator( onRefresh: () => fetchFriendships(), child: CustomScrollView( slivers: [ SliverToBoxAdapter( child: _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(), ), SliverToBoxAdapter( child: Container( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8), child: Text(AppLocalizations.of(context)!.friendPending), ), ), SliverList.builder( itemCount: filterWithStatus(0).length, itemBuilder: (_, __) => friendshipTileBuilder(_, __, 0), ), SliverToBoxAdapter( child: Container( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8), child: Text(AppLocalizations.of(context)!.friendActive), ), ), SliverList.builder( itemCount: filterWithStatus(1).length, itemBuilder: (_, __) => friendshipTileBuilder(_, __, 1), ), SliverToBoxAdapter( child: Container( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8), child: Text(AppLocalizations.of(context)!.friendBlocked), ), ), SliverList.builder( itemCount: filterWithStatus(2).length, itemBuilder: (_, __) => friendshipTileBuilder(_, __, 2), ), SliverToBoxAdapter( child: Container( decoration: BoxDecoration( border: Border( top: BorderSide( color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.8), width: 0.3, )), ), padding: const EdgeInsets.only(top: 16), child: Text( AppLocalizations.of(context)!.friendListHint, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall, ), ), ), ], ), ), ); } }