diff --git a/lib/models/constants.dart b/lib/models/constants.dart new file mode 100644 index 0000000..d047a28 --- /dev/null +++ b/lib/models/constants.dart @@ -0,0 +1,2 @@ +const String kProjectsKey = "projects"; +const String kEditorsKey = "editors"; diff --git a/lib/models/editor.dart b/lib/models/editor.dart index 4577181..8509995 100644 --- a/lib/models/editor.dart +++ b/lib/models/editor.dart @@ -1,13 +1,19 @@ import "package:flutter/material.dart"; import "package:process_run/which.dart"; +import "package:prod/models/rand.dart"; class Editor { + final String id; final String name; final String command; final String commandTemplate; // final Icon icon; - const Editor(this.name, this.command, this.commandTemplate); + const Editor(this.name, this.command, this.commandTemplate, this.id); + + factory Editor.create(String name, String command, String commandTemplate) { + return Editor(name, command, commandTemplate, getRandomString(5)); + } bool validateCommand() { final String? fullPath = whichSync(command); @@ -17,4 +23,29 @@ class Editor { return true; } } + + Map toJson() { + return { + "name": name, + "command": command, + "commandTemplate": commandTemplate, + "id": id, + }; + } + + factory Editor.fromJson(Map data) { + if (!data.containsKey("name") || + !data.containsKey("command") || + !data.containsKey("commandTemplate") || + !data.containsKey("id")) { + print("Found invalid editor config when parsing: $data"); + return Editor("null", "null", "null", "null"); + } + return Editor( + data["name"] as String, + data["command"] as String, + data["commandTemplate"] as String, + data["id"] as String, + ); + } } diff --git a/lib/models/globalModel.dart b/lib/models/globalModel.dart index 3ae6401..2bb0db2 100644 --- a/lib/models/globalModel.dart +++ b/lib/models/globalModel.dart @@ -1,27 +1,75 @@ import 'package:flutter/foundation.dart'; import 'package:prod/models/editor.dart'; import 'package:prod/models/project.dart'; +import "package:shared_preferences/shared_preferences.dart"; +import "package:prod/models/constants.dart"; +// import "package:json_annotation/json_annotation.dart"; +import "dart:convert"; class GlobalModel extends ChangeNotifier { late List projects; late List hoverShow; late List editors; + late SharedPreferences prefs; + bool importedData = false; GlobalModel() { - projects = []; - editors = []; - hoverShow = List.filled(projects.length, false, growable: true); + SharedPreferences.getInstance().then((a) { + print("Loaded sp"); + prefs = a; + if (prefs.containsKey(kProjectsKey)) { + String prjData = prefs.getString(kProjectsKey)!; + List data = jsonDecode(prjData); + projects = []; + for (var d in data) { + projects.add(Project.fromJson(d)); + } + } else { + projects = []; + prefs.setString(kProjectsKey, jsonEncode(projects)); + } + print(projects); + + if (prefs.containsKey(kEditorsKey)) { + String edtData = prefs.getString(kEditorsKey)!; + List data = jsonDecode(edtData); + editors = []; + for (var d in data) { + editors.add(Editor.fromJson(d)); + } + } else { + editors = []; + prefs.setString(kEditorsKey, jsonEncode(editors)); + } + + hoverShow = List.filled(projects.length, false, growable: true); + print("loaded data"); + importedData = true; + notifyListeners(); + }); + } + + void saveProjectState() { + var arst = projects.map((a) => a.toJson()).toList(); + prefs.setString(kProjectsKey, jsonEncode(arst)); + } + + void saveEditorState() { + var arst = editors.map((a) => a.toJson()).toList(); + prefs.setString(kEditorsKey, jsonEncode(arst)); } void addPrj(Project prj) { projects.add(prj); hoverShow.add(false); + saveProjectState(); notifyListeners(); } void delPrj(int index) { projects.removeAt(index); hoverShow.removeAt(index); + saveProjectState(); notifyListeners(); } @@ -50,11 +98,13 @@ class GlobalModel extends ChangeNotifier { void addEdt(Editor edt) { editors.add(edt); + saveEditorState(); notifyListeners(); } void delEdt(int index) { editors.removeAt(index); + saveEditorState(); notifyListeners(); } diff --git a/lib/models/project.dart b/lib/models/project.dart index 67c0ef0..773bfee 100644 --- a/lib/models/project.dart +++ b/lib/models/project.dart @@ -6,7 +6,7 @@ class Project { final String name; final String language; final File path; - final List editors; + final List editors; final bool isGit; final bool enableTerminal; @@ -19,14 +19,15 @@ class Project { this.enableTerminal, ); - factory Project.validated( + factory Project.newValidated( String name, String lang, String path, - List editors, + List editors, bool enableTerminal, ) { final File fpath = File(path); + print(fpath.absolute.path); if (fpath.existsSync()) { print("Project not found!!!"); } else { @@ -38,6 +39,28 @@ class Project { return Project(name, lang, fpath, editors, isGit, enableTerminal); } + Map toJson() { + return { + "name": name, + "language": language, + "path": path.path, + "editors": editors, + "isGit": isGit, + "enableTerminal": enableTerminal, + }; + } + + factory Project.fromJson(Map data) { + return Project( + data["name"] as String, + data["language"] as String, + File(data["path"] as String), + (data["editors"] as List).map((a) => a.toString()).toList(), + data["isGit"] as bool, + data["enableTerminal"] as bool, + ); + } + // bool validatePath(){ // return File // } diff --git a/lib/models/rand.dart b/lib/models/rand.dart new file mode 100644 index 0000000..60e0cdd --- /dev/null +++ b/lib/models/rand.dart @@ -0,0 +1,11 @@ +import 'dart:math'; + +const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; +Random _rnd = Random(); + +String getRandomString(int length) => String.fromCharCodes( + Iterable.generate( + length, + (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length)), + ), +); diff --git a/lib/views/editors.dart b/lib/views/editors.dart index 37ef835..6578451 100644 --- a/lib/views/editors.dart +++ b/lib/views/editors.dart @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; import "package:prod/models/editor.dart"; import "package:prod/models/globalModel.dart"; import "package:prod/widgets/editorCard.dart"; +import "package:prod/widgets/edtFAB.dart"; import "package:provider/provider.dart"; class EditorEditor extends StatelessWidget { @@ -11,7 +12,8 @@ class EditorEditor extends StatelessWidget { Widget build(BuildContext context) { GlobalModel gm = Provider.of(context); return Scaffold( - appBar: AppBar(title: Text("PROject Dashboard")), + appBar: AppBar(title: Text("/ PROject Dashboard / Editors")), + floatingActionButton: EditorFAB(), body: gm.lenEdt > 0 ? LayoutBuilder( builder: (context, constraints) { diff --git a/lib/views/home.dart b/lib/views/home.dart index ce4089f..a4f09fd 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -15,18 +15,21 @@ class HomePage extends StatelessWidget { GlobalModel gm = Provider.of(context); return SafeArea( child: Scaffold( - appBar: AppBar(title: Text("PROject Dashboard")), + appBar: AppBar(title: Text("/ PROject Dashboard")), drawer: ProdDrawer(), floatingActionButton: ProjFAB(), body: Padding( padding: const EdgeInsets.all(8.0), - child: gm.lenPrj > 0 + child: !gm.importedData + ? Container() + : gm.lenPrj > 0 ? LayoutBuilder( builder: (context, constraints) { - int cols = (constraints.maxWidth / 200).floor(); + int cols = (constraints.maxWidth / 300).floor(); return GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: cols, + childAspectRatio: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, ), diff --git a/lib/widgets/editorCard.dart b/lib/widgets/editorCard.dart index 2b6f0ab..d552d0b 100644 --- a/lib/widgets/editorCard.dart +++ b/lib/widgets/editorCard.dart @@ -15,13 +15,16 @@ class EditorCard extends StatelessWidget { GlobalModel gm = Provider.of(context); final Editor edt = gm.nthEdt(id); return YaruBanner( - onTap: () {}, + onTap: () { + gm.delEdt(id); + }, child: Center( child: Column( mainAxisAlignment: .start, crossAxisAlignment: .start, children: [ Text("${edt.name}", style: TextStyle(fontSize: 30)), + Text("${edt.command}"), Text("${edt.commandTemplate}"), ], ), diff --git a/lib/widgets/edtFAB.dart b/lib/widgets/edtFAB.dart index e4b2d00..e1e35a8 100644 --- a/lib/widgets/edtFAB.dart +++ b/lib/widgets/edtFAB.dart @@ -1,5 +1,7 @@ import "package:flutter/material.dart"; import "package:prod/models/editor.dart"; +import "package:prod/models/globalModel.dart"; +import "package:provider/provider.dart"; class EditorFAB extends StatelessWidget { const EditorFAB({super.key}); @@ -17,6 +19,68 @@ class EditorFAB extends StatelessWidget { // true, // ), // ); + TextEditingController nameController = TextEditingController(); + TextEditingController commandController = TextEditingController(); + TextEditingController commandTemplateController = + TextEditingController(); + showDialog( + context: context, + builder: (context) => SimpleDialog( + title: Text("Add Editor"), + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + autofocus: true, + controller: nameController, + decoration: InputDecoration(labelText: "Editor Name"), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: commandController, + decoration: InputDecoration(labelText: "Command"), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: commandTemplateController, + decoration: InputDecoration(labelText: "Command Template"), + ), + ), + Padding( + child: const Text("Example: zed -n \$path"), + padding: const EdgeInsets.all(9.0), + ), + + Row( + mainAxisAlignment: .end, + children: [ + TextButton( + child: Text("Cancel"), + onPressed: () => Navigator.pop(context), + ), + TextButton( + child: Text("Add"), + onPressed: () { + Provider.of(context, listen: false).addEdt( + Editor.create( + nameController.text, + commandController.text, + commandTemplateController.text, + ), + ); + + Navigator.pop(context); + }, + ), + ], + ), + ], + ), + ); }, child: Icon(Icons.add), ); diff --git a/lib/widgets/prjFAB.dart b/lib/widgets/prjFAB.dart index 5679873..1fb0193 100644 --- a/lib/widgets/prjFAB.dart +++ b/lib/widgets/prjFAB.dart @@ -8,15 +8,58 @@ class ProjFAB extends StatelessWidget { @override Widget build(BuildContext context) { + TextEditingController nameController = TextEditingController(); + TextEditingController locationController = TextEditingController(); return FloatingActionButton( onPressed: () { - Provider.of(context, listen: false).addPrj( - Project.validated( - "Kimi", - "Rust", - "/home/arrow/Gitted/cowin", - [], - true, + showDialog( + context: context, + builder: (context) => SimpleDialog( + title: Text("Add Project"), + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + autofocus: true, + controller: nameController, + decoration: InputDecoration(labelText: "Project Name"), + ), + ), + + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: locationController, + decoration: InputDecoration(labelText: "Path"), + ), + ), + + Row( + mainAxisAlignment: .end, + children: [ + TextButton( + child: Text("Cancel"), + onPressed: () => Navigator.pop(context), + ), + TextButton( + child: Text("Add"), + onPressed: () { + Provider.of(context, listen: false).addPrj( + Project.newValidated( + nameController.text, + "Rust", + locationController.text, + [], + true, + ), + ); + + Navigator.pop(context); + }, + ), + ], + ), + ], ), ); }, diff --git a/lib/widgets/projectCard.dart b/lib/widgets/projectCard.dart index 17feabd..14260a9 100644 --- a/lib/widgets/projectCard.dart +++ b/lib/widgets/projectCard.dart @@ -1,4 +1,5 @@ import "package:flutter/material.dart"; +import "package:prod/models/editor.dart"; import "package:prod/models/globalModel.dart"; import "package:prod/models/project.dart"; import "package:provider/provider.dart"; @@ -16,32 +17,36 @@ class ProjectCard extends StatelessWidget { return YaruBanner( padding: .only( left: kYaruPagePadding, - top: kYaruPagePadding, + top: kYaruPagePadding * 0.5, bottom: kYaruPagePadding, right: kYaruPagePadding, ), onHover: (st) => gm.setHoverShow(id, st), onTap: () { var shell = Shell(); - shell.run("konsole -e nvim ${prj.path.path}"); + Editor edt1 = gm.editors[0]; + String comm = edt1.commandTemplate.replaceAll("\$path", prj.path.path); + shell.run(comm); }, child: Center( child: Column( mainAxisAlignment: .start, crossAxisAlignment: .start, children: [ - Row( - mainAxisAlignment: .spaceBetween, - children: [ - Text("${prj.name}", style: TextStyle(fontSize: 30)), - gm.getHoverShow(id) - ? ElevatedButton( - child: Icon(Icons.close), - onPressed: () => print("pressed delete"), - ) - : Container(), - ], + Text( + "${prj.name}", + style: TextStyle(fontSize: 30), + overflow: .ellipsis, ), + // Row( + // mainAxisAlignment: .spaceBetween, + // children: [ + // Flexible( + // child: Container( + // ), + // ), + // ], + // ), Row( spacing: 10, children: [ @@ -50,6 +55,15 @@ class ProjectCard extends StatelessWidget { ], ), gm.getHoverShow(id) ? Text("${prj.path.path}") : Container(), + gm.getHoverShow(id) + ? IconButton( + icon: Icon(Icons.close), + onPressed: () => gm.delPrj(id), + style: IconButton.styleFrom( + overlayColor: Color(0xffff0000), + ), + ) + : Container(), ], ), ), diff --git a/lib/widgets/simpleListTile.dart b/lib/widgets/simpleListTile.dart index 930d630..02fc1f0 100644 --- a/lib/widgets/simpleListTile.dart +++ b/lib/widgets/simpleListTile.dart @@ -14,6 +14,7 @@ class SListTile extends StatelessWidget { leading: Icon(icon), title: Text(title), onTap: () { + Navigator.pop(context); Navigator.pushNamed(context, route); }, ); diff --git a/pubspec.yaml b/pubspec.yaml index 01e4c16..ebe9910 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,8 @@ dependencies: yaru: ^9.0.1 path: ^1.9.1 provider: ^6.1.5+1 + shared_preferences: ^2.5.4 + json_annotation: ^4.10.0 dev_dependencies: flutter_test: