오대리ㅣㅣㅣㅣ
This commit is contained in:
190
mobile/lib/features/home/my_profile_tab.dart
Normal file
190
mobile/lib/features/home/my_profile_tab.dart
Normal file
@@ -0,0 +1,190 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/msn_api.dart';
|
||||
import '../../core/session_controller.dart';
|
||||
import '../../models/context_model.dart';
|
||||
import '../../models/profile_model.dart';
|
||||
import '../../theme/toss_theme.dart';
|
||||
import 'home_providers.dart';
|
||||
|
||||
final myProfileForContextProvider =
|
||||
FutureProvider.autoDispose.family<ProfileModel, String>((ref, contextId) async {
|
||||
return ref.watch(msnApiProvider).getMyProfile(contextId);
|
||||
});
|
||||
|
||||
/// Edit display name and status per messenger space, with 일상 / 직장 segment synced to top chips.
|
||||
class MyProfileTab extends ConsumerStatefulWidget {
|
||||
const MyProfileTab({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<MyProfileTab> createState() => _MyProfileTabState();
|
||||
}
|
||||
|
||||
class _MyProfileTabState extends ConsumerState<MyProfileTab> {
|
||||
final _nameCtrl = TextEditingController();
|
||||
final _statusCtrl = TextEditingController();
|
||||
bool _dirty = false;
|
||||
bool _saving = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameCtrl.dispose();
|
||||
_statusCtrl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _save(String contextId) async {
|
||||
setState(() => _saving = true);
|
||||
try {
|
||||
await ref.read(msnApiProvider).updateMyProfile(
|
||||
contextId,
|
||||
displayName: _nameCtrl.text.trim(),
|
||||
statusMessage: _statusCtrl.text.trim(),
|
||||
);
|
||||
ref.invalidate(myProfileForContextProvider(contextId));
|
||||
ref.invalidate(membersForContextProvider(contextId));
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_dirty = false;
|
||||
_saving = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('저장했습니다')),
|
||||
);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() => _saving = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('$e')));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSegmentChanged(String contextId) async {
|
||||
await ref.read(sessionProvider.notifier).setSelectedContext(contextId);
|
||||
setState(() => _dirty = false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final session = ref.watch(sessionProvider).value;
|
||||
final cid = session?.effectiveContextId;
|
||||
final contextsAsync = ref.watch(contextsListProvider);
|
||||
|
||||
return contextsAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator(color: TossColors.blue)),
|
||||
error: (e, _) => Center(child: Text('$e')),
|
||||
data: (contexts) {
|
||||
final personal = _firstOfKind(contexts, 'personal');
|
||||
final work = _firstOfKind(contexts, 'work');
|
||||
|
||||
if (cid == null) {
|
||||
return const Center(child: Text('맥락이 없습니다'));
|
||||
}
|
||||
|
||||
final async = ref.watch(myProfileForContextProvider(cid));
|
||||
|
||||
ref.listen<AsyncValue<ProfileModel>>(myProfileForContextProvider(cid), (prev, next) {
|
||||
next.whenData((p) {
|
||||
if (!_dirty && mounted) {
|
||||
_nameCtrl.text = p.displayName;
|
||||
_statusCtrl.text = p.statusMessage ?? '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator(color: TossColors.blue)),
|
||||
error: (e, _) => Center(child: Text('$e')),
|
||||
data: (_) => RefreshIndicator(
|
||||
color: TossColors.blue,
|
||||
onRefresh: () async {
|
||||
ref.invalidate(myProfileForContextProvider(cid));
|
||||
await ref.read(myProfileForContextProvider(cid).future);
|
||||
},
|
||||
child: ListView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 100),
|
||||
children: [
|
||||
if (personal != null && work != null) ...[
|
||||
SegmentedButton<String>(
|
||||
segments: [
|
||||
ButtonSegment<String>(
|
||||
value: personal.id,
|
||||
label: Text(personal.name),
|
||||
icon: const Icon(Icons.home_outlined, size: 18),
|
||||
),
|
||||
ButtonSegment<String>(
|
||||
value: work.id,
|
||||
label: Text(work.name),
|
||||
icon: const Icon(Icons.business_outlined, size: 18),
|
||||
),
|
||||
],
|
||||
selected: {cid},
|
||||
onSelectionChanged: (s) {
|
||||
if (s.isEmpty) return;
|
||||
_onSegmentChanged(s.first);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
Text(
|
||||
'지금 편집: ${_contextName(contexts, cid)}',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'이 맥락에서만 보이는 이름과 상태입니다.',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
TextField(
|
||||
controller: _nameCtrl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: '표시 이름',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
onChanged: (_) => setState(() => _dirty = true),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _statusCtrl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: '상태 메시지',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
onChanged: (_) => setState(() => _dirty = true),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
FilledButton(
|
||||
onPressed: _saving ? null : () => _save(cid),
|
||||
child: _saving
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
|
||||
)
|
||||
: const Text('저장'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
ContextModel? _firstOfKind(List<ContextModel> list, String kind) {
|
||||
for (final c in list) {
|
||||
if (c.kind == kind) return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String _contextName(List<ContextModel> list, String id) {
|
||||
for (final c in list) {
|
||||
if (c.id == id) return c.name;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user