Skip to content

Commit

Permalink
fix(neon_files): do not allow to copy or move a folder into itself.
Browse files Browse the repository at this point in the history
Signed-off-by: Nikolas Rimikis <[email protected]>
  • Loading branch information
Leptopoda committed Dec 29, 2023
1 parent 42e7e65 commit 681529a
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 107 deletions.
61 changes: 56 additions & 5 deletions packages/neon/neon_files/lib/src/blocs/browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,33 @@ import 'package:neon_framework/utils.dart';
import 'package:nextcloud/webdav.dart';
import 'package:rxdart/rxdart.dart';

/// Mode to operate the `FilesBrowserView` in.
enum FilesBrowserMode {
/// Default file browser mode.
///
/// When a file is selected it will be opened or downloaded.
browser,

/// Select directory.
selectDirectory,

/// Do not show file actions.
noActions,
}

sealed class FilesBrowserBloc implements InteractiveBloc {
@internal
factory FilesBrowserBloc(
final FilesOptions options,
final Account account, {
final PathUri? initialPath,
final FilesBrowserMode? mode,
}) =>
_FilesBrowserBloc(
options,
account,
initialPath: initialPath,
mode: mode,
);

void setPath(final PathUri uri);
Expand All @@ -30,27 +46,41 @@ sealed class FilesBrowserBloc implements InteractiveBloc {
BehaviorSubject<PathUri> get uri;

FilesOptions get options;

/// Mode to operate the `FilesBrowserView` in.
FilesBrowserMode get mode;
}

class _FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBloc {
_FilesBrowserBloc(
this.options,
this.account, {
final PathUri? initialPath,
}) {
if (initialPath != null) {
uri.add(initialPath);
this.initialPath,
final FilesBrowserMode? mode,
}) : mode = mode ?? FilesBrowserMode.browser {
final parent = initialPath?.parent;
if (parent != null) {
uri.add(parent);
}

options.showHiddenFilesOption.addListener(refresh);

unawaited(refresh());
}

@override
final FilesOptions options;
final Account account;

@override
final FilesBrowserMode mode;

final PathUri? initialPath;

@override
void dispose() {
options.showHiddenFilesOption.removeListener(refresh);

unawaited(files.close());
unawaited(uri.close());
super.dispose();
Expand Down Expand Up @@ -80,7 +110,28 @@ class _FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBloc {
),
depth: WebDavDepth.one,
),
(final response) => response.toWebDavFiles().sublist(1),
(final response) {
final unwrapped = response.toWebDavFiles().sublist(1);

return unwrapped.where((final file) {
// Do not show files when selecting a directory
if (mode == FilesBrowserMode.selectDirectory && !file.isDirectory) {
return false;
}

// Do not show itself when selecting a directory
if (mode == FilesBrowserMode.selectDirectory && initialPath == file.path) {
return false;
}

// Do not show hidden files unless the option is enabled
if (!options.showHiddenFilesOption.value && file.isHidden) {
return false;
}

return true;
}).toList();
},
emitEmptyCache: true,
);
}
Expand Down
7 changes: 3 additions & 4 deletions packages/neon/neon_files/lib/src/blocs/files.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ sealed class FilesBloc implements InteractiveBloc {

FilesBrowserBloc get browser;

FilesBrowserBloc getNewFilesBrowserBloc({final PathUri? initialUri});
FilesBrowserBloc getNewFilesBrowserBloc({final PathUri? initialUri, final FilesBrowserMode? mode});
}

class _FilesBloc extends InteractiveBloc implements FilesBloc {
Expand Down Expand Up @@ -235,13 +235,12 @@ class _FilesBloc extends InteractiveBloc implements FilesBloc {
}

@override
FilesBrowserBloc getNewFilesBrowserBloc({
final PathUri? initialUri,
}) =>
FilesBrowserBloc getNewFilesBrowserBloc({final PathUri? initialUri, final FilesBrowserMode? mode}) =>
FilesBrowserBloc(
options,
account,
initialPath: initialUri,
mode: mode,
);

void downloadParallelismListener() {
Expand Down
7 changes: 6 additions & 1 deletion packages/neon/neon_files/lib/src/utils/dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:filesize/filesize.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:neon_files/l10n/localizations.dart';
import 'package:neon_files/src/blocs/browser.dart';
import 'package:neon_files/src/blocs/files.dart';
import 'package:neon_files/src/models/file_details.dart';
import 'package:neon_files/src/widgets/dialog.dart';
Expand Down Expand Up @@ -73,7 +74,11 @@ Future<PathUri?> showChooseFolderDialog(final BuildContext context, final FileDe
final bloc = NeonProvider.of<FilesBloc>(context);

final originalUri = details.uri;
final b = bloc.getNewFilesBrowserBloc(initialUri: originalUri);
final b = bloc.getNewFilesBrowserBloc(
initialUri: originalUri,
mode: FilesBrowserMode.selectDirectory,
);

final result = await showDialog<PathUri>(
context: context,
builder: (final context) => FilesChooseFolderDialog(
Expand Down
142 changes: 54 additions & 88 deletions packages/neon/neon_files/lib/src/widgets/browser_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,15 @@ import 'package:neon_framework/sort_box.dart';
import 'package:neon_framework/widgets.dart';
import 'package:nextcloud/webdav.dart';

/// Mode to operate the [FilesBrowserView] in.
enum FilesBrowserMode {
/// Default file browser mode.
///
/// When a file is selected it will be opened or downloaded.
browser,

/// Select directory.
selectDirectory,

/// Don't show file actions.
noActions,
}

class FilesBrowserView extends StatefulWidget {
const FilesBrowserView({
required this.bloc,
required this.filesBloc,
this.mode = FilesBrowserMode.browser,
super.key,
});

final FilesBrowserBloc bloc;
final FilesBloc filesBloc;
final FilesBrowserMode mode;

@override
State<FilesBrowserView> createState() => _FilesBrowserViewState();
Expand All @@ -64,81 +48,63 @@ class _FilesBrowserViewState extends State<FilesBrowserView> {
if (!uriSnapshot.hasData || !tasksSnapshot.hasData) {
return const SizedBox();
}
return ValueListenableBuilder(
valueListenable: widget.bloc.options.showHiddenFilesOption,
builder: (final context, final showHiddenFiles, final _) {
final files = filesSnapshot.data?.where((final file) {
var hideFile = false;
if (widget.mode == FilesBrowserMode.selectDirectory && !file.isDirectory) {
hideFile = true;
}
if (!showHiddenFiles && file.isHidden) {
hideFile = true;
}

return !hideFile;
}).toList();

return BackButtonListener(
onBackButtonPressed: () async {
final parent = uriSnapshot.requireData.parent;
if (parent != null) {
widget.bloc.setPath(parent);
return true;
}
return false;
},
child: SortBoxBuilder<FilesSortProperty, WebDavFile>(
sortBox: filesSortBox,
sortProperty: widget.bloc.options.filesSortPropertyOption,
sortBoxOrder: widget.bloc.options.filesSortBoxOrderOption,
presort: const {
(property: FilesSortProperty.isFolder, order: SortBoxOrder.ascending),
},
input: files,
builder: (final context, final sorted) {
final uploadingTaskTiles = buildUploadTasks(tasksSnapshot.requireData, sorted);

return NeonListView(
scrollKey: 'files-${uriSnapshot.requireData.path}',
itemCount: sorted.length,
itemBuilder: (final context, final index) {
final file = sorted[index];
final matchingTask = tasksSnapshot.requireData.firstWhereOrNull(
(final task) => file.name == task.uri.name && widget.bloc.uri.value == task.uri.parent,
);

final details = matchingTask != null
? FileDetails.fromTask(
task: matchingTask,
file: file,
)
: FileDetails.fromWebDav(
file: file,
);
return BackButtonListener(
onBackButtonPressed: () async {
final parent = uriSnapshot.requireData.parent;
if (parent != null) {
widget.bloc.setPath(parent);
return true;
}
return false;
},
child: SortBoxBuilder<FilesSortProperty, WebDavFile>(
sortBox: filesSortBox,
sortProperty: widget.bloc.options.filesSortPropertyOption,
sortBoxOrder: widget.bloc.options.filesSortBoxOrderOption,
presort: const {
(property: FilesSortProperty.isFolder, order: SortBoxOrder.ascending),
},
input: filesSnapshot.data,
builder: (final context, final sorted) {
final uploadingTaskTiles = buildUploadTasks(tasksSnapshot.requireData, sorted);

return NeonListView(
scrollKey: 'files-${uriSnapshot.requireData.path}',
itemCount: sorted.length,
itemBuilder: (final context, final index) {
final file = sorted[index];
final matchingTask = tasksSnapshot.requireData.firstWhereOrNull(
(final task) => file.name == task.uri.name && widget.bloc.uri.value == task.uri.parent,
);

return FileListTile(
bloc: widget.filesBloc,
browserBloc: widget.bloc,
details: details,
mode: widget.mode,
);
},
isLoading: filesSnapshot.isLoading,
error: filesSnapshot.error,
onRefresh: widget.bloc.refresh,
topScrollingChildren: [
FilesBrowserNavigator(
uri: uriSnapshot.requireData,
bloc: widget.bloc,
),
...uploadingTaskTiles,
],
final details = matchingTask != null
? FileDetails.fromTask(
task: matchingTask,
file: file,
)
: FileDetails.fromWebDav(
file: file,
);

return FileListTile(
bloc: widget.filesBloc,
browserBloc: widget.bloc,
details: details,
);
},
),
);
},
isLoading: filesSnapshot.isLoading,
error: filesSnapshot.error,
onRefresh: widget.bloc.refresh,
topScrollingChildren: [
FilesBrowserNavigator(
uri: uriSnapshot.requireData,
bloc: widget.bloc,
),
...uploadingTaskTiles,
],
);
},
),
);
},
),
Expand Down
7 changes: 3 additions & 4 deletions packages/neon/neon_files/lib/src/widgets/dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,15 @@ class FilesChooseFolderDialog extends StatelessWidget {
const FilesChooseFolderDialog({
required this.bloc,
required this.filesBloc,
required this.originalPath,
this.originalPath,
super.key,
});

final FilesBrowserBloc bloc;
final FilesBloc filesBloc;

/// The initial path to start at.
final PathUri originalPath;
final PathUri? originalPath;

@override
Widget build(final BuildContext context) {
Expand All @@ -244,7 +244,7 @@ class FilesChooseFolderDialog extends StatelessWidget {
),
),
ElevatedButton(
onPressed: originalPath != uriSnapshot.data ? () => Navigator.of(context).pop(uriSnapshot.data) : null,
onPressed: () => Navigator.of(context).pop(uriSnapshot.data),
child: Text(
FilesLocalizations.of(context).folderChoose,
textAlign: TextAlign.end,
Expand All @@ -261,7 +261,6 @@ class FilesChooseFolderDialog extends StatelessWidget {
child: FilesBrowserView(
bloc: bloc,
filesBloc: filesBloc,
mode: FilesBrowserMode.selectDirectory,
),
),
),
Expand Down
7 changes: 2 additions & 5 deletions packages/neon/neon_files/lib/src/widgets/file_list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import 'package:neon_files/src/models/file_details.dart';
import 'package:neon_files/src/utils/dialog.dart';
import 'package:neon_files/src/utils/task.dart';
import 'package:neon_files/src/widgets/actions.dart';
import 'package:neon_files/src/widgets/browser_view.dart';
import 'package:neon_files/src/widgets/file_preview.dart';
import 'package:neon_framework/theme.dart';
import 'package:neon_framework/widgets.dart';
Expand All @@ -17,19 +16,17 @@ class FileListTile extends StatelessWidget {
required this.bloc,
required this.browserBloc,
required this.details,
this.mode = FilesBrowserMode.browser,
super.key,
});

final FilesBloc bloc;
final FilesBrowserBloc browserBloc;
final FileDetails details;
final FilesBrowserMode mode;

Future<void> _onTap(final BuildContext context, final FileDetails details) async {
if (details.isDirectory) {
browserBloc.setPath(details.uri);
} else if (mode == FilesBrowserMode.browser) {
} else if (browserBloc.mode == FilesBrowserMode.browser) {
final sizeWarning = bloc.options.downloadSizeWarning.value;
if (sizeWarning != null && details.size != null && details.size! > sizeWarning) {
final decision = await showDownloadConfirmationDialog(context, sizeWarning, details.size!);
Expand Down Expand Up @@ -73,7 +70,7 @@ class FileListTile extends StatelessWidget {
details: details,
bloc: bloc,
),
trailing: !details.hasTask && mode == FilesBrowserMode.browser
trailing: !details.hasTask && browserBloc.mode == FilesBrowserMode.browser
? FileActions(details: details)
: const SizedBox.square(
dimension: largeIconSize,
Expand Down

0 comments on commit 681529a

Please sign in to comment.