reorganize and rename widgets
This commit is contained in:
parent
05d25869ef
commit
d5f29dc344
4 changed files with 568 additions and 547 deletions
42
lib/widgets/avatars/avatar_or_hash.dart
Normal file
42
lib/widgets/avatars/avatar_or_hash.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import "package:color_hash/color_hash.dart";
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/helpers/mxc_image.dart";
|
||||
|
||||
final class const AvatarOrHash(
|
||||
final Uri? avatar,
|
||||
final String title, {
|
||||
final Widget? fallback,
|
||||
final double dimension = 24,
|
||||
super.key,
|
||||
}) extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final box = ColoredBox(
|
||||
color: ColorHash(title).color,
|
||||
child: Center(child: Icon(Icons.person, size: dimension / 2)),
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
width: dimension,
|
||||
height: dimension,
|
||||
child: Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: .all(.circular((dimension - 8) / 2.5)),
|
||||
child: SizedBox.square(
|
||||
dimension: dimension,
|
||||
child: avatar == null
|
||||
? fallback ?? box
|
||||
: Image(
|
||||
image: MxcImage(ref, .new(mxc: avatar!, isAvatar: true)),
|
||||
fit: .cover,
|
||||
loadingBuilder: (_, child, loadingProgress) =>
|
||||
loadingProgress == null ? child : fallback ?? box,
|
||||
errorBuilder: (_, _, _) => fallback ?? box,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
39
lib/widgets/avatars/avatar_ring.dart
Normal file
39
lib/widgets/avatars/avatar_ring.dart
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import "package:material_ui/material_ui.dart";
|
||||
|
||||
class const AvatarRing({
|
||||
required final String tooltip,
|
||||
required final VoidCallback onTap,
|
||||
required final Widget child,
|
||||
final double dimension = 24,
|
||||
super.key,
|
||||
}) extends StatelessWidget {
|
||||
static const ringWidth = 1.5;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Tooltip(
|
||||
message: tooltip,
|
||||
child: Container(
|
||||
padding: .all(ringWidth),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
shape: .circle,
|
||||
),
|
||||
child: ClipOval(
|
||||
child: SizedBox.square(
|
||||
dimension: dimension,
|
||||
child: Stack(
|
||||
fit: .expand,
|
||||
children: [
|
||||
child,
|
||||
// Over the child, so the ripple shows on top of avatars
|
||||
Material(
|
||||
type: .transparency,
|
||||
child: InkWell(onTap: onTap),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
86
lib/widgets/avatars/editable_avatar.dart
Normal file
86
lib/widgets/avatars/editable_avatar.dart
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:material_adaptive_sheets/material_adaptive_sheets.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/helpers/file_dialog_helper.dart";
|
||||
import "package:nexus/helpers/hooks/saving.dart";
|
||||
import "package:nexus/main.dart";
|
||||
import "package:nexus/models/content/message.dart";
|
||||
|
||||
/// Shows [child], an avatar, with a button to upload a new one or remove it.
|
||||
class const EditableAvatar({
|
||||
required final Widget child,
|
||||
|
||||
/// Whether there's an avatar to remove.
|
||||
required final bool hasAvatar,
|
||||
|
||||
/// Called with the uploaded image, or null when the avatar is removed.
|
||||
required final Future<void> Function(Uri? url) onChanged,
|
||||
final double dimension = 200,
|
||||
super.key,
|
||||
}) extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final saving = Saving.use();
|
||||
Future<void> save(Future<void> Function() action) =>
|
||||
saving.run(() => action().onError(showError));
|
||||
|
||||
Future<void> pick() async {
|
||||
final file = await ref
|
||||
.read(FileDialogHelper.provider)
|
||||
.pickFile(images: true);
|
||||
if (file == null || !context.mounted) return;
|
||||
|
||||
await save(() async {
|
||||
final content = await ref
|
||||
.read(ClientController.provider.notifier)
|
||||
.uploadMedia(.new(path: file.path, encrypt: false));
|
||||
if (content case ImageMessageContent(:final url)) {
|
||||
await onChanged(url);
|
||||
} else {
|
||||
throw Exception("The file isn't an image");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return SizedBox.square(
|
||||
dimension: dimension,
|
||||
child: Stack(
|
||||
children: [
|
||||
child,
|
||||
if (saving.active) const Center(child: CircularProgressIndicator()),
|
||||
Positioned(
|
||||
right: dimension * 0.01,
|
||||
bottom: dimension * 0.01,
|
||||
child: Builder(
|
||||
builder: (context) => IconButton.filled(
|
||||
tooltip: "Edit avatar",
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: saving.active
|
||||
? null
|
||||
: !hasAvatar
|
||||
? pick
|
||||
: () => context.showAdaptiveMenu([
|
||||
PopupMenuItem(
|
||||
onTap: pick,
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.upload),
|
||||
title: Text("Upload new avatar"),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () => save(() => onChanged(null)),
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.delete),
|
||||
title: Text("Remove avatar"),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
14
lib/widgets/avatars/image_pack_avatar.dart
Normal file
14
lib/widgets/avatars/image_pack_avatar.dart
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import "package:material_ui/material_ui.dart";
|
||||
import "package:nexus/widgets/emoji/mxc_emoji.dart";
|
||||
|
||||
class const ImagePackAvatar(final Uri? icon, {super.key})
|
||||
extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => SizedBox.square(
|
||||
dimension: 40,
|
||||
child: switch (icon) {
|
||||
final icon? => MxcEmoji(icon),
|
||||
null => Icon(Icons.emoji_emotions),
|
||||
},
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue