diff --git a/lib/src/screens/explore/user_screen.dart b/lib/src/screens/explore/user_screen.dart index 5e3ed17..3d2ba94 100644 --- a/lib/src/screens/explore/user_screen.dart +++ b/lib/src/screens/explore/user_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:interstellar/src/api/feed_source.dart'; import 'package:interstellar/src/api/messages.dart'; import 'package:interstellar/src/api/users.dart' as api_users; @@ -52,116 +53,180 @@ class _UserScreenState extends State { source: FeedSourceUser(widget.userId), title: _data?.username ?? '', details: _data != null - ? Padding( - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - children: [ - if (_data!.avatar?.storageUrl != null) - Padding( - padding: const EdgeInsets.only(right: 12), - child: Avatar(_data!.avatar!.storageUrl, radius: 32), - ), - Expanded( - child: Text( - _data!.username, - style: Theme.of(context).textTheme.titleLarge, - softWrap: true, + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.bottomLeft, + children: [ + if (_data!.cover != null) + Container( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height / 3, ), - ), - OutlinedButton( - style: ButtonStyle( - foregroundColor: _data!.isFollowedByUser == true - ? null - : MaterialStatePropertyAll( - Theme.of(context).disabledColor)), - onPressed: whenLoggedIn(context, () async { - var newValue = await api_users.putFollow( - context.read().httpClient, - context.read().instanceHost, - _data!.userId, - !_data!.isFollowedByUser!); - - setState(() { - _data = newValue; - }); - if (widget.onUpdate != null) { - widget.onUpdate!(newValue); - } - }), - child: Row( - children: [ - const Icon(Icons.group), - Text(' ${intFormat(_data!.followersCount)}'), - ], + child: Image.network( + _data!.cover!.storageUrl, + width: double.infinity, + fit: BoxFit.cover, ), ), - if (!_data!.username.contains('@')) - IconButton( - onPressed: () { - setState(() { - _messageController = TextEditingController(); - }); - }, - icon: const Icon(Icons.mail), - tooltip: 'Send message', - ) - ], - ), - if (_messageController != null) - Column(children: [ - TextEditor( - _messageController!, - isMarkdown: true, - label: 'Message', + Padding( + padding: const EdgeInsets.all(12), + child: Avatar( + _data!.avatar?.storageUrl, + radius: 32, + borderRadius: 4, ), - const SizedBox(height: 8), + ), + ], + ), + Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Row( - mainAxisAlignment: MainAxisAlignment.end, children: [ - OutlinedButton( - onPressed: () async { - setState(() { - _messageController = null; - }); - }, - child: const Text('Cancel'), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _data!.username.contains('@') + ? _data!.username.split('@')[1] + : _data!.username, + style: Theme.of(context).textTheme.titleLarge, + softWrap: true, + ), + InkWell( + onTap: () async { + await Clipboard.setData( + ClipboardData( + text: _data!.username.contains('@') + ? _data!.username + : '@${_data!.username}@${context.read().instanceHost}'), + ); + + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Copied'))); + }, + child: Text( + _data!.username.contains('@') + ? _data!.username + : '@${_data!.username}@${context.read().instanceHost}', + softWrap: true, + ), + ) + ], ), - FilledButton( - onPressed: () async { - final newThread = await postMessage( - context.read().httpClient, - context.read().instanceHost, - _data!.userId, - _messageController!.text, - ); + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: OutlinedButton( + style: ButtonStyle( + foregroundColor: + _data!.isFollowedByUser == true + ? null + : MaterialStatePropertyAll( + Theme.of(context) + .disabledColor)), + onPressed: whenLoggedIn(context, () async { + var newValue = await api_users.putFollow( + context + .read() + .httpClient, + context + .read() + .instanceHost, + _data!.userId, + !_data!.isFollowedByUser!); + setState(() { + _data = newValue; + }); + if (widget.onUpdate != null) { + widget.onUpdate!(newValue); + } + }), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group), + Text( + ' ${intFormat(_data!.followersCount)}'), + ], + ), + ))), + if (!_data!.username.contains('@')) + IconButton( + onPressed: () { + setState(() { + _messageController = TextEditingController(); + }); + }, + icon: const Icon(Icons.mail), + tooltip: 'Send message', + ) + ], + ), + if (_messageController != null) + Column(children: [ + TextEditor( + _messageController!, + isMarkdown: true, + label: 'Message', + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + onPressed: () async { + setState(() { + _messageController = null; + }); + }, + child: const Text('Cancel'), + ), + FilledButton( + onPressed: () async { + final newThread = await postMessage( + context + .read() + .httpClient, + context + .read() + .instanceHost, + _data!.userId, + _messageController!.text, + ); - setState(() { - _messageController = null; + setState(() { + _messageController = null; - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => MessageThreadScreen( - initData: newThread, - ), - ), - ); - }); - }, - child: const Text('Send'), + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + MessageThreadScreen( + initData: newThread, + ), + ), + ); + }); + }, + child: const Text('Send'), + ) + ], ) - ], - ) - ]), - if (_data!.about != null) - Padding( - padding: const EdgeInsets.only(top: 12), - child: Markdown(_data!.about!), - ) - ], - ), + ]), + if (_data!.about != null) + Padding( + padding: const EdgeInsets.only(top: 12), + child: Markdown(_data!.about!), + ) + ], + ), + ) + ], ) : null, ); diff --git a/lib/src/widgets/avatar.dart b/lib/src/widgets/avatar.dart index 8db8338..b412c0f 100644 --- a/lib/src/widgets/avatar.dart +++ b/lib/src/widgets/avatar.dart @@ -1,17 +1,23 @@ import 'package:flutter/material.dart'; class Avatar extends StatelessWidget { - final String url; + final String? url; final double? radius; + final double? borderRadius; - const Avatar(this.url, {super.key, this.radius}); + const Avatar(this.url, {super.key, this.radius, this.borderRadius}); @override Widget build(BuildContext context) { return CircleAvatar( - backgroundColor: Colors.transparent, - backgroundImage: NetworkImage(url), - radius: radius, + radius: radius != null && borderRadius != null ? radius! + borderRadius! : radius, + backgroundColor: radius == null || borderRadius == null ? Colors.transparent : null, + child: CircleAvatar( + backgroundColor: Colors.transparent, + foregroundImage: url != null ? NetworkImage(url!) : null, + backgroundImage: url == null ? const AssetImage('assets/icons/logo.png') : null, + radius: radius, + ) ); } }