(fix) felhős projekthez csak bejelentkezés után lehet csatlakozni, név megjelenítése a kapcsolatok mellett a terepbejárás nézetben.
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s

This commit is contained in:
2026-08-07 21:25:31 +02:00
parent 950c2f48f1
commit e1128700f0
10 changed files with 124 additions and 18 deletions
+2 -1
View File
@@ -1351,7 +1351,8 @@ class AppDatabase {
Future<void> insertPendingContact(Map<String, dynamic> row) async {
final db = await database;
await db.insert('contacts_outbox', row);
await db.insert('contacts_outbox', row,
conflictAlgorithm: ConflictAlgorithm.replace);
}
/// [projectId] NÉLKÜL (a flush-hoz) MINDEN várólistás sort ad vissza —
+1
View File
@@ -280,6 +280,7 @@ class AppLogger extends GetxService {
final userId = Supabase.instance.client.auth.currentUser?.id;
await Supabase.instance.client.from('terepi_seged_logs').insert({
'type': type,
'tag': tag,
'message': message,
'error_text': error?.toString(),
+14 -1
View File
@@ -40,7 +40,17 @@ class ContactExportService {
: v;
}
final header = ['Nev', 'Cim', 'Telefon', 'Email', 'Megjegyzes'].join(sep);
final header = [
'Nev',
'Cim',
'Telefon',
'Email',
'Megjegyzes',
'Szélesség',
'Hosszúság',
'EOV_Y',
'EOV_X'
].join(sep);
final sb = StringBuffer();
sb.write('\uFEFF'); // UTF-8 BOM — az Excel enélkül elrontja az ékezeteket
@@ -52,6 +62,9 @@ class ContactExportService {
txt(c.phone),
txt(c.email),
txt(c.note),
c.lat?.toString() ?? '',
c.lon?.toString() ?? '',
c.eovY?.toString() ?? '',
].join(sep));
}
+27 -4
View File
@@ -4,6 +4,7 @@ import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:terepi_seged/services/app_logger.dart';
import 'package:uuid/uuid.dart';
import '../models/contact.dart';
@@ -81,16 +82,34 @@ class ContactService extends GetxService {
/// Új kapcsolat vagy szerver-oldali frissítés.
/// Visszaadja, hogy a művelet OFFLINE pufferbe került-e (true = várólistán).
Future<bool> save(Contact c) async {
Future<bool> save(Contact c, {String? existingLocalUuid}) async {
// Meglévő (szerver-oldali) rekord frissítése CSAK online — lásd korlát.
final isUpdate = c.id != null;
if (await _isOnline) {
try {
await _client.from('terepi_seged_contacts').upsert(c.toWriteMap());
// Ha ez korábban egy PENDING (helyi puffer-) sorból indult, és
// most sikerült felmenni, a régi helyi sort törölni kell —
// különben örökre "függőben" maradna, és a flush() is
// duplikálná.
if (existingLocalUuid != null) {
await _db.deletePendingContact(existingLocalUuid);
}
return false; // felment
} catch (e) {
if (isUpdate) rethrow; // frissítést nem pufferelünk
if (e is PostgrestException) {
// Válasz érkezett a szervertől, de hibás (séma, RLS, megkötés) —
// ez NEM hálózati probléma. Ha csendben pufferbe tennénk, ez a
// sor SOSEM jutna fel, és senki nem venné észre — inkább
// azonnal, láthatóan hibázzon.
AppLogger.e('ContactService.save',
'Supabase hiba új kapcsolat mentésekor: $e');
rethrow;
}
// Valódi hálózati/kapcsolati hiba (pl. pillanatnyi kiesés) →
// pufferbe, a flush() majd újrapróbálja.
// Új rekord + online hiba (pl. pillanatnyi kiesés) → pufferbe.
}
} else if (isUpdate) {
@@ -101,7 +120,7 @@ class ContactService extends GetxService {
// Offline (vagy online-hiba) új rekord → outbox.
await _db.insertPendingContact({
'local_uuid': _uuid.v4(),
'local_uuid': existingLocalUuid ?? _uuid.v4(),
'project_id': c.projectId,
'name': c.name.trim(),
'address': c.address.trim(),
@@ -173,8 +192,12 @@ class ContactService extends GetxService {
// Sikeres felküldés → a lokális példány törölhető.
await _db.deletePendingContact(m['local_uuid'] as String);
uploaded++;
} catch (_) {
// A sor marad a pufferben, a következő flush újrapróbálja.
} catch (e) {
// A sor marad a pufferben (legközelebb újrapróbáljuk) — de
// naplózzuk, hogy ne maradjon örökre észrevétlen, ha a hiba nem
// hálózati, hanem tartós (pl. séma-eltérés, RLS).
AppLogger.e('ContactService.flush',
'Kapcsolat feltöltési hiba (local_uuid=${m['local_uuid']}): $e');
}
}
return uploaded;
+37 -2
View File
@@ -6,6 +6,15 @@ import '../models/project.dart';
import 'app_database.dart';
import 'ts_sync_service.dart';
class ProjectRequiresLoginException implements Exception {
final String message;
ProjectRequiresLoginException(
[this.message = 'Felhős projekt használatához be kell jelentkezni.']);
@override
String toString() => message;
}
class ProjectService extends GetxService {
static ProjectService get to => Get.find();
@@ -19,6 +28,20 @@ class ProjectService extends GetxService {
super.onInit();
await _loadProjects();
await _restoreActiveProject();
Supabase.instance.client.auth.onAuthStateChange.listen((data) {
final loggedOut = data.session == null;
final active = activeProject.value;
if (loggedOut && active != null && !active.isLocalOnly) {
final fallback = projects.firstWhereOrNull((p) => p.isLocalOnly);
if (fallback != null) {
setActiveProject(fallback);
} else {
activeProject.value = null;
}
}
});
}
Future<void> _loadProjects() async {
@@ -44,6 +67,11 @@ class ProjectService extends GetxService {
}
Future<void> setActiveProject(Project project) async {
if (!project.isLocalOnly &&
Supabase.instance.client.auth.currentUser == null) {
throw ProjectRequiresLoginException();
}
activeProject.value = project;
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('active_project_id', project.id!);
@@ -164,13 +192,18 @@ class ProjectService extends GetxService {
/// 3. azonnali szinkron, hogy a projekt eddigi adatai lejöjjenek.
Future<Project> joinSharedProject(Map<String, dynamic> sharedRow) async {
final client = Supabase.instance.client;
final user = client.auth.currentUser;
if (user == null) {
throw ProjectRequiresLoginException(
'Közös projekthez csatlakozáshoz be kell jelentkezni.');
}
final projectUuid = sharedRow['id'] as String;
// 1. Tagság (idempotens: ha már tag, nem hiba).
await client.from('terepi_seged_project_members').upsert(
{
'project_id': projectUuid,
'user_id': client.auth.currentUser!.id,
'user_id': user.id,
'role': 'editor',
},
ignoreDuplicates: true,
@@ -193,11 +226,13 @@ class ProjectService extends GetxService {
/// a lokális adat megmarad (archiválható külön).
Future<void> leaveSharedProject(Project project) async {
final client = Supabase.instance.client;
final user = client.auth.currentUser;
if (user == null) return; // nincs bejelentkezve — nincs mit tenni
await client
.from('terepi_seged_project_members')
.delete()
.eq('project_id', project.uuid)
.eq('user_id', client.auth.currentUser!.id);
.eq('user_id', user.id);
}
// ═════════════════════════════════════════════════════════════════