From bce8e97368eb25265ac5e85c376df0d5c40c6043 Mon Sep 17 00:00:00 2001 From: Andrew nuark G Date: Mon, 1 May 2023 01:55:45 +0700 Subject: [PATCH] Preview for audio and images, better asset table --- lib/pages/home_panels/assets_panel.dart | 212 ++++++------------------ lib/widgets/audio_player_widget.dart | 72 ++++++++ pubspec.lock | 80 ++++++++- pubspec.yaml | 5 +- 4 files changed, 196 insertions(+), 173 deletions(-) create mode 100644 lib/widgets/audio_player_widget.dart diff --git a/lib/pages/home_panels/assets_panel.dart b/lib/pages/home_panels/assets_panel.dart index 82c828d..9b24c1a 100644 --- a/lib/pages/home_panels/assets_panel.dart +++ b/lib/pages/home_panels/assets_panel.dart @@ -1,18 +1,16 @@ -import 'dart:convert'; -import 'dart:typed_data'; - import 'package:data_table_2/data_table_2.dart'; import 'package:dio/dio.dart'; +import 'package:file_icon/file_icon.dart'; import 'package:flutter/material.dart'; import 'package:flutter_fast_forms/flutter_fast_forms.dart'; import 'package:get/get.dart' hide MultipartFile; -import 'package:mime/mime.dart'; import 'package:photo_view/photo_view.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:super_drag_and_drop/super_drag_and_drop.dart'; import 'package:tuuli_api/tuuli_api.dart'; import 'package:tuuli_app/api_controller.dart'; import 'package:http_parser/http_parser.dart'; +import 'package:tuuli_app/widgets/audio_player_widget.dart'; class AssetsPagePanelController extends GetxController { @override @@ -22,6 +20,8 @@ class AssetsPagePanelController extends GetxController { refreshData(); } + final scrollController = ScrollController(); + final _isLoading = false.obs; bool get isLoading => _isLoading.value; @@ -258,93 +258,7 @@ class AssetsPagePanelController extends GetxController { ).paddingAll(8).card(color: Colors.blueGrey.shade200).expanded(), ), ElevatedButton( - onPressed: () { - Get.dialog( - Stack( - children: [ - FutureBuilder( - future: () async { - final resp = await ApiController.to.apiClient - .getAsset(fid: e.fid); - - final respData = resp.data; - if (respData == null) { - throw Exception("No data in response"); - } - - return respData; - }(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.waiting) { - return const CircularProgressIndicator(); - } - - if (snapshot.hasError) { - printError(info: snapshot.error.toString()); - return const Icon(Icons.error); - } - - if (snapshot.hasData && snapshot.data == null) { - return const Icon(Icons.error); - } - - final data = snapshot.data!; - final mime = lookupMimeType( - "file", - headerBytes: data, - ); - switch (mime) { - case "image/gif": - case "image/gif": - case "image/jpeg": - case "image/png": - case "image/tiff": - case "image/webp": - return PhotoView( - imageProvider: MemoryImage(data), - enablePanAlways: true, - ); - case "application/pdf": - case "application/postscript": - return const Icon(Icons.picture_as_pdf); - case "application/zip": - case "application/x-rar-compressed": - return const Icon(Icons.archive); - case "audio/x-aiff": - case "audio/x-flac": - case "audio/x-wav": - case "audio/aac": - case "audio/aac": - case "audio/weba": - case "audio/mpeg": - case "audio/mpeg": - case "audio/ogg": - return const Icon(Icons.audiotrack); - case "video/mp4": - return const Icon(Icons.movie); - case "model/gltf-binary": - case "font/woff2": - default: - return const Icon(Icons.device_unknown); - } - }, - ).center(), - Positioned( - top: 8, - right: 8, - child: Material( - color: Colors.transparent, - child: IconButton( - onPressed: () => Get.back(), - icon: const Icon(Icons.close), - ), - ), - ), - ], - ), - ); - }, + onPressed: () => previewAsset(e), child: const Text("View asset")), ], ).constrained(width: Get.width * 0.5, height: Get.width * 0.5), @@ -473,6 +387,43 @@ class AssetsPagePanelController extends GetxController { ); } } + + void previewAsset(Asset e) { + Get.dialog( + Stack( + children: [ + if (e.mime.split("/").first == "image") + PhotoView( + imageProvider: + NetworkImage("${ApiController.to.endPoint}/assets/${e.fid}"), + enablePanAlways: true, + ) + else if (e.mime.split("/").first == "audio") + AudioPlayerWidget.create( + title: e.name, + url: "${ApiController.to.endPoint}/assets/${e.fid}", + ) + else + const Text("Unsupported media type") + .fontSize(16) + .paddingAll(8) + .card() + .center(), + Positioned( + top: 8, + right: 8, + child: Material( + color: Colors.transparent, + child: IconButton( + onPressed: () => Get.back(), + icon: const Icon(Icons.close), + ), + ), + ), + ], + ), + ); + } } class AssetsPagePanel extends GetView { @@ -542,9 +493,9 @@ class AssetsPagePanel extends GetView { Widget get assetsPanel => Obx( () => DataTable2( + horizontalScrollController: controller.scrollController, columns: const [ - DataColumn2(label: Text(""), size: ColumnSize.S), - DataColumn2(label: Text("ID"), size: ColumnSize.S, numeric: true), + DataColumn2(label: Text(""), fixedWidth: 16), DataColumn2(label: Text("Filename"), size: ColumnSize.M), DataColumn2(label: Text("Description"), size: ColumnSize.L), DataColumn2(label: Text("File ID"), size: ColumnSize.M), @@ -562,76 +513,7 @@ class AssetsPagePanel extends GetView { }) .map((e) => DataRow2( cells: [ - DataCell( - FutureBuilder( - future: () async { - final resp = await ApiController.to.apiClient - .getAsset(fid: e.fid); - - final respData = resp.data; - if (respData == null) { - throw Exception("No data in response"); - } - - return respData; - }(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.waiting) { - return const CircularProgressIndicator(); - } - - if (snapshot.hasError) { - printError(info: snapshot.error.toString()); - return const Icon(Icons.error); - } - - if (snapshot.hasData && snapshot.data == null) { - return const Icon(Icons.error); - } - - final data = snapshot.data!; - final mime = lookupMimeType( - "file", - headerBytes: data, - ); - switch (mime) { - case "image/gif": - case "image/gif": - case "image/jpeg": - case "image/png": - case "image/tiff": - case "image/webp": - return Image.memory(data).card( - clipBehavior: Clip.antiAlias, - ); - case "application/pdf": - case "application/postscript": - return const Icon(Icons.picture_as_pdf); - case "application/zip": - case "application/x-rar-compressed": - return const Icon(Icons.archive); - case "audio/x-aiff": - case "audio/x-flac": - case "audio/x-wav": - case "audio/aac": - case "audio/aac": - case "audio/weba": - case "audio/mpeg": - case "audio/mpeg": - case "audio/ogg": - return const Icon(Icons.audiotrack); - case "video/mp4": - return const Icon(Icons.movie); - case "model/gltf-binary": - case "font/woff2": - default: - return const Icon(Icons.device_unknown); - } - }, - ), - ), - DataCell(Text(e.id.toString())), + DataCell(FileIcon(e.name)), DataCell(Tooltip( message: e.name, child: Text( @@ -665,6 +547,10 @@ class AssetsPagePanel extends GetView { ), )), DataCell(Row(children: [ + IconButton( + icon: const Icon(Icons.preview), + onPressed: () => controller.previewAsset(e), + ), IconButton( icon: const Icon(Icons.edit), onPressed: () => controller.editAsset(e), diff --git a/lib/widgets/audio_player_widget.dart b/lib/widgets/audio_player_widget.dart new file mode 100644 index 0000000..64c5751 --- /dev/null +++ b/lib/widgets/audio_player_widget.dart @@ -0,0 +1,72 @@ +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:styled_widget/styled_widget.dart'; + +class AudioPlayerWidgetController extends GetxController { + final String title; + final String url; + + AudioPlayerWidgetController({ + required this.title, + required this.url, + }); + + @override + void onInit() { + super.onInit(); + + player = AudioPlayer(); + player.play(UrlSource(url)); + + player.onPlayerStateChanged.listen((event) { + _playerState.value = event; + }); + } + + @override + void onClose() { + player.dispose(); + super.onClose(); + } + + late AudioPlayer player; + + final _playerState = PlayerState.stopped.obs; + get playerState => _playerState.value; +} + +class AudioPlayerWidget extends GetView { + const AudioPlayerWidget({super.key}); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: Obx( + () => controller.playerState == PlayerState.playing + ? IconButton( + onPressed: () => controller.player.pause(), + icon: const Icon(Icons.pause), + ) + : IconButton( + onPressed: () => controller.player.resume(), + icon: const Icon(Icons.play_arrow), + ), + ), + title: const Text("Playing"), + subtitle: Text(controller.title), + ).card().paddingAll(16).center(); + } + + static AudioPlayerWidget create( + {required String url, required String title}) { + Get.lazyPut( + () => AudioPlayerWidgetController( + title: title, + url: url, + ), + ); + + return const AudioPlayerWidget(); + } +} diff --git a/pubspec.lock b/pubspec.lock index 0936d81..860577d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.11.1" - animated_background: - dependency: "direct main" - description: - name: animated_background - sha256: "24b05a6dca2cb0231b011f9e8fd2e9d8060faac08a78cf0643915bb7d6e9b03b" - url: "https://pub.dev" - source: hosted - version: "2.0.0" args: dependency: transitive description: @@ -41,6 +33,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + audioplayers: + dependency: "direct main" + description: + name: audioplayers + sha256: "6063c05f987596ba7a3dad9bb9a5d8adfa5e7c07b9bae5301b27c11d0b3a239f" + url: "https://pub.dev" + source: hosted + version: "4.0.1" + audioplayers_android: + dependency: transitive + description: + name: audioplayers_android + sha256: fb6bca878ad175d8f6ddc0e0a2d4226d81fa7c10747c12db420e96c7a096b2cc + url: "https://pub.dev" + source: hosted + version: "3.0.1" + audioplayers_darwin: + dependency: transitive + description: + name: audioplayers_darwin + sha256: c4a56c49347b2e85ac4e1efea218948ca0fba87f04d2a3d3de07ce2410037038 + url: "https://pub.dev" + source: hosted + version: "4.0.1" + audioplayers_linux: + dependency: transitive + description: + name: audioplayers_linux + sha256: "897e24f190232a3fbb88134b062aa83a9240f55789b5e8d17c114283284ef56b" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + audioplayers_platform_interface: + dependency: transitive + description: + name: audioplayers_platform_interface + sha256: "3a90a46198d375fc7d47bc1d3070c8fd8863b6469b7d87ca80f953efb090f976" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + audioplayers_web: + dependency: transitive + description: + name: audioplayers_web + sha256: "4f5dcbfec0bf98ea09e243d5f5b64ea43a4e6710a2f292724bed16cdba3c691e" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + audioplayers_windows: + dependency: transitive + description: + name: audioplayers_windows + sha256: "010f575653c01ccbe9756050b18df83d89426740e04b684f6438aa26c775a965" + url: "https://pub.dev" + source: hosted + version: "2.0.1" boolean_selector: dependency: transitive description: @@ -241,6 +289,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + file_icon: + dependency: "direct main" + description: + name: file_icon + sha256: c46b6c24d9595d18995758b90722865baeda407f56308eadd757e1ab913f50a1 + url: "https://pub.dev" + source: hosted + version: "1.0.0" fixnum: dependency: transitive description: @@ -773,6 +829,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0+2" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5a47417..79c95a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,17 +10,19 @@ dependencies: flutter: sdk: flutter - animated_background: ^2.0.0 + audioplayers: ^4.0.1 bottom_sheet: ^3.1.2 built_collection: ^5.1.1 built_value: ^8.4.4 data_table_2: ^2.4.2 dio: ^5.1.1 + file_icon: ^1.0.0 flutter_chips_input: ^2.0.0 flutter_fast_forms: ^10.0.0 get: ^4.6.5 get_storage: ^2.1.1 http: ^0.13.5 + http_parser: ^4.0.2 mime: ^1.0.4 omni_datetime_picker: ^1.0.7 one_of_serializer: ^1.5.0 @@ -34,7 +36,6 @@ dependencies: git: url: https://glab.nuark.xyz/nuark/tuuli_api.git ref: master - http_parser: ^4.0.2 dev_dependencies: flutter_test: