frindlymap/lib/modules/ui/src/screens/location_screen.dart

97 lines
4.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:friendly_map/modules/app/app.dart';
import 'package:friendly_map/modules/platform/platform.dart';
import 'package:friendly_map/modules/storage/storage.dart';
import 'package:friendly_map/modules/visibility/visibility.dart';
import '../app_scope.dart';
import '../theme.dart';
import '../widgets/async_view.dart';
import '../widgets/common.dart';
import '../widgets/radar_view.dart';
class _LocData {
const _LocData(this.on, this.mine, this.friends, this.recipients);
final bool on;
final GeoPoint? mine;
final List<FriendLocation> friends;
final Map<Person, VLevel> recipients;
}
/// 위치: 켜면 공개 범위 표에서 허용한 친구에게만 내 위치를 보내고, 친구들이 보내 준 위치를 레이더로 본다.
class LocationScreen extends StatefulWidget {
const LocationScreen({super.key});
@override
State<LocationScreen> createState() => _LocationScreenState();
}
class _LocationScreenState extends State<LocationScreen> {
bool _busy = false;
Future<_LocData> _load(FriendlyApp app) async =>
_LocData(await app.location.sharingOn(), await app.location.mine(), await app.location.friends(), await app.location.recipients());
Future<void> _share() async {
final app = AppScope.of(context).app;
setState(() => _busy = true);
final n = await app.location.shareNow();
if (!mounted) return;
setState(() => _busy = false);
showNote(context, n == 0 ? '보낼 수 있는 친구가 없거나 위치를 얻지 못했어요.' : '친구 $n명에게 위치를 보냈어요.');
}
@override
Widget build(BuildContext context) {
final app = AppScope.of(context).app;
return Scaffold(
appBar: AppBar(title: const Text('위치')),
body: AsyncView<_LocData>(
load: _load,
builder: (context, d) {
final dots = [
if (d.mine != null)
for (final f in d.friends)
RadarDot(f.person.name, bearingDegrees(d.mine!, f.point), distanceMeters(d.mine!, f.point)),
];
return ListView(padding: const EdgeInsets.only(bottom: 32), children: [
Panel(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
ToggleRow(
title: '내 위치 공유',
subtitle: app.location.available ? '꺼져 있으면 아무에게도 보내지 않아요' : '이 기기에서는 위치를 쓸 수 없어요',
value: d.on,
onChanged: (v) => app.location.setSharing(app.location.available && v),
),
if (d.on) ...[
Text(
d.recipients.isEmpty
? '받을 수 있는 친구가 없어요. 내 정보의 공개 범위 표에서 "실시간 위치"를 열어 주세요.'
: '받는 친구: ${d.recipients.entries.map((e) => '${e.key.name}${e.value == VLevel.partial ? '(대략)' : ''}').join(', ')}',
style: const TextStyle(fontSize: 12, color: Pal.muted, height: 1.5),
),
const SizedBox(height: 8),
FilledButton.tonal(onPressed: _busy ? null : _share, child: const Text('지금 보내기')),
],
]),
),
const SectionHeader('친구 위치'),
if (d.mine == null) const EmptyNote('내 위치를 얻지 못했어요. 위치 권한과 GPS 를 확인해 주세요.'),
Center(child: RadarView(dots: dots)),
if (d.friends.isEmpty) const EmptyNote('친구가 보내 준 위치가 아직 없어요.'),
for (final f in d.friends)
ListTile(
leading: Avatar(f.person.name, size: 40),
title: Text(f.person.name),
subtitle: Text(
d.mine == null ? '${f.point.lat.toStringAsFixed(3)}, ${f.point.lng.toStringAsFixed(3)}' : '${distanceLabel(distanceMeters(d.mine!, f.point))} · ${shortTime(f.at)}',
style: const TextStyle(fontSize: 12, color: Pal.muted),
),
),
]);
},
),
);
}
}