Files
pyramid/lib/features/spaces/admin/space_admin_shared.dart
Bernd Steckmeister 0c7d359076 refactor: Gott-Datei space_admin_dialog.dart in 6 Dateien aufgeteilt
Gleiche Technik wie bei den vorigen drei Umbauten: reine Verschiebung per
Dart part/part-of, keine Logikänderung. space_admin_dialog.dart schrumpft
von 2278 auf 19 Zeilen (nur noch Bibliothekskopf). Verifiziert per
automatisiertem Blockvergleich gegen das Original und flutter analyze.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 11:01:58 +02:00

233 lines
6.5 KiB
Dart

part of '../space_admin_dialog.dart';
class _SectionLabel extends StatelessWidget {
final String text;
final PyramidTheme pt;
const _SectionLabel(this.text, this.pt);
@override
Widget build(BuildContext context) {
return Text(text,
style: TextStyle(
color: pt.fgDim,
fontSize: 11,
fontWeight: FontWeight.w700,
letterSpacing: 0.8));
}
}
class _Field extends StatelessWidget {
final TextEditingController controller;
final String hint;
final int maxLines;
final PyramidTheme pt;
const _Field({
required this.controller,
required this.hint,
this.maxLines = 1,
required this.pt,
});
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
maxLines: maxLines,
style: TextStyle(color: pt.fg, fontSize: 13),
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(color: pt.fgDim),
filled: true,
fillColor: pt.bg0,
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(pt.rSm),
borderSide: BorderSide(color: pt.border),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(pt.rSm),
borderSide: BorderSide(color: pt.border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(pt.rSm),
borderSide: BorderSide(color: pt.accent),
),
),
);
}
}
class _SearchField extends StatelessWidget {
final TextEditingController controller;
final String hint;
final PyramidTheme pt;
final ValueChanged<String> onChanged;
const _SearchField({
required this.controller,
required this.hint,
required this.pt,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: onChanged,
style: TextStyle(color: pt.fg, fontSize: 13),
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(color: pt.fgDim),
prefixIcon: Icon(Icons.search, color: pt.fgDim, size: 18),
filled: true,
fillColor: pt.bg0,
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
isDense: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(pt.rSm),
borderSide: BorderSide(color: pt.border),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(pt.rSm),
borderSide: BorderSide(color: pt.border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(pt.rSm),
borderSide: BorderSide(color: pt.accent),
),
),
);
}
}
class _AvatarEditor extends StatelessWidget {
final Room space;
final Uint8List? pendingAvatar;
final bool canEdit;
final VoidCallback onPick;
final VoidCallback onRemove;
final PyramidTheme pt;
final dynamic client;
const _AvatarEditor({
required this.space,
required this.pendingAvatar,
required this.canEdit,
required this.onPick,
required this.onRemove,
required this.pt,
required this.client,
});
@override
Widget build(BuildContext context) {
final letter = space.getLocalizedDisplayname().isNotEmpty
? space.getLocalizedDisplayname()[0].toUpperCase()
: '?';
final avatarUri = space.avatar;
final hasAvatar = pendingAvatar != null || avatarUri != null;
Widget avatarWidget;
if (pendingAvatar != null) {
avatarWidget = Image.memory(pendingAvatar!, fit: BoxFit.cover,
width: 64, height: 64);
} else if (avatarUri != null && client != null) {
avatarWidget = MxcAvatar(
mxcUri: avatarUri,
client: client,
size: 64,
borderRadius: BorderRadius.circular(32),
placeholder: (_) => _InitialAvatar(letter: letter, pt: pt, size: 64),
);
} else {
avatarWidget = _InitialAvatar(letter: letter, pt: pt, size: 64);
}
return Row(
children: [
GestureDetector(
onTap: canEdit ? onPick : null,
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: pt.border),
),
clipBehavior: Clip.hardEdge,
child: avatarWidget,
),
if (canEdit)
Positioned(
right: -2,
bottom: -2,
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: pt.accent,
shape: BoxShape.circle,
border: Border.all(color: pt.bg1, width: 2),
),
child: const Icon(Icons.edit, size: 10, color: Colors.white),
),
),
],
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
canEdit ? 'Klicken zum Ändern' : 'Serverbild',
style: TextStyle(color: pt.fg, fontSize: 13,
fontWeight: FontWeight.w500),
),
if (canEdit && hasAvatar)
GestureDetector(
onTap: onRemove,
child: Text('Entfernen',
style: TextStyle(
color: const Color(0xFFED4245), fontSize: 12)),
),
],
),
],
);
}
}
class _InitialAvatar extends StatelessWidget {
final String letter;
final PyramidTheme pt;
final double size;
const _InitialAvatar(
{required this.letter, required this.pt, required this.size});
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: pt.bg3,
borderRadius: BorderRadius.circular(size / 2),
),
child: Center(
child: Text(letter,
style: TextStyle(
color: pt.fg,
fontSize: size * 0.4,
fontWeight: FontWeight.w700)),
),
);
}
}