part of '../rooms_panel.dart'; class _SpaceInviteView extends ConsumerStatefulWidget { final Room space; final PyramidTheme pt; const _SpaceInviteView({required this.space, required this.pt}); @override ConsumerState<_SpaceInviteView> createState() => _SpaceInviteViewState(); } class _SpaceInviteViewState extends ConsumerState<_SpaceInviteView> { bool _accepting = false; bool _declining = false; Future _accept() async { setState(() => _accepting = true); final client = widget.space.client; final id = widget.space.id; try { await client.joinRoomById(id).timeout(const Duration(seconds: 20)); // Server confirmed the join. Switch to the channel list optimistically — // the local sync membership may lag behind, so don't wait for it. ref.read(forceJoinedSpacesProvider.notifier).add(id); if (mounted) setState(() => _accepting = false); } catch (e) { if (mounted) { setState(() => _accepting = false); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( 'Beitritt fehlgeschlagen: ${e.toString().split('\n').first}'), backgroundColor: widget.pt.danger, )); } } } Future _decline() async { setState(() => _declining = true); try { await widget.space.leave(); if (mounted) { ref.read(activeSpaceIdProvider.notifier).state = 'rooms'; } } catch (_) { if (mounted) setState(() => _declining = false); } } @override Widget build(BuildContext context) { final pt = widget.pt; final client = ref.watch(matrixClientProvider).valueOrNull; final name = widget.space.getLocalizedDisplayname(); final busy = _accepting || _declining; return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 72, height: 72, decoration: BoxDecoration( color: pt.accent.withAlpha(40), borderRadius: BorderRadius.circular(20), ), child: client != null && widget.space.avatar != null ? ClipRRect( borderRadius: BorderRadius.circular(20), child: MxcAvatar( mxcUri: widget.space.avatar, client: client, size: 72, borderRadius: BorderRadius.circular(20), placeholder: (_) => Center( child: Text( name.isNotEmpty ? name[0].toUpperCase() : '?', style: TextStyle( color: pt.accent, fontSize: 28, fontWeight: FontWeight.w700), ), ), ), ) : Center( child: Text( name.isNotEmpty ? name[0].toUpperCase() : '?', style: TextStyle( color: pt.accent, fontSize: 28, fontWeight: FontWeight.w700), ), ), ), const SizedBox(height: 16), Text('Einladung zum Space', style: TextStyle(color: pt.fgDim, fontSize: 13)), const SizedBox(height: 4), Text( name, textAlign: TextAlign.center, style: TextStyle( color: pt.fg, fontSize: 18, fontWeight: FontWeight.w700), ), const SizedBox(height: 24), Row( children: [ Expanded( child: OutlinedButton( style: OutlinedButton.styleFrom( foregroundColor: pt.danger, side: BorderSide(color: pt.danger.withAlpha(100)), padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8)), ), onPressed: busy ? null : _decline, child: _declining ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator.adaptive( strokeWidth: 2)) : const Text('Ablehnen'), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: pt.accent, foregroundColor: pt.accentFg, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8)), ), onPressed: busy ? null : _accept, child: _accepting ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator.adaptive( strokeWidth: 2)) : const Text('Beitreten'), ), ), ], ), ], ), ), ); } }