Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/features/map/data/pin_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ class PinData {
final LatLng position;
final DateTime createdAt;
final bool isLocal;
final String? memo;

PinData({
required this.id,
required this.userId,
required this.position,
required this.createdAt,
this.isLocal = false,
this.memo,
});

factory PinData.local(LatLng position) {
Expand All @@ -90,6 +92,7 @@ class PinData {
),
createdAt: DateTime.parse(json['createdAt'] as String),
isLocal: json['isLocal'] as bool? ?? false,
memo: json['memo'] as String?,
);
}

Expand All @@ -101,6 +104,7 @@ class PinData {
'longitude': position.longitude,
'createdAt': createdAt.toUtc().toIso8601String(),
'isLocal': isLocal,
'memo': memo,
};
}
}
Expand Down
5 changes: 4 additions & 1 deletion lib/features/map/presentation/map_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ class _MapScreenState extends ConsumerState<MapScreen> {
width: 60,
height: 60,
alignment: Alignment.topCenter,
child: const Icon(Icons.location_pin, size: 60, color: Colors.red),
child: GestureDetector(
onTap: () => context.push('/memo/${pin.id}'),
child: const Icon(Icons.location_pin, size: 60, color: Colors.red),
),
);
}).toList();
}
Expand Down
89 changes: 89 additions & 0 deletions lib/features/map/presentation/memo_edit_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:memomap/features/map/providers/pin_provider.dart';

class MemoEditScreen extends ConsumerStatefulWidget {
const MemoEditScreen({super.key, required this.pinId});

final String pinId;

@override
ConsumerState<MemoEditScreen> createState() => _MemoEditScreenState();
}

class _MemoEditScreenState extends ConsumerState<MemoEditScreen> {
late final TextEditingController _controller;
String? _initialMemo;

@override
void initState() {
super.initState();
final pins = ref.read(pinsProvider).valueOrNull ?? [];
final pin = pins.where((p) => p.id == widget.pinId).firstOrNull;
_initialMemo = pin?.memo;
_controller = TextEditingController(text: _initialMemo ?? '');
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

void _saveMemo() {
final text = _controller.text.trim();
final memo = text.isEmpty ? null : text;
ref.read(pinsProvider.notifier).updatePinMemo(widget.pinId, memo);
context.pop();
}

void _deleteMemo() {
ref.read(pinsProvider.notifier).updatePinMemo(widget.pinId, null);
context.pop();
}

@override
Widget build(BuildContext context) {
final hasMemo = _initialMemo != null && _initialMemo!.isNotEmpty;

return Scaffold(
appBar: AppBar(
title: Text(hasMemo ? 'メモを編集' : 'メモを追加'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller,
maxLines: null,
decoration: const InputDecoration(
hintText: 'ここにメモを入力',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (hasMemo)
TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.red,
),
onPressed: _deleteMemo,
child: const Text('削除'),
),
ElevatedButton(
onPressed: _saveMemo,
child: const Text('保存'),
),
],
),
],
),
),
);
}
}
25 changes: 25 additions & 0 deletions lib/features/map/providers/pin_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,31 @@ class PinsNotifier extends AsyncNotifier<List<PinData>> {
}
}

Future<void> updatePinMemo(String id, String? memo) async {
final pins = state.value ?? [];
final index = pins.indexWhere((p) => p.id == id);
if (index == -1) return;

final updatedPin = PinData(
id: pins[index].id,
userId: pins[index].userId,
position: pins[index].position,
createdAt: pins[index].createdAt,
isLocal: pins[index].isLocal,
memo: memo,
);

final newPins = List<PinData>.from(pins);
newPins[index] = updatedPin;

state = AsyncValue.data(newPins);

// Update local storage
final storage = ref.read(localPinStorageProvider);
await storage.setLocalPins(newPins.where((p) => p.isLocal).toList());
await storage.setCachedPins(newPins.where((p) => !p.isLocal).toList());
}

Future<void> refresh() async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() => build());
Expand Down
8 changes: 6 additions & 2 deletions lib/router/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:memomap/features/auth/presentation/login_screen.dart';
import 'package:memomap/features/auth/providers/auth_provider.dart';
import 'package:memomap/features/map/presentation/map_screen.dart';
import 'package:memomap/features/map/presentation/memo_edit_screen.dart';
import 'package:memomap/features/profile/presentation/profile_screen.dart';

final routerProvider = Provider<GoRouter>((ref) {
Expand Down Expand Up @@ -43,8 +44,11 @@ final routerProvider = Provider<GoRouter>((ref) {
},
),
GoRoute(
path: '/auth-callback',
builder: (context, state) => const AuthCallbackScreen(),
path: '/memo/:pinId',
builder: (context, state) {
final pinId = state.pathParameters['pinId']!;
return MemoEditScreen(pinId: pinId);
},
),
],
errorBuilder: (context, state) => Scaffold(
Expand Down