add profile popovers
This commit is contained in:
parent
7ee165b300
commit
0b9ddbfbc8
11 changed files with 302 additions and 149 deletions
17
lib/controllers/profile_controller.dart
Normal file
17
lib/controllers/profile_controller.dart
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client_controller.dart";
|
||||
import "package:nexus/models/profile.dart";
|
||||
|
||||
class ProfileController extends AsyncNotifier<Profile> {
|
||||
final String userId;
|
||||
ProfileController(this.userId);
|
||||
|
||||
@override
|
||||
Future<Profile> build() {
|
||||
final client = ref.watch(ClientController.provider.notifier);
|
||||
return client.getProfile(userId);
|
||||
}
|
||||
|
||||
static final provider = AsyncNotifierProvider.autoDispose
|
||||
.family<ProfileController, Profile, String>(ProfileController.new);
|
||||
}
|
||||
|
|
@ -7,6 +7,15 @@ extension SchemeToTheme on ColorScheme {
|
|||
titleSpacing: 0,
|
||||
backgroundColor: surfaceContainerLow,
|
||||
),
|
||||
menuTheme: MenuThemeData(
|
||||
style: MenuStyle(
|
||||
backgroundColor: WidgetStatePropertyAll(primaryContainer),
|
||||
),
|
||||
),
|
||||
chipTheme: ChipThemeData(
|
||||
labelStyle: TextStyle(color: onPrimary),
|
||||
color: WidgetStatePropertyAll(primary),
|
||||
),
|
||||
textTheme: ThemeData(
|
||||
fontFamilyFallback: ["sans", "emoji"],
|
||||
brightness: brightness,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ extension ShowContextMenu on BuildContext {
|
|||
|
||||
showMenu(
|
||||
context: this,
|
||||
constraints: BoxConstraints.loose(Size.infinite),
|
||||
position: RelativeRect.fromLTRB(
|
||||
globalPosition.dx,
|
||||
globalPosition.dy,
|
||||
|
|
|
|||
18
lib/helpers/extensions/show_user_popover.dart
Normal file
18
lib/helpers/extensions/show_user_popover.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:nexus/helpers/extensions/show_context_menu.dart";
|
||||
import "package:nexus/models/membership.dart";
|
||||
import "package:nexus/widgets/chat_page/user_popover.dart";
|
||||
|
||||
extension ShowUserPopover on BuildContext {
|
||||
void showUserPopover(Membership member, {required Offset globalPosition}) =>
|
||||
showContextMenu(
|
||||
globalPosition: globalPosition,
|
||||
children: [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: IconTheme(data: IconThemeData(), child: UserPopover(member)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ class AvatarOrHash extends ConsumerWidget {
|
|||
ref.watch(CrossCacheController.provider),
|
||||
headers: ref.headers,
|
||||
),
|
||||
fit: BoxFit.contain,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, _, _) => box,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ class Html extends ConsumerWidget {
|
|||
const Html(this.html, {this.textStyle, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => HtmlWidget(
|
||||
Widget build(BuildContext context, WidgetRef ref) => SelectionArea(
|
||||
child: HtmlWidget(
|
||||
html,
|
||||
textStyle: textStyle,
|
||||
customWidgetBuilder: (element) {
|
||||
|
|
@ -149,5 +150,6 @@ class Html extends ConsumerWidget {
|
|||
},
|
||||
onTapUrl: (url) =>
|
||||
ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(url)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import "package:flutter/widgets.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/author_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
|
||||
class MessageAvatar extends ConsumerWidget {
|
||||
|
|
@ -14,11 +15,17 @@ class MessageAvatar extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) => ref
|
||||
.watch(AuthorController.provider(message))
|
||||
.betterWhen(
|
||||
data: (membership) => AvatarOrHash(
|
||||
data: (membership) => InkWell(
|
||||
onTapDown: (details) => context.showUserPopover(
|
||||
membership,
|
||||
globalPosition: details.globalPosition,
|
||||
),
|
||||
child: AvatarOrHash(
|
||||
membership.avatarUrl,
|
||||
membership.displayName,
|
||||
height: height,
|
||||
),
|
||||
),
|
||||
loading: () =>
|
||||
AvatarOrHash(null, message.authorId.substring(1), height: height),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import "package:flutter/widgets.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/author_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
||||
|
||||
class MessageDisplayname extends ConsumerWidget {
|
||||
final Message message;
|
||||
|
|
@ -13,11 +14,17 @@ class MessageDisplayname extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) => ref
|
||||
.watch(AuthorController.provider(message))
|
||||
.betterWhen(
|
||||
data: (membership) => Text(
|
||||
data: (membership) => InkWell(
|
||||
onTapDown: (details) => context.showUserPopover(
|
||||
membership,
|
||||
globalPosition: details.globalPosition,
|
||||
),
|
||||
child: Text(
|
||||
"${membership.displayName}${message.metadata?["pmp"] == null ? "" : " (via ${message.authorId})"}",
|
||||
style: style,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
loading: () => Text(""),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import "package:flutter/material.dart";
|
|||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:nexus/controllers/members_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
|
||||
class MemberList extends ConsumerWidget {
|
||||
|
|
@ -35,12 +36,12 @@ class MemberList extends ConsumerWidget {
|
|||
child: ListView(
|
||||
children: members
|
||||
.map(
|
||||
(member) => ListTile(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
Dialog(child: Text("TODO: Open member popover")),
|
||||
(member) => InkWell(
|
||||
onTapDown: (details) => context.showUserPopover(
|
||||
member,
|
||||
globalPosition: details.globalPosition,
|
||||
),
|
||||
child: ListTile(
|
||||
leading: AvatarOrHash(
|
||||
member.avatarUrl,
|
||||
member.displayName,
|
||||
|
|
@ -54,6 +55,7 @@ class MemberList extends ConsumerWidget {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
|
|
|
|||
91
lib/widgets/chat_page/user_popover.dart
Normal file
91
lib/widgets/chat_page/user_popover.dart
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/profile_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/models/membership.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
|
||||
class UserPopover extends ConsumerWidget {
|
||||
final Membership member;
|
||||
const UserPopover(this.member, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = Theme.of(context);
|
||||
final textTheme = theme.textTheme;
|
||||
return IntrinsicWidth(
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 16,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AvatarOrHash(member.avatarUrl, member.displayName, height: 80),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SelectableText(
|
||||
member.displayName,
|
||||
style: textTheme.headlineSmall,
|
||||
),
|
||||
SelectableText(member.userId, style: textTheme.titleSmall),
|
||||
SizedBox(height: 4),
|
||||
ref
|
||||
.watch(ProfileController.provider(member.userId))
|
||||
.betterWhen(
|
||||
loading: SizedBox.shrink,
|
||||
data: (profile) => Row(
|
||||
spacing: 4,
|
||||
children: [
|
||||
for (final pronoun in profile.pronouns.where(
|
||||
(pronoun) => pronoun.language == "en",
|
||||
))
|
||||
Chip(label: Text(pronoun.summary)),
|
||||
if (profile.timezone != null)
|
||||
Chip(label: Text(profile.timezone!)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
FilledButton.icon(onPressed: null, label: Text("Message")),
|
||||
FilledButton.icon(
|
||||
onPressed: null,
|
||||
label: Text("Kick"),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll(
|
||||
theme.colorScheme.error,
|
||||
),
|
||||
foregroundColor: WidgetStatePropertyAll(
|
||||
theme.colorScheme.onError,
|
||||
),
|
||||
),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: null,
|
||||
label: Text("Ban"),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll(
|
||||
theme.colorScheme.errorContainer,
|
||||
),
|
||||
foregroundColor: WidgetStatePropertyAll(
|
||||
theme.colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
].map((e) => Expanded(child: e)).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -490,11 +490,10 @@ packages:
|
|||
flutter_link_previewer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "packages/flutter_link_previewer"
|
||||
ref: HEAD
|
||||
resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627"
|
||||
url: "https://github.com/Henry-Hiles/flutter_chat_ui"
|
||||
source: git
|
||||
name: flutter_link_previewer
|
||||
sha256: "346f345064e65bc8bf739bccf19d6d6ca50f8183ffc52e452afa58c06ee2cbf7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue