Add Firestore integration with real-time sync

This commit is contained in:
2026-03-27 19:02:43 +05:00
parent 2336df8e86
commit 6e428b4ede
5 changed files with 362 additions and 76 deletions
+107 -27
View File
@@ -23,6 +23,12 @@ class FirestoreService {
await _db.collection('users').doc(user.id).update(user.toFirestore());
}
Stream<UserModel?> watchUser(String userId) {
return _db.collection('users').doc(userId).snapshots().map((doc) {
return doc.exists ? UserModel.fromFirestore(doc) : null;
});
}
// === PROJECTS ===
Future<void> createProject(ProjectModel project) async {
await _db.collection('projects').doc(project.id).set(project.toFirestore());
@@ -34,7 +40,7 @@ class FirestoreService {
.where('teamId', isEqualTo: teamId)
.orderBy('createdAt', descending: true)
.get();
return snapshot.docs.map((doc) => ProjectModel.fromFirestore(doc)).toList();
}
@@ -43,10 +49,20 @@ class FirestoreService {
.collection('projects')
.where('memberIds', arrayContains: userId)
.get();
return snapshot.docs.map((doc) => ProjectModel.fromFirestore(doc)).toList();
}
Stream<List<ProjectModel>> watchProjectsByUser(String userId) {
return _db
.collection('projects')
.where('memberIds', arrayContains: userId)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => ProjectModel.fromFirestore(doc))
.toList());
}
// === TASKS ===
Future<void> createTask(TaskModel task) async {
await _db.collection('tasks').doc(task.id).set(task.toFirestore());
@@ -58,7 +74,7 @@ class FirestoreService {
.where('projectId', isEqualTo: projectId)
.orderBy('createdAt', descending: true)
.get();
return snapshot.docs.map((doc) => TaskModel.fromFirestore(doc)).toList();
}
@@ -66,9 +82,17 @@ class FirestoreService {
final snapshot = await _db
.collection('tasks')
.where('assigneeId', isEqualTo: assigneeId)
.where('status', whereIn: ['todo', 'inProgress', 'review'])
.where('status', whereIn: ['todo', 'inProgress', 'review']).get();
return snapshot.docs.map((doc) => TaskModel.fromFirestore(doc)).toList();
}
Future<List<TaskModel>> getAllTasks() async {
final snapshot = await _db
.collection('tasks')
.orderBy('createdAt', descending: true)
.get();
return snapshot.docs.map((doc) => TaskModel.fromFirestore(doc)).toList();
}
@@ -86,13 +110,31 @@ class FirestoreService {
.collection('tasks')
.where('projectId', isEqualTo: projectId)
.get();
return snapshot.docs.map((doc) => TaskModel.fromFirestore(doc)).toList();
}
// Поток задач для реального времени
Stream<List<TaskModel>> watchTasks(String? projectId) {
Query query = _db.collection('tasks');
if (projectId != null && projectId.isNotEmpty) {
query = query.where('projectId', isEqualTo: projectId);
}
return query.orderBy('createdAt', descending: true).snapshots().map(
(snapshot) =>
snapshot.docs.map((doc) => TaskModel.fromFirestore(doc)).toList());
}
// === MIND MAPS ===
Future<void> createMindMapNode(MindMapNode node) async {
await _db.collection('mindmaps').doc(node.projectId).collection('nodes').doc(node.id).set(node.toFirestore());
await _db
.collection('mindmaps')
.doc(node.projectId)
.collection('nodes')
.doc(node.id)
.set(node.toFirestore());
}
Future<List<MindMapNode>> getMindMapNodes(String projectId) async {
@@ -102,7 +144,7 @@ class FirestoreService {
.collection('nodes')
.orderBy('level')
.get();
return snapshot.docs.map((doc) => MindMapNode.fromFirestore(doc)).toList();
}
@@ -115,6 +157,15 @@ class FirestoreService {
.update(node.toFirestore());
}
Future<void> deleteMindMapNode(String projectId, String nodeId) async {
await _db
.collection('mindmaps')
.doc(projectId)
.collection('nodes')
.doc(nodeId)
.delete();
}
// === CALENDAR EVENTS ===
Future<void> createEvent(CalendarEventModel event) async {
await _db.collection('events').doc(event.id).set(event.toFirestore());
@@ -129,13 +180,15 @@ class FirestoreService {
.collection('events')
.where('startTime', isLessThanOrEqualTo: Timestamp.fromDate(end))
.where('endTime', isGreaterThanOrEqualTo: Timestamp.fromDate(start));
if (userId != null) {
query = query.where('attendeeIds', arrayContains: userId);
}
final snapshot = await query.get();
return snapshot.docs.map((doc) => CalendarEventModel.fromFirestore(doc)).toList();
return snapshot.docs
.map((doc) => CalendarEventModel.fromFirestore(doc))
.toList();
}
Future<void> updateEvent(CalendarEventModel event) async {
@@ -146,6 +199,17 @@ class FirestoreService {
await _db.collection('events').doc(eventId).delete();
}
Stream<List<CalendarEventModel>> watchEvents(String userId) {
return _db
.collection('events')
.where('attendeeIds', arrayContains: userId)
.orderBy('startTime')
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => CalendarEventModel.fromFirestore(doc))
.toList());
}
// === ALARMS ===
Future<void> createAlarm(AlarmModel alarm) async {
await _db.collection('alarms').doc(alarm.id).set(alarm.toFirestore());
@@ -157,7 +221,7 @@ class FirestoreService {
.where('isEnabled', isEqualTo: true)
.orderBy('dateTime')
.get();
return snapshot.docs.map((doc) => AlarmModel.fromFirestore(doc)).toList();
}
@@ -172,29 +236,45 @@ class FirestoreService {
// === BATCH OPERATIONS ===
Future<void> syncTasks(List<TaskModel> tasks) async {
final batch = _db.batch();
for (final task in tasks) {
batch.set(_db.collection('tasks').doc(task.id), task.toFirestore());
}
await batch.commit();
}
// === STREAMS (для реального времени) ===
Stream<List<TaskModel>> watchTasks(String projectId) {
return _db
.collection('tasks')
.where('projectId', isEqualTo: projectId)
.snapshots()
.map((snapshot) => snapshot.docs.map((doc) => TaskModel.fromFirestore(doc)).toList());
// === TEAMS ===
Future<void> createTeam(String teamId, String name, String ownerId) async {
await _db.collection('teams').doc(teamId).set({
'name': name,
'ownerId': ownerId,
'memberIds': [ownerId],
'createdAt': Timestamp.now(),
'updatedAt': Timestamp.now(),
});
}
Stream<List<CalendarEventModel>> watchEvents(String userId) {
return _db
.collection('events')
.where('attendeeIds', arrayContains: userId)
.snapshots()
.map((snapshot) => snapshot.docs.map((doc) => CalendarEventModel.fromFirestore(doc)).toList());
Future<void> addTeamMember(String teamId, String userId) async {
await _db.collection('teams').doc(teamId).update({
'memberIds': FieldValue.arrayUnion([userId]),
'updatedAt': Timestamp.now(),
});
}
Future<void> removeTeamMember(String teamId, String userId) async {
await _db.collection('teams').doc(teamId).update({
'memberIds': FieldValue.arrayRemove([userId]),
'updatedAt': Timestamp.now(),
});
}
Stream<List<Map<String, dynamic>>> watchTeamMembers(String teamId) {
return _db.collection('teams').doc(teamId).snapshots().map((doc) {
if (!doc.exists) return [];
final data = doc.data() as Map<String, dynamic>;
return List<Map<String, dynamic>>.from(data['memberIds'] ?? []);
});
}
}
+46
View File
@@ -1,4 +1,5 @@
import 'package:go_router/go_router.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../screens/auth_screen.dart';
import '../screens/home_screen.dart';
import '../screens/tasks_screen.dart';
@@ -7,10 +8,16 @@ import '../screens/mind_map_screen.dart';
import '../screens/calendar_screen.dart';
import '../screens/alarms_screen.dart';
import '../screens/ai_test_screen.dart';
import '../providers/auth_provider.dart';
// Глобальный ключ для навигации
final GlobalKey<NavigatorState> _rootNavigatorKey = GlobalKey<NavigatorState>();
class AppRouter {
static final GoRouter router = GoRouter(
navigatorKey: _rootNavigatorKey,
initialLocation: '/auth',
refreshListenable: GoRouterRefreshNotifier(),
routes: [
GoRoute(
path: '/auth',
@@ -74,5 +81,44 @@ class AppRouter {
],
),
],
redirect: (context, state) {
final isLoggedIn = GoRouterRefreshNotifier.isAuthenticated;
final isLoggingIn = state.matchedLocation == '/auth';
if (!isLoggedIn && !isLoggingIn) {
return '/auth';
}
if (isLoggedIn && isLoggingIn) {
return '/home';
}
return null;
},
);
}
// Класс для уведомления об изменении состояния аутентификации
class GoRouterRefreshNotifier extends ChangeNotifier {
static bool isAuthenticated = false;
GoRouterRefreshNotifier() {
// Подписка на изменения аутентификации будет добавлена через provider
}
@override
void dispose() {
super.dispose();
}
}
// Provider для обновления роутера при изменении аутентификации
final routerRefreshProvider = ChangeNotifierProvider((ref) {
final auth = ref.watch(authProvider);
auth.whenData((user) {
GoRouterRefreshNotifier.isAuthenticated = user != null;
});
return GoRouterRefreshNotifier();
});