You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
8.4 KiB
235 lines
8.4 KiB
import 'dart:io'; |
|
|
|
import 'package:archive/archive.dart'; |
|
import 'package:file_picker/file_picker.dart'; |
|
import 'package:flutter/material.dart'; |
|
import 'package:flutter_hooks/flutter_hooks.dart'; |
|
import 'package:logic_circuits_simulator/state/projects.dart'; |
|
import 'package:logic_circuits_simulator/utils/provider_hook.dart'; |
|
import 'package:provider/provider.dart'; |
|
|
|
class NewProjectDialog extends HookWidget { |
|
const NewProjectDialog({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
final projectsState = useProvider<ProjectsState>(); |
|
final newDialogNameController = useTextEditingController(); |
|
useListenable(newDialogNameController); |
|
|
|
final newProjectAction = useMemoized(() { |
|
if (newDialogNameController.text.isEmpty) return null; |
|
return () { |
|
projectsState.newProject(newDialogNameController.text.trim()); |
|
Navigator.pop(context); |
|
}; |
|
}, [newDialogNameController.text]); |
|
|
|
final importProjectAction = useMemoized(() { |
|
return () async { |
|
final projectsState = Provider.of<ProjectsState>(context, listen: false); |
|
final msg = ScaffoldMessenger.of(context); |
|
final nav = Navigator.of(context); |
|
|
|
try { |
|
final inputFiles = await FilePicker.platform.pickFiles( |
|
dialogTitle: 'Import Project', |
|
allowedExtensions: Platform.isLinux || Platform.isWindows ? ['lcsproj'] : null, |
|
lockParentWindow: true, |
|
type: Platform.isLinux || Platform.isWindows ? FileType.custom : FileType.any, |
|
allowMultiple: false, |
|
withData: true, |
|
); |
|
|
|
if (inputFiles == null) { |
|
return; |
|
} |
|
|
|
final inputFile = inputFiles.files.first; |
|
|
|
final dec = ZipDecoder(); |
|
final archive = dec.decodeBytes(inputFile.bytes!); |
|
|
|
// bool editAfter = false; |
|
final result = await projectsState.importProject( |
|
archive: archive, |
|
onConflictingId: () async { |
|
final response = await showDialog<bool>( |
|
barrierDismissible: false, |
|
context: context, |
|
builder: (context) { |
|
return AlertDialog( |
|
title: const Text('Conflicting ID'), |
|
content: const Text('You already have a project with the same ID as the one you are importing.\n\nAre you sure you want to replace the current project with the imported one?'), |
|
actions: [ |
|
TextButton( |
|
onPressed: () { |
|
Navigator.of(context).pop(false); |
|
}, |
|
child: const Text('Cancel'), |
|
), |
|
Theme( |
|
data: ThemeData( |
|
brightness: Theme.of(context).brightness, |
|
primarySwatch: Colors.red, |
|
), |
|
child: ElevatedButton( |
|
onPressed: () { |
|
Navigator.of(context).pop(true); |
|
}, |
|
child: const Text('Overwrite and import'), |
|
), |
|
), |
|
], |
|
); |
|
}, |
|
); |
|
|
|
// Allow conflicting id ONLY if allow button is explicitly tapped |
|
return response == true; |
|
}, |
|
onConflictingName: (String name) async { |
|
final response = await showDialog<bool>( |
|
context: context, |
|
builder: (context) { |
|
return AlertDialog( |
|
title: const Text('Conflicting name'), |
|
content: Text('You already have a project named $name.\n\nYou may import the project and have both coexist, but confusion may arise.'), |
|
actions: [ |
|
TextButton( |
|
onPressed: () { |
|
Navigator.of(context).pop(false); |
|
}, |
|
child: const Text('Cancel'), |
|
), |
|
ElevatedButton( |
|
onPressed: () { |
|
Navigator.of(context).pop(true); |
|
}, |
|
child: const Text('Import anyway'), |
|
), |
|
], |
|
); |
|
}, |
|
); |
|
|
|
// Allow conflicting name UNLESS deny button is explicitly tapped |
|
return response != false; |
|
}, |
|
); |
|
|
|
if (result != null) { |
|
nav.pop(); |
|
// if (!editAfter) { |
|
msg.showSnackBar( |
|
SnackBar( |
|
content: Text('Project ${result.projectName} imported'), |
|
), |
|
); |
|
// } |
|
// else { |
|
// // TODO: Allow editing project name in the future |
|
// } |
|
} |
|
} |
|
catch (e) { |
|
nav.pop(); |
|
msg.showSnackBar( |
|
SnackBar( |
|
content: Text('Failed to import project: $e'), |
|
duration: const Duration(seconds: 10), |
|
), |
|
); |
|
} |
|
}; |
|
}); |
|
|
|
return Dialog( |
|
child: SingleChildScrollView( |
|
child: Padding( |
|
padding: const EdgeInsets.all(8.0), |
|
child: IntrinsicWidth( |
|
child: Column( |
|
crossAxisAlignment: CrossAxisAlignment.stretch, |
|
mainAxisSize: MainAxisSize.min, |
|
children: [ |
|
Align( |
|
alignment: Alignment.centerRight, |
|
child: IconButton( |
|
icon: const Icon(Icons.close), |
|
tooltip: 'Close', |
|
onPressed: () { |
|
Navigator.of(context).pop(); |
|
}, |
|
), |
|
), |
|
Center( |
|
child: Padding( |
|
padding: const EdgeInsets.all(8.0), |
|
child: OutlinedButton.icon( |
|
onPressed: importProjectAction, |
|
icon: const Icon(Icons.download), |
|
label: const Text('Import Project'), |
|
), |
|
), |
|
), |
|
Row( |
|
children: [ |
|
const Expanded( |
|
child: Padding( |
|
padding: EdgeInsets.all(8.0), |
|
child: Divider(), |
|
), |
|
), |
|
Padding( |
|
padding: const EdgeInsets.symmetric(vertical: 8.0), |
|
child: Text( |
|
'OR', |
|
style: Theme.of(context).textTheme.caption, |
|
), |
|
), |
|
const Expanded( |
|
child: Padding( |
|
padding: EdgeInsets.all(8.0), |
|
child: Divider(), |
|
), |
|
), |
|
], |
|
), |
|
Padding( |
|
padding: const EdgeInsets.all(8.0), |
|
child: Text( |
|
'New Project', |
|
style: Theme.of(context).textTheme.headline6, |
|
textAlign: TextAlign.center, |
|
), |
|
), |
|
Padding( |
|
padding: const EdgeInsets.all(8.0), |
|
child: Container( |
|
constraints: const BoxConstraints(minWidth: 300), |
|
child: TextField( |
|
controller: newDialogNameController, |
|
decoration: InputDecoration( |
|
border: const OutlineInputBorder(), |
|
labelText: 'Project name', |
|
suffixIcon: IconButton( |
|
icon: const Icon(Icons.done), |
|
onPressed: newProjectAction, |
|
), |
|
errorText: projectsState.projects.map((p) => p.projectName).contains(newDialogNameController.text.trim()) |
|
? 'A project with the same name already exists' |
|
: null, |
|
), |
|
onSubmitted: newProjectAction == null ? null : (_) => newProjectAction(), |
|
), |
|
), |
|
), |
|
], |
|
), |
|
), |
|
), |
|
), |
|
); |
|
} |
|
}
|
|
|