diff --git a/lib/apps/file/editor.dart b/lib/apps/file/editor.dart new file mode 100644 index 0000000..4a948e5 --- /dev/null +++ b/lib/apps/file/editor.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_quill/flutter_quill.dart' hide Text; + +import 'package:esse/utils/adaptive.dart'; +import 'package:esse/l10n/localizations.dart'; +import 'package:esse/provider.dart'; + +import 'package:esse/apps/file/models.dart'; +import 'package:esse/apps/file/list.dart'; + +class EditorPage extends StatefulWidget { + final FilePath path; + const EditorPage({Key? key, required this.path}) : super(key: key); + + @override + _EditorPageState createState() => _EditorPageState(); +} + +class _EditorPageState extends State { + QuillController _controller = QuillController.basic(); + + @override + initState() { + print("File editor initState..."); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final color = Theme.of(context).colorScheme; + final lang = AppLocalizations.of(context); + final isDesktop = isDisplayDesktop(context); + + + return Scaffold( + appBar: AppBar( + leading: isDesktop ? IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + final w = FilesList(path: widget.path); + context.read().updateActivedWidget(w); + } + ) : null, + centerTitle: true, + title: TextButton(child: Text('New Document 0'), + onPressed: () {} + ), + ), + body: Column( + children: [ + Container( + width: double.infinity, + decoration: BoxDecoration(color: color.secondary), + padding: const EdgeInsets.all(10.0), + child: QuillToolbar.basic( + controller: _controller, + showAlignmentButtons: false, + ) + ), + Expanded( + child: Container( + padding: const EdgeInsets.all(10.0), + child: QuillEditor.basic( + controller: _controller, + readOnly: false, // true for view only mode + ), + ), + ) + ], + ) + ); + } +} diff --git a/lib/apps/file/list.dart b/lib/apps/file/list.dart index 818c678..cee5ba7 100644 --- a/lib/apps/file/list.dart +++ b/lib/apps/file/list.dart @@ -1,16 +1,17 @@ import 'package:flutter/material.dart'; -//import 'package:provider/provider.dart'; +import 'package:provider/provider.dart'; import 'package:esse/utils/adaptive.dart'; import 'package:esse/utils/file_image.dart'; import 'package:esse/l10n/localizations.dart'; -//import 'package:esse/provider.dart'; +import 'package:esse/provider.dart'; import 'package:esse/apps/file/models.dart'; +import 'package:esse/apps/file/editor.dart'; class FilesList extends StatefulWidget { - final RootDirectory root; - const FilesList({Key? key, required this.root}) : super(key: key); + FilePath path; + FilesList({Key? key, required this.path}) : super(key: key); @override _FilesListState createState() => _FilesListState(); @@ -20,20 +21,43 @@ class _FilesListState extends State { @override void initState() { super.initState(); - loadRecents(); + _loadDirectory(); } - loadRecents() { + _loadDirectory() { // } - changeItem(String name, bool isDesktop) { + _nextDirectory(FilePath path) { setState(() { - // chooseIndex = index; - // loadFolder(isDesktop, index); + widget.path = path; + _loadDirectory(); }); } + List _pathWidget(String root) { + List widgets = [ + InkWell( + onTap: () => _nextDirectory(FilePath.root(widget.path.root)), + child: Text(root, + style: TextStyle(fontSize: 14.0, color: Color(0xFFADB0BB))) + ) + ]; + + final n = widget.path.path.length; + for (int i = 0; i < n; i++) { + final name = widget.path.path[i]; + final current_path = List.generate(i+1, (i) => widget.path.path[i]); + widgets.add(InkWell( + onTap: () => _nextDirectory(FilePath(widget.path.root, current_path)), + child: Text('/'+FilePath.directoryName(name), + style: TextStyle(fontSize: 14.0, color: Color(0xFFADB0BB))) + )); + } + + return widgets; + } + @override Widget build(BuildContext context) { final color = Theme.of(context).colorScheme; @@ -41,35 +65,112 @@ class _FilesListState extends State { final isDesktop = isDisplayDesktop(context); return Scaffold( - appBar: AppBar(title: Text(lang.dataCenter + ' (${lang.wip})')), + appBar: AppBar( + title: Text(lang.dataCenter + ' (${lang.wip})'), + actions: [ + widget.path.root == RootDirectory.Trash + ? IconButton( + icon: Icon(Icons.delete_forever, color: Colors.red), + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(lang.trashClear), + actions: [ + TextButton( + child: Text(lang.cancel), + onPressed: () => Navigator.pop(context), + ), + TextButton( + child: Text(lang.ok), + onPressed: () { + Navigator.pop(context); + //rpc.send('trash-clear', []); + }, + ), + ] + ); + }, + ) + ) + : PopupMenuButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15) + ), + color: const Color(0xFFEDEDED), + child: SizedBox(width: 40.0, child: Icon(Icons.add_rounded, color: color.primary)), + onSelected: (int value) { + if (value == 0) { + final editor = EditorPage(path: widget.path); + if (!isDesktop) { + Navigator.push(context, MaterialPageRoute(builder: (_) => editor)); + } else { + context.read().updateActivedWidget(editor); + } + } else if (value == 1) { + // new folder + } else if (value == 2) { + // upload file + } + }, + itemBuilder: (context) { + return >[ + PopupMenuItem(value: 0, + child: Text(lang.newPost, style: TextStyle(color: Colors.black, fontSize: 16.0)), + ), + PopupMenuItem(value: 1, + child: Text(lang.newFolder, style: TextStyle(color: Colors.black, fontSize: 16.0)), + ), + PopupMenuItem(value: 2, + child: Text(lang.uploadFile, style: TextStyle(color: Colors.black, fontSize: 16.0)), + ), + ]; + } + ), + const SizedBox(width: 10.0), + ] + ), body: Padding( padding: const EdgeInsets.all(10.0), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Align( - alignment: Alignment.centerLeft, - child: Text('/' + widget.root.params(lang)[1], style: Theme.of(context).textTheme.caption) - ), + Row(children: this._pathWidget('/' + widget.path.root.params(lang)[1])), + const SizedBox(height: 4.0), Expanded( child: GridView.extent( - maxCrossAxisExtent: 80.0, + maxCrossAxisExtent: 75.0, childAspectRatio: 0.8, children: [ - FileItem(name: 'myworks.dir'), - FileItem(name: 'ESSE-infos-public.dir'), - FileItem(name: 'personal.dir'), - FileItem(name: 'others.dir'), - FileItem(name: 'logo.jpg'), - FileItem(name: 'cat.png'), - FileItem(name: 'what-is-esse_en.doc'), - FileItem(name: '20210101-customers.xls'), - FileItem(name: 'product.pdf'), - FileItem(name: 'deck.ppt'), - FileItem(name: 'coder.md'), - FileItem(name: 'how-to-live-in-happy.mp4'), - FileItem(name: 'something_important'), - FileItem(name: 'car.json'), + FileItem( + path: FilePath.next(widget.path, 'myworks.dir'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'ESSE-infos-public.dir'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'personal.dir'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'others.dir'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'logo.jpg'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'cat.png'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'what-is-esse_en.doc'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, '20210101-customers.xls'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'product.pdf'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'deck.ppt'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'coder.md'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'how-to-live-in-happy.mp4'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'something_important'), + directory: this._nextDirectory), + FileItem(path: FilePath.next(widget.path, 'car.json'), + directory: this._nextDirectory), ], ), ) @@ -81,155 +182,36 @@ class _FilesListState extends State { } class FileItem extends StatelessWidget { - final String name; - const FileItem({Key? key, required this.name}) : super(key: key); - - String remove_dir(String name) { - if (name.endsWith('.dir')) { - final i = name.lastIndexOf('.'); - return name.substring(0, i); - } - return name; - } - - @override - Widget build(BuildContext context) { - final trueName = remove_dir(name); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - height: 60.0, - width: 60.0, - child: fileIcon(name, 48.0), - ), - Tooltip( - message: trueName, - child: Text(trueName, - style: TextStyle(fontSize: 14.0), maxLines: 1, overflow: TextOverflow.ellipsis), - ) - ] - ); - } -} - -class FilePage extends StatelessWidget { - final String title; - const FilePage({Key? key, required this.title}) : super(key: key); + final FilePath path; + final Function directory; + const FileItem({Key? key, required this.path, required this.directory}) : super(key: key); @override Widget build(BuildContext context) { - final isDesktop = isDisplayDesktop(context); - final color = Theme.of(context).colorScheme; - final lang = AppLocalizations.of(context); - - return Scaffold( - body: Padding( - padding: const EdgeInsets.all(10.0), + final trueName = this.path.name(); + return InkWell( + onTap: () { + if (this.path.isDirectory()) { + this.directory(this.path); + } + }, + child: Container( + padding: const EdgeInsets.all(4.0), child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.center, - children: [ - Row( - children: [ - if (!isDesktop) - GestureDetector( - onTap: () => Navigator.pop(context), - child: Container(width: 20.0, child: Icon(Icons.arrow_back, color: color.primary)), - ), - const SizedBox(width: 15.0), - Expanded(child: Text(title, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0))), - PopupMenuButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15) - ), - color: const Color(0xFFEDEDED), - child: Icon(Icons.add_rounded, color: color.primary), - onSelected: (int value) { - if (value == 0) { - // new post - } else if (value == 1) { - // new folder - } else if (value == 2) { - // upload file - } - }, - itemBuilder: (context) { - return >[ - PopupMenuItem(value: 0, - child: Text('New Post', style: TextStyle(color: Colors.black, fontSize: 16.0)), - ), - PopupMenuItem(value: 1, - child: Text('New Folder', style: TextStyle(color: Colors.black, fontSize: 16.0)), - ), - PopupMenuItem(value: 2, - child: Text('Upload File', style: TextStyle(color: Colors.black, fontSize: 16.0)), - ), - ]; - } - ), - const SizedBox(width: 15.0), - GestureDetector( - onTap: () {}, // view_module_rounded - child: Container(width: 20.0, child: Icon(Icons.view_list_rounded, color: color.primary)), - ), - const SizedBox(width: 10.0), - ], - ), - SizedBox(height: 5.0), - Row( - children: [ - const SizedBox(width: 15.0), - InkWell( - onTap: () { - print('Home'); - }, - child: Text('Home', style: TextStyle(fontSize: 14.0, color: Color(0xFFADB0BB))) - ), - Text('/', style: TextStyle(fontSize: 14.0, color: Color(0xFFADB0BB))), - InkWell( - onTap: () { - print('Home/workspace'); - }, - child: Text('workspace', style: TextStyle(fontSize: 14.0, color: Color(0xFFADB0BB))) - ), - Text('/', style: TextStyle(fontSize: 14.0, color: Color(0xFFADB0BB))), - InkWell( - onTap: () { - print('Home/workspace/cymple'); - }, - child: Text('cymple', style: TextStyle(fontSize: 14.0, color: Color(0xFFADB0BB))) - ), - ] + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + height: 55.0, + width: 60.0, + child: fileIcon(this.path.fullName, 48.0), ), - SizedBox(height: 15.0), - Expanded( - child: Wrap( - spacing: 4.0, - runSpacing: 16.0, - alignment: WrapAlignment.start, - children: [ - FileItem(name: 'myworks.dir'), - FileItem(name: 'ESSE-infos-public.dir'), - FileItem(name: 'personal.dir'), - FileItem(name: 'others.dir'), - FileItem(name: 'logo.jpg'), - FileItem(name: 'cat.png'), - FileItem(name: 'what-is-esse_en.doc'), - FileItem(name: '20210101-customers.xls'), - FileItem(name: 'product.pdf'), - FileItem(name: 'deck.ppt'), - FileItem(name: 'coder.md'), - FileItem(name: 'how-to-live-in-happy.mp4'), - FileItem(name: 'something_important'), - FileItem(name: 'car.json'), - ], - ) + Tooltip( + message: trueName, + child: Text(trueName, + style: TextStyle(fontSize: 14.0), maxLines: 1, overflow: TextOverflow.ellipsis), ) ] - ) - ) - ); + ))); } } diff --git a/lib/apps/file/models.dart b/lib/apps/file/models.dart index fc2ca46..84fa4e0 100644 --- a/lib/apps/file/models.dart +++ b/lib/apps/file/models.dart @@ -23,17 +23,56 @@ extension InnerServiceExtension on RootDirectory { List params(AppLocalizations lang) { switch (this) { case RootDirectory.Star: - return [Icons.star, lang.star]; + return [Icons.star, lang.star, FilePath.root(RootDirectory.Star)]; case RootDirectory.Document: - return [Icons.description, lang.document]; + return [Icons.description, lang.document, FilePath.root(RootDirectory.Document)]; case RootDirectory.Image: - return [Icons.image, lang.image]; + return [Icons.image, lang.image, FilePath.root(RootDirectory.Image)]; case RootDirectory.Music: - return [Icons.music_note, lang.music]; + return [Icons.music_note, lang.music, FilePath.root(RootDirectory.Music)]; case RootDirectory.Video: - return [Icons.play_circle_filled, lang.video]; + return [Icons.play_circle_filled, lang.video, FilePath.root(RootDirectory.Video)]; case RootDirectory.Trash: - return [Icons.auto_delete, lang.trash]; + return [Icons.auto_delete, lang.trash, FilePath.root(RootDirectory.Trash)]; } } } + +class FilePath { + RootDirectory root; + List path = []; + String get fullName => this.path.last; + + FilePath.root(this.root); + + FilePath(this.root, this.path); + + static FilePath next(FilePath file, String name) { + final root = file.root; + List path = List.from(file.path); + path.add(name); + return FilePath(root, path); + } + + static directoryName(String name) { + final i = name.lastIndexOf('.'); + return name.substring(0, i); + } + + void add(String next) { + this.path.add(next); + } + + String name() { + if (isDirectory()) { + final i = this.path.last.lastIndexOf('.'); + return this.path.last.substring(0, i); + } else { + return this.path.last; + } + } + + bool isDirectory() { + return this.path.last.endsWith('.dir'); + } +} diff --git a/lib/l10n/localizations.dart b/lib/l10n/localizations.dart index 8dcbf2b..5a788ad 100644 --- a/lib/l10n/localizations.dart +++ b/lib/l10n/localizations.dart @@ -236,6 +236,10 @@ abstract class AppLocalizations { String get music; String get video; String get trash; + String get newPost; + String get newFolder; + String get uploadFile; + String get trashClear; } class _AppLocalizationsDelegate diff --git a/lib/l10n/localizations_en.dart b/lib/l10n/localizations_en.dart index 39af608..e3e6526 100644 --- a/lib/l10n/localizations_en.dart +++ b/lib/l10n/localizations_en.dart @@ -376,4 +376,12 @@ class AppLocalizationsEn extends AppLocalizations { String get video => 'Videos'; @override String get trash => 'Trash'; + @override + String get newPost => 'Create Post'; + @override + String get newFolder => 'Create Folder'; + @override + String get uploadFile => 'Upload File'; + @override + String get trashClear => 'Empty the trash, Remove completely?'; } diff --git a/lib/l10n/localizations_zh.dart b/lib/l10n/localizations_zh.dart index 061d269..7f78d4f 100644 --- a/lib/l10n/localizations_zh.dart +++ b/lib/l10n/localizations_zh.dart @@ -376,4 +376,12 @@ class AppLocalizationsZh extends AppLocalizations { String get video => '视频'; @override String get trash => '垃圾箱'; + @override + String get newPost => '新建文档'; + @override + String get newFolder => '新建文件夹'; + @override + String get uploadFile => '上传文件'; + @override + String get trashClear => '清空垃圾箱,彻底删除?'; } diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 3971673..dc5733d 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -267,7 +267,7 @@ class _HomeListState extends State { title: Text(params[1], style: TextStyle(fontSize: 16.0)), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { - final widget = FilesList(root: ROOT_DIRECTORY[index]); + final widget = FilesList(path: params[2]); if (widget != null) { if (isDesktop) { Provider.of(context, listen: false).updateActivedWidget(widget); diff --git a/pubspec.lock b/pubspec.lock index 5b9322d..d49ed5f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -106,6 +106,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.1" device_info_plus: dependency: "direct main" description: @@ -148,6 +155,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + diff_match_patch: + dependency: transitive + description: + name: diff_match_patch + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.1" esse_core: dependency: "direct main" description: @@ -236,11 +250,46 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_colorpicker: + dependency: transitive + description: + name: flutter_colorpicker + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" flutter_driver: dependency: transitive description: flutter source: sdk version: "0.0.0" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.2" + flutter_keyboard_visibility: + dependency: transitive + description: + name: flutter_keyboard_visibility + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.3" + flutter_keyboard_visibility_platform_interface: + dependency: transitive + description: + name: flutter_keyboard_visibility_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + flutter_keyboard_visibility_web: + dependency: transitive + description: + name: flutter_keyboard_visibility_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" flutter_launcher_icons: dependency: "direct dev" description: @@ -281,6 +330,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.3" + flutter_quill: + dependency: "direct main" + description: + name: flutter_quill + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" flutter_test: dependency: "direct dev" description: flutter @@ -296,6 +352,13 @@ packages: description: flutter source: sdk version: "0.0.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.0" http: dependency: transitive description: @@ -462,6 +525,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.3" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" percent_indicator: dependency: "direct main" description: @@ -490,6 +560,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.3.0" + photo_view: + dependency: transitive + description: + name: photo_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.0" platform: dependency: transitive description: @@ -539,6 +616,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" record: dependency: "direct main" description: @@ -642,6 +726,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + string_validator: + dependency: transitive + description: + name: string_validator + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" sync_http: dependency: transitive description: @@ -663,6 +754,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.2" + tuple: + dependency: transitive + description: + name: tuple + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" typed_data: dependency: transitive description: @@ -740,6 +838,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + video_player: + dependency: transitive + description: + name: video_player + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.5" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.0" + video_player_web: + dependency: transitive + description: + name: video_player_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" vm_service: dependency: transitive description: @@ -789,6 +908,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0" + youtube_player_flutter: + dependency: transitive + description: + name: youtube_player_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.0" sdks: dart: ">=2.14.0 <3.0.0" flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index 2419471..d011d14 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,7 @@ dependencies: device_info_plus: any percent_indicator: any bottom_navy_bar: ^6.0.0 + flutter_quill: ^2.0.6 dev_dependencies: flutter_test: