Files
iykyk_msn/mobile/lib/features/home/context_members_tab.dart
2026-04-07 16:17:03 +09:00

190 lines
8.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../core/msn_api.dart';
import '../../core/session_controller.dart';
import '../../theme/toss_theme.dart';
import '../profile/user_profile_sheet.dart';
import 'home_providers.dart';
/// Members of the selected messenger space: row tap opens DM; profile icon opens sheet.
class ContextMembersTab extends ConsumerWidget {
const ContextMembersTab({super.key, required this.contextId});
final String contextId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final session = ref.watch(sessionProvider).value;
final myId = session?.userId;
final async = ref.watch(membersForContextProvider(contextId));
return async.when(
loading: () => const Center(child: CircularProgressIndicator(color: TossColors.blue)),
error: (e, _) => Center(child: Text('$e')),
data: (members) {
if (members.isEmpty) {
return ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: const [
SizedBox(height: 120),
Center(child: Text('친구가 없습니다')),
],
);
}
return RefreshIndicator(
color: TossColors.blue,
onRefresh: () async {
ref.invalidate(membersForContextProvider(contextId));
await ref.read(membersForContextProvider(contextId).future);
},
child: ListView.separated(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.fromLTRB(16, 8, 16, 100),
itemCount: members.length,
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: (context, i) {
final m = members[i];
final isSelf = myId != null && m.userId == myId;
Future<void> openChat() async {
final roomId =
await ref.read(msnApiProvider).openDirectRoom(contextId, m.userId);
if (!context.mounted) return;
await context.push('/chat?roomId=$roomId&contextId=$contextId');
}
Future<void> openProfile() async {
await showUserProfileSheet(
context,
ref,
contextId: contextId,
userId: m.userId,
);
}
return Material(
color: TossColors.surface,
borderRadius: BorderRadius.circular(16),
child: Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(color: TossColors.line),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
child: Row(
children: [
Expanded(
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () async {
if (isSelf) {
await openProfile();
} else {
await openChat();
}
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
child: Row(
children: [
CircleAvatar(
radius: 22,
backgroundColor: TossColors.blue.withValues(alpha: 0.1),
backgroundImage:
m.avatarUrl != null && m.avatarUrl!.isNotEmpty
? NetworkImage(m.avatarUrl!)
: null,
child: m.avatarUrl == null || m.avatarUrl!.isEmpty
? Text(
m.displayName.isNotEmpty
? m.displayName[0].toUpperCase()
: '?',
style: const TextStyle(
color: TossColors.blue,
fontWeight: FontWeight.w600,
),
)
: null,
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
m.displayName.isNotEmpty
? m.displayName
: m.userId,
style: Theme.of(context).textTheme.titleMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (isSelf)
Padding(
padding: const EdgeInsets.only(left: 8),
child: Text(
'',
style: Theme.of(context)
.textTheme
.labelSmall
?.copyWith(
color: TossColors.textSecondary,
),
),
),
],
),
if (m.statusMessage != null &&
m.statusMessage!.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
m.statusMessage!,
style: Theme.of(context).textTheme.bodySmall,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
if (!isSelf)
Icon(
Icons.chat_bubble_outline,
size: 20,
color: TossColors.blue.withValues(alpha: 0.7),
),
],
),
),
),
),
IconButton(
tooltip: '프로필',
icon: Icon(
Icons.person_outline_rounded,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPressed: () async {
await openProfile();
},
),
],
),
),
),
);
},
),
);
},
);
}
}