0c7d359076
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>
233 lines
6.5 KiB
Dart
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)),
|
|
),
|
|
);
|
|
}
|
|
}
|