[client] Show pending actions in info view

This commit is contained in:
Abhishek Shroff
2025-01-23 08:43:00 +05:30
parent 7d82ba5199
commit 386d4c9711
2 changed files with 85 additions and 14 deletions

View File

@@ -12,27 +12,27 @@ import 'package:provider/provider.dart';
import '../../libphylum/explorer/explorer_controller.dart';
enum _SyncState {
enum SyncState {
none(Icons.block),
waiting(Icons.cloud_queue),
error(Icons.sync_problem),
syncing(Icons.sync);
const _SyncState(this.icon);
const SyncState(this.icon);
final IconData icon;
factory _SyncState.fromStatus(ActionStatus status) {
factory SyncState.fromStatus(ActionStatus status) {
if (status is ActionStatusReady || status is ActionStatusWaiting) {
return _SyncState.waiting;
return SyncState.waiting;
}
if (status is ActionStatusUploading) {
return _SyncState.syncing;
return SyncState.syncing;
}
if (status is ActionStatusError) {
return _SyncState.error;
return SyncState.error;
}
return _SyncState.none;
return SyncState.none;
}
}
@@ -47,10 +47,10 @@ 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 sync = context.select<PhylumActionQueueState, _SyncState>((state) {
return state.actions.fold(_SyncState.none, (acc, action) {
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);
final actionState = SyncState.fromStatus(action.status);
if (actionState.index > acc.index) return actionState;
}
return acc;
@@ -117,7 +117,7 @@ class ResourceDetailsRow extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (sync != _SyncState.none)
if (sync != SyncState.none)
Padding(
padding: const EdgeInsets.only(right: 6.0),
child: Icon(sync.icon, size: 14),

View File

@@ -1,14 +1,19 @@
import 'dart:async';
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/db/resource_helpers.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/explorer/explorer_controller.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/ui/explorer/resource_details_row.dart';
import 'package:phylum/util/file_size.dart';
import 'package:phylum/util/permissions.dart';
import 'package:phylum/util/time.dart';
import 'package:provider/provider.dart';
import 'package:state_notifier/state_notifier.dart';
import 'resource_icon_extension.dart';
@@ -47,7 +52,7 @@ class ReactiveResourceInfoView extends StatelessWidget {
class ResourceInfoView extends StatelessWidget {
final Resource resource;
const ResourceInfoView({super.key, required this.resource});
ResourceInfoView({required this.resource}) : super(key: ValueKey(resource.id));
@override
Widget build(BuildContext context) {
@@ -59,6 +64,7 @@ class ResourceInfoView extends StatelessWidget {
),
child: Column(
children: [
PendingActionsTile(resourceId: resource.id),
ListTile(
title: const Text('Type'),
subtitle: Text(resource.dir ? 'Folder' : resource.contentType),
@@ -93,10 +99,75 @@ class ResourceInfoView extends StatelessWidget {
}
}
class PendingActionsTile extends StatefulWidget {
final String resourceId;
const PendingActionsTile({super.key, required this.resourceId});
@override
State<PendingActionsTile> createState() => _PendingActionsTileState();
}
class _PendingActionsTileState extends State<PendingActionsTile> {
RemoveListener? _removeListener;
final Map<PhylumAction, RemoveListener> _listeners = {};
final Map<PhylumAction, ActionStatus> _statusMap = {};
@override
void initState() {
super.initState();
_removeListener = context.read<PhylumActionQueue>().addListener((state) {
final actions =
state.actions.where((action) => action is ResourceAction && action.resourceId == widget.resourceId);
for (final action in actions) {
if (!_listeners.containsKey(action)) {
_listeners[action] = action.statusNotifier.addListener((status) {
if (_statusMap[action].runtimeType != status.runtimeType) {
if (status is ActionStatusDone) {
final l = _listeners.remove(action);
_statusMap.remove(action);
if (l != null) {
Future.microtask(() => l());
}
} else {
_statusMap[action] = status;
}
if (mounted) {
setState(() {});
}
}
}, fireImmediately: true);
}
}
}, fireImmediately: true);
}
@override
void dispose() {
_removeListener?.call();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
for (final e in _statusMap.entries)
ListTile(
visualDensity: VisualDensity.compact,
dense: true,
leading: Icon(SyncState.fromStatus(e.value).icon),
title: Text(e.key.description, maxLines: 1),
),
],
);
}
}
class PermissionsTile extends StatefulWidget {
final String resourceId;
PermissionsTile({required this.resourceId}) : super(key: ValueKey(resourceId));
const PermissionsTile({super.key, required this.resourceId});
@override
State<PermissionsTile> createState() => _PermissionsTileState();
@@ -105,8 +176,8 @@ class PermissionsTile extends StatefulWidget {
class _PermissionsTileState extends State<PermissionsTile> {
Permission? myPermission;
Map<String, Permission>? otherPermissions;
StreamSubscription<List<ParentsResult>>? sub;
@override
void initState() {
super.initState();