[client] Show sync errors in list

This commit is contained in:
Abhishek Shroff
2025-01-23 00:03:44 +05:30
parent efc5918b9c
commit 576ebdf404
3 changed files with 114 additions and 72 deletions

View File

@@ -15,6 +15,8 @@ import 'package:phylum/libphylum/requests/resource_detail_request.dart';
import 'package:phylum/libphylum/responses/responses.dart';
import 'package:phylum/libphylum/util/uuid.dart';
const performLocalNameCheck = true;
class ResourceRepository {
final PhylumAccount account;
@@ -33,27 +35,29 @@ class ResourceRepository {
required NameConflictResolution conflictResolution,
String? id,
}) async {
var existing = await account.db.getResourcesByName(parent: parent, name: name);
String? deletedId;
id ??= generateUuid();
String localName = name;
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
case nameConflictDelete:
conflictResolution = nameConflictDelete;
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
String? deletedId;
if (performLocalNameCheck) {
var existing = await account.db.getResourcesByName(parent: parent, name: name);
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
case nameConflictDelete:
conflictResolution = nameConflictDelete;
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
}
}
}
id ??= generateUuid();
await account.addAction(ResourceMkdirAction(
parent: parent,
resourceName: name,
@@ -73,25 +77,27 @@ class ResourceRepository {
required String name,
NameConflictResolution conflictResolution = nameConflictError,
}) async {
var existing = await account.db.getResourcesByName(parent: parent, name: name);
String localName = name;
String id = generateUuid();
String localName = name;
String? deletedId;
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
id = existing.first.id;
break;
case nameConflictDelete:
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
if (performLocalNameCheck) {
var existing = await account.db.getResourcesByName(parent: parent, name: name);
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
id = existing.first.id;
break;
case nameConflictDelete:
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
}
}
}
final f = File(path);
@@ -118,23 +124,25 @@ class ResourceRepository {
required String parent,
required NameConflictResolution conflictResolution,
}) async {
var existing = await account.db.getResourcesByName(parent: parent, name: name);
String? deletedId;
String localName = name;
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
case nameConflictDelete:
conflictResolution = nameConflictDelete;
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
String? deletedId;
if (performLocalNameCheck) {
var existing = await account.db.getResourcesByName(parent: parent, name: name);
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
case nameConflictDelete:
conflictResolution = nameConflictDelete;
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
}
}
}
await account.addAction(ResourceMoveAction(
@@ -162,23 +170,25 @@ class ResourceRepository {
}
String id = generateUuid();
var existing = await account.db.getResourcesByName(parent: parent, name: name);
String? deletedId;
String localName = name;
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
case nameConflictDelete:
conflictResolution = nameConflictDelete;
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
if (performLocalNameCheck) {
var existing = await account.db.getResourcesByName(parent: parent, name: name);
if (existing.isNotEmpty) {
switch (conflictResolution) {
case nameConflictError:
throw NameConflictException(existing.first);
case nameConflictRename:
localName = await _createUniqueName(existing, parent, name);
break;
case nameConflictOverwrite:
case nameConflictDelete:
conflictResolution = nameConflictDelete;
deletedId = existing.first.id;
break;
default:
throw UnsupportedError('Unsupported conflict resolution');
}
}
}
await account.addAction(ResourceCopyAction(

View File

@@ -41,7 +41,7 @@ class PhylumApiErrorResponse extends ApiResponse<PhylumAccount> {
final String message;
@override
String get description => 'Api Error: $message';
String get description => message;
@override
bool get success => false;

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/actions/action_resource.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
@@ -9,6 +10,30 @@ import 'package:provider/provider.dart';
import '../../libphylum/explorer/explorer_controller.dart';
enum _SyncState {
none(Icons.block),
waiting(Icons.cloud_queue),
error(Icons.sync_problem),
syncing(Icons.sync);
const _SyncState(this.icon);
final IconData icon;
factory _SyncState.fromStatus(ActionStatus status) {
if (status is ActionStatusReady || status is ActionStatusWaiting) {
return _SyncState.waiting;
}
if (status is ActionStatusUploading) {
return _SyncState.syncing;
}
if (status is ActionStatusError) {
return _SyncState.error;
}
return _SyncState.none;
}
}
class ResourceDetailsRow extends StatelessWidget {
final Resource resource;
final bool dropTargetActive;
@@ -20,8 +45,15 @@ class ResourceDetailsRow extends StatelessWidget {
final showBorder = context.select<ExplorerState, bool>((state) => state.focusId == resource.id && state.showFocus);
final highlight = context.select<ExplorerState, bool>((state) => state.isSelected(resource.id));
final dim = context.select<ExplorerState, bool>((state) => state.isSelected(resource.id) && state.dragging);
final hasChanges = context.select<PhylumActionQueueState, bool>(
(state) => state.actions.any((action) => action is ResourceAction && action.resourceId == resource.id));
final sync = context.select<PhylumActionQueueState, _SyncState>((state) {
return state.actions.fold(_SyncState.none, (acc, action) {
if (action is ResourceAction && action.resourceId == resource.id) {
final actionState = _SyncState.fromStatus(action.status);
if (actionState.index > acc.index) return actionState;
}
return acc;
});
});
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final border = dropTargetActive
@@ -83,10 +115,10 @@ class ResourceDetailsRow extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (hasChanges)
const Padding(
padding: EdgeInsets.only(right: 6.0),
child: Icon(Icons.sync, size: 14),
if (sync != _SyncState.none)
Padding(
padding: const EdgeInsets.only(right: 6.0),
child: Icon(sync.icon, size: 14),
),
if (resource.permissions != null)
const Padding(