188 lines
6.2 KiB
Dart
188 lines
6.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
|
|
|
import '../../../routing/app_routes.dart';
|
|
import '../../../share_intent/pending_share.dart';
|
|
import '../../../share_intent/share_intent_listener.dart';
|
|
import '../../../state/app/infrastructure/loadable_state/loadable_state.dart';
|
|
import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart';
|
|
import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart';
|
|
import '../../../state/app/modules/files/bloc/files_bloc.dart';
|
|
import '../../../state/app/modules/files/bloc/files_state.dart';
|
|
import '../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
|
import '../../../widget/placeholder_view.dart';
|
|
import '../files/data/sort_options.dart';
|
|
import '../files/files_upload_dialog.dart';
|
|
import '../files/widgets/add_file_menu.dart';
|
|
import '../files/widgets/files_sort_actions.dart';
|
|
|
|
class ShareFolderPicker extends StatelessWidget {
|
|
final PendingShare share;
|
|
|
|
const ShareFolderPicker({super.key, required this.share});
|
|
|
|
@override
|
|
Widget build(BuildContext context) =>
|
|
BlocModule<FilesBloc, LoadableState<FilesState>>(
|
|
create: (_) => FilesBloc(),
|
|
child: (context, _, _) => _ShareFolderPickerView(share: share),
|
|
);
|
|
}
|
|
|
|
class _ShareFolderPickerView extends StatefulWidget {
|
|
final PendingShare share;
|
|
const _ShareFolderPickerView({required this.share});
|
|
|
|
@override
|
|
State<_ShareFolderPickerView> createState() => _ShareFolderPickerViewState();
|
|
}
|
|
|
|
class _ShareFolderPickerViewState extends State<_ShareFolderPickerView> {
|
|
late final SettingsCubit _settings;
|
|
late SortOption _currentSort;
|
|
late bool _ascending;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_settings = context.read<SettingsCubit>();
|
|
_currentSort = _settings.val().fileSettings.sortBy;
|
|
_ascending = _settings.val().fileSettings.ascending;
|
|
}
|
|
|
|
void _enter(FilesBloc bloc, List<String> currentPath, String folderName) {
|
|
bloc.setPath([...currentPath, folderName]);
|
|
}
|
|
|
|
void _goUp(FilesBloc bloc, List<String> currentPath) {
|
|
if (currentPath.isEmpty) return;
|
|
bloc.setPath(currentPath.sublist(0, currentPath.length - 1));
|
|
}
|
|
|
|
Future<void> _uploadHere(List<String> currentPath) async {
|
|
await pushScreen(
|
|
context,
|
|
withNavBar: false,
|
|
screen: FilesUploadDialog(
|
|
filePaths: widget.share.filePaths,
|
|
remotePath: currentPath.join('/'),
|
|
onUploadFinished: (_) => _afterUploaded(currentPath),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _afterUploaded(List<String> targetPath) {
|
|
ShareIntentListener.instance.clear();
|
|
if (!mounted) return;
|
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
|
AppRoutes.openFolder(context, targetPath);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final bloc = context.read<FilesBloc>();
|
|
return BlocBuilder<FilesBloc, LoadableState<FilesState>>(
|
|
buildWhen: (a, b) => a.data?.currentPath != b.data?.currentPath,
|
|
builder: (_, outerState) {
|
|
final currentPath = outerState.data?.currentPath ?? const [];
|
|
return PopScope(
|
|
// Back navigates one level up while inside a sub-folder; only the
|
|
// root level actually closes the picker. Matches the standard
|
|
// files-app pattern and keeps the AppBar back-arrow consistent
|
|
// with the chat picker.
|
|
canPop: currentPath.isEmpty,
|
|
onPopInvokedWithResult: (didPop, _) {
|
|
if (didPop) return;
|
|
if (currentPath.isNotEmpty) _goUp(bloc, currentPath);
|
|
},
|
|
child: _buildScaffold(context, bloc, currentPath),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildScaffold(
|
|
BuildContext context,
|
|
FilesBloc bloc,
|
|
List<String> currentPath,
|
|
) => Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(
|
|
currentPath.isEmpty ? 'Ordner wählen' : '/${currentPath.join('/')}',
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.create_new_folder_outlined),
|
|
tooltip: 'Ordner erstellen',
|
|
onPressed: () => showCreateFolderDialog(context, bloc),
|
|
),
|
|
FilesSortActions(
|
|
currentSort: _currentSort,
|
|
ascending: _ascending,
|
|
onDirectionChanged: (e) {
|
|
setState(() {
|
|
_ascending = e;
|
|
_settings.val(write: true).fileSettings.ascending = e;
|
|
});
|
|
},
|
|
onSortChanged: (e) {
|
|
setState(() {
|
|
_currentSort = e;
|
|
_settings.val(write: true).fileSettings.sortBy = e;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
heroTag: 'shareUploadHere',
|
|
onPressed: () => _uploadHere(currentPath),
|
|
icon: const Icon(Icons.upload),
|
|
label: const Text('Hier hochladen'),
|
|
),
|
|
body: LoadableStateConsumer<FilesBloc, FilesState>(
|
|
isReady: (state) => state.listing != null,
|
|
child: (state, _) {
|
|
final listing = state.listing!;
|
|
final entries = listing.sortBy(
|
|
sortOption: _currentSort,
|
|
foldersToTop: _settings.val().fileSettings.sortFoldersToTop,
|
|
reversed: _ascending,
|
|
);
|
|
|
|
if (entries.isEmpty) {
|
|
return PlaceholderView(
|
|
icon: Icons.folder_off_rounded,
|
|
text: state.currentPath.isEmpty
|
|
? 'Leer. Du kannst hier direkt hochladen.'
|
|
: 'Ordner ist leer. Du kannst hier hochladen.',
|
|
);
|
|
}
|
|
|
|
return ListView.builder(
|
|
padding: EdgeInsets.zero,
|
|
itemCount: entries.length,
|
|
itemBuilder: (context, i) {
|
|
final entry = entries[i];
|
|
if (entry.isDirectory) {
|
|
return ListTile(
|
|
leading: const Icon(Icons.folder_outlined),
|
|
title: Text(entry.name),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () => _enter(bloc, state.currentPath, entry.name),
|
|
);
|
|
}
|
|
return ListTile(
|
|
enabled: false,
|
|
leading: const Icon(Icons.description_outlined),
|
|
title: Text(entry.name),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|