Preview for audio and images, better asset table
This commit is contained in:
parent
a4ab10db17
commit
bce8e97368
4 changed files with 196 additions and 173 deletions
|
|
@ -1,18 +1,16 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:data_table_2/data_table_2.dart';
|
import 'package:data_table_2/data_table_2.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:file_icon/file_icon.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_fast_forms/flutter_fast_forms.dart';
|
import 'package:flutter_fast_forms/flutter_fast_forms.dart';
|
||||||
import 'package:get/get.dart' hide MultipartFile;
|
import 'package:get/get.dart' hide MultipartFile;
|
||||||
import 'package:mime/mime.dart';
|
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:super_drag_and_drop/super_drag_and_drop.dart';
|
import 'package:super_drag_and_drop/super_drag_and_drop.dart';
|
||||||
import 'package:tuuli_api/tuuli_api.dart';
|
import 'package:tuuli_api/tuuli_api.dart';
|
||||||
import 'package:tuuli_app/api_controller.dart';
|
import 'package:tuuli_app/api_controller.dart';
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
import 'package:tuuli_app/widgets/audio_player_widget.dart';
|
||||||
|
|
||||||
class AssetsPagePanelController extends GetxController {
|
class AssetsPagePanelController extends GetxController {
|
||||||
@override
|
@override
|
||||||
|
|
@ -22,6 +20,8 @@ class AssetsPagePanelController extends GetxController {
|
||||||
refreshData();
|
refreshData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final scrollController = ScrollController();
|
||||||
|
|
||||||
final _isLoading = false.obs;
|
final _isLoading = false.obs;
|
||||||
bool get isLoading => _isLoading.value;
|
bool get isLoading => _isLoading.value;
|
||||||
|
|
||||||
|
|
@ -258,93 +258,7 @@ class AssetsPagePanelController extends GetxController {
|
||||||
).paddingAll(8).card(color: Colors.blueGrey.shade200).expanded(),
|
).paddingAll(8).card(color: Colors.blueGrey.shade200).expanded(),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () => previewAsset(e),
|
||||||
Get.dialog(
|
|
||||||
Stack(
|
|
||||||
children: [
|
|
||||||
FutureBuilder<Uint8List>(
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Text("View asset")),
|
child: const Text("View asset")),
|
||||||
],
|
],
|
||||||
).constrained(width: Get.width * 0.5, height: Get.width * 0.5),
|
).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<AssetsPagePanelController> {
|
class AssetsPagePanel extends GetView<AssetsPagePanelController> {
|
||||||
|
|
@ -542,9 +493,9 @@ class AssetsPagePanel extends GetView<AssetsPagePanelController> {
|
||||||
|
|
||||||
Widget get assetsPanel => Obx(
|
Widget get assetsPanel => Obx(
|
||||||
() => DataTable2(
|
() => DataTable2(
|
||||||
|
horizontalScrollController: controller.scrollController,
|
||||||
columns: const [
|
columns: const [
|
||||||
DataColumn2(label: Text(""), size: ColumnSize.S),
|
DataColumn2(label: Text(""), fixedWidth: 16),
|
||||||
DataColumn2(label: Text("ID"), size: ColumnSize.S, numeric: true),
|
|
||||||
DataColumn2(label: Text("Filename"), size: ColumnSize.M),
|
DataColumn2(label: Text("Filename"), size: ColumnSize.M),
|
||||||
DataColumn2(label: Text("Description"), size: ColumnSize.L),
|
DataColumn2(label: Text("Description"), size: ColumnSize.L),
|
||||||
DataColumn2(label: Text("File ID"), size: ColumnSize.M),
|
DataColumn2(label: Text("File ID"), size: ColumnSize.M),
|
||||||
|
|
@ -562,76 +513,7 @@ class AssetsPagePanel extends GetView<AssetsPagePanelController> {
|
||||||
})
|
})
|
||||||
.map((e) => DataRow2(
|
.map((e) => DataRow2(
|
||||||
cells: [
|
cells: [
|
||||||
DataCell(
|
DataCell(FileIcon(e.name)),
|
||||||
FutureBuilder<Uint8List>(
|
|
||||||
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(Tooltip(
|
DataCell(Tooltip(
|
||||||
message: e.name,
|
message: e.name,
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
@ -665,6 +547,10 @@ class AssetsPagePanel extends GetView<AssetsPagePanelController> {
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
DataCell(Row(children: [
|
DataCell(Row(children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.preview),
|
||||||
|
onPressed: () => controller.previewAsset(e),
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
onPressed: () => controller.editAsset(e),
|
onPressed: () => controller.editAsset(e),
|
||||||
|
|
|
||||||
72
lib/widgets/audio_player_widget.dart
Normal file
72
lib/widgets/audio_player_widget.dart
Normal file
|
|
@ -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<AudioPlayerWidgetController> {
|
||||||
|
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>(
|
||||||
|
() => AudioPlayerWidgetController(
|
||||||
|
title: title,
|
||||||
|
url: url,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return const AudioPlayerWidget();
|
||||||
|
}
|
||||||
|
}
|
||||||
80
pubspec.lock
80
pubspec.lock
|
|
@ -17,14 +17,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.11.1"
|
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:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -41,6 +33,62 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.0"
|
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:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -241,6 +289,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.4"
|
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:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -773,6 +829,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0+2"
|
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:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,19 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
animated_background: ^2.0.0
|
audioplayers: ^4.0.1
|
||||||
bottom_sheet: ^3.1.2
|
bottom_sheet: ^3.1.2
|
||||||
built_collection: ^5.1.1
|
built_collection: ^5.1.1
|
||||||
built_value: ^8.4.4
|
built_value: ^8.4.4
|
||||||
data_table_2: ^2.4.2
|
data_table_2: ^2.4.2
|
||||||
dio: ^5.1.1
|
dio: ^5.1.1
|
||||||
|
file_icon: ^1.0.0
|
||||||
flutter_chips_input: ^2.0.0
|
flutter_chips_input: ^2.0.0
|
||||||
flutter_fast_forms: ^10.0.0
|
flutter_fast_forms: ^10.0.0
|
||||||
get: ^4.6.5
|
get: ^4.6.5
|
||||||
get_storage: ^2.1.1
|
get_storage: ^2.1.1
|
||||||
http: ^0.13.5
|
http: ^0.13.5
|
||||||
|
http_parser: ^4.0.2
|
||||||
mime: ^1.0.4
|
mime: ^1.0.4
|
||||||
omni_datetime_picker: ^1.0.7
|
omni_datetime_picker: ^1.0.7
|
||||||
one_of_serializer: ^1.5.0
|
one_of_serializer: ^1.5.0
|
||||||
|
|
@ -34,7 +36,6 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://glab.nuark.xyz/nuark/tuuli_api.git
|
url: https://glab.nuark.xyz/nuark/tuuli_api.git
|
||||||
ref: master
|
ref: master
|
||||||
http_parser: ^4.0.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue