Skip to content

Commit

Permalink
Some updates to profile page (#16)
Browse files Browse the repository at this point in the history
* Adds cover image to profile page.
Adds optional border for avatars.
Updates profile page to show username with and without instance info.
Clicking on username with instance info copies to clipboard.

* Adds default image for avatars

* Restrict user banner's height, add successful copy indicator
  • Loading branch information
olorin99 authored Feb 13, 2024
1 parent 88d83af commit 3e9bc6f
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 105 deletions.
265 changes: 165 additions & 100 deletions lib/src/screens/explore/user_screen.dart
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -52,116 +53,180 @@ class _UserScreenState extends State<UserScreen> {
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<SettingsController>().httpClient,
context.read<SettingsController>().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<SettingsController>().instanceHost}'),
);

if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Copied')));
},
child: Text(
_data!.username.contains('@')
? _data!.username
: '@${_data!.username}@${context.read<SettingsController>().instanceHost}',
softWrap: true,
),
)
],
),
FilledButton(
onPressed: () async {
final newThread = await postMessage(
context.read<SettingsController>().httpClient,
context.read<SettingsController>().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<SettingsController>()
.httpClient,
context
.read<SettingsController>()
.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<SettingsController>()
.httpClient,
context
.read<SettingsController>()
.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,
);
Expand Down
16 changes: 11 additions & 5 deletions lib/src/widgets/avatar.dart
Original file line number Diff line number Diff line change
@@ -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,
)
);
}
}

0 comments on commit 3e9bc6f

Please sign in to comment.