Compare commits
No commits in common. "03f2470dae2e191a0ca107ca13330d9b4e961f5d" and "bbea4b4359cc341b2e597d4c1bc6fa7fe64a71d1" have entirely different histories.
03f2470dae
...
bbea4b4359
@ -141,7 +141,7 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
try {
|
try {
|
||||||
for (var idx = 0; idx < _periods.length; idx++) {
|
for (var idx = 0; idx < _periods.length; idx++) {
|
||||||
await _periods[idx].action();
|
await _periods[idx].action();
|
||||||
if (_isErrored && !_isDismissable) break;
|
if (_isErrored) break;
|
||||||
if (_periodCursor < _periods.length - 1) {
|
if (_periodCursor < _periods.length - 1) {
|
||||||
setState(() => _periodCursor++);
|
setState(() => _periodCursor++);
|
||||||
}
|
}
|
||||||
@ -179,11 +179,11 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (_isErrored && !_isDismissable && !_isBusy)
|
if (_isErrored && !_isDismissable)
|
||||||
const Icon(Icons.cancel, size: 24),
|
const Icon(Icons.cancel, size: 24),
|
||||||
if (_isErrored && _isDismissable)
|
if (_isErrored && _isDismissable)
|
||||||
const Icon(Icons.warning, size: 24),
|
const Icon(Icons.warning, size: 24),
|
||||||
if ((_isErrored && _isDismissable && _isBusy) || _isBusy)
|
if (!_isErrored && _isBusy)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
import 'package:solian/models/account.dart';
|
|
||||||
import 'package:solian/models/attachment.dart';
|
|
||||||
|
|
||||||
class Sticker {
|
|
||||||
int id;
|
|
||||||
DateTime createdAt;
|
|
||||||
DateTime updatedAt;
|
|
||||||
DateTime? deletedAt;
|
|
||||||
String alias;
|
|
||||||
String name;
|
|
||||||
int attachmentId;
|
|
||||||
Attachment attachment;
|
|
||||||
int packId;
|
|
||||||
StickerPack? pack;
|
|
||||||
int accountId;
|
|
||||||
Account account;
|
|
||||||
|
|
||||||
Sticker({
|
|
||||||
required this.id,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
required this.deletedAt,
|
|
||||||
required this.alias,
|
|
||||||
required this.name,
|
|
||||||
required this.attachmentId,
|
|
||||||
required this.attachment,
|
|
||||||
required this.packId,
|
|
||||||
required this.pack,
|
|
||||||
required this.accountId,
|
|
||||||
required this.account,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Sticker.fromJson(Map<String, dynamic> json) => Sticker(
|
|
||||||
id: json['id'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: json['deleted_at'],
|
|
||||||
alias: json['alias'],
|
|
||||||
name: json['name'],
|
|
||||||
attachmentId: json['attachment_id'],
|
|
||||||
attachment: Attachment.fromJson(json['attachment']),
|
|
||||||
packId: json['pack_id'],
|
|
||||||
pack: json['pack'] != null ? StickerPack.fromJson(json['pack']) : null,
|
|
||||||
accountId: json['account_id'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'alias': alias,
|
|
||||||
'name': name,
|
|
||||||
'attachment_id': attachmentId,
|
|
||||||
'attachment': attachment.toJson(),
|
|
||||||
'pack_id': packId,
|
|
||||||
'account_id': accountId,
|
|
||||||
'account': account.toJson(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class StickerPack {
|
|
||||||
int id;
|
|
||||||
DateTime createdAt;
|
|
||||||
DateTime updatedAt;
|
|
||||||
DateTime? deletedAt;
|
|
||||||
String prefix;
|
|
||||||
String name;
|
|
||||||
String description;
|
|
||||||
List<Sticker>? stickers;
|
|
||||||
int accountId;
|
|
||||||
Account account;
|
|
||||||
|
|
||||||
StickerPack({
|
|
||||||
required this.id,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
required this.deletedAt,
|
|
||||||
required this.prefix,
|
|
||||||
required this.name,
|
|
||||||
required this.description,
|
|
||||||
required this.stickers,
|
|
||||||
required this.accountId,
|
|
||||||
required this.account,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory StickerPack.fromJson(Map<String, dynamic> json) => StickerPack(
|
|
||||||
id: json['id'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: json['deleted_at'],
|
|
||||||
prefix: json['prefix'],
|
|
||||||
name: json['name'],
|
|
||||||
description: json['description'],
|
|
||||||
stickers: json['stickers'] == null
|
|
||||||
? []
|
|
||||||
: List<Sticker>.from(
|
|
||||||
json['stickers']!.map((x) => Sticker.fromJson(x))),
|
|
||||||
accountId: json['account_id'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'prefix': prefix,
|
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'stickers': stickers == null
|
|
||||||
? []
|
|
||||||
: List<dynamic>.from(stickers!.map((x) => x.toJson())),
|
|
||||||
'account_id': accountId,
|
|
||||||
'account': account.toJson(),
|
|
||||||
};
|
|
||||||
}
|
|
@ -14,7 +14,6 @@ class AttachmentUploadTask {
|
|||||||
double progress = 0;
|
double progress = 0;
|
||||||
bool isUploading = false;
|
bool isUploading = false;
|
||||||
bool isCompleted = false;
|
bool isCompleted = false;
|
||||||
dynamic error;
|
|
||||||
|
|
||||||
AttachmentUploadTask({
|
AttachmentUploadTask({
|
||||||
required this.file,
|
required this.file,
|
||||||
@ -67,7 +66,7 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
queueOfUpload.remove(task);
|
queueOfUpload.remove(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Attachment?> performSingleTask(int queueIndex) async {
|
Future<Attachment> performSingleTask(int queueIndex) async {
|
||||||
isUploading.value = true;
|
isUploading.value = true;
|
||||||
progressOfUpload.value = 0;
|
progressOfUpload.value = 0;
|
||||||
|
|
||||||
@ -84,15 +83,9 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
queueOfUpload[queueIndex].progress = value;
|
queueOfUpload[queueIndex].progress = value;
|
||||||
_progressOfUpload = value;
|
_progressOfUpload = value;
|
||||||
},
|
},
|
||||||
onError: (err) {
|
|
||||||
queueOfUpload[queueIndex].error = err;
|
|
||||||
queueOfUpload[queueIndex].isUploading = false;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (queueOfUpload[queueIndex].error == null) {
|
queueOfUpload.removeAt(queueIndex);
|
||||||
queueOfUpload.removeAt(queueIndex);
|
|
||||||
}
|
|
||||||
_stopProgressSyncTimer();
|
_stopProgressSyncTimer();
|
||||||
_syncProgress();
|
_syncProgress();
|
||||||
|
|
||||||
@ -110,10 +103,6 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
_startProgressSyncTimer();
|
_startProgressSyncTimer();
|
||||||
|
|
||||||
for (var idx = 0; idx < queueOfUpload.length; idx++) {
|
for (var idx = 0; idx < queueOfUpload.length; idx++) {
|
||||||
if (queueOfUpload[idx].isUploading || queueOfUpload[idx].error != null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
queueOfUpload[idx].isUploading = true;
|
queueOfUpload[idx].isUploading = true;
|
||||||
|
|
||||||
final task = queueOfUpload[idx];
|
final task = queueOfUpload[idx];
|
||||||
@ -126,20 +115,15 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
queueOfUpload[idx].progress = value;
|
queueOfUpload[idx].progress = value;
|
||||||
_progressOfUpload = (idx + value) / queueOfUpload.length;
|
_progressOfUpload = (idx + value) / queueOfUpload.length;
|
||||||
},
|
},
|
||||||
onError: (err) {
|
|
||||||
queueOfUpload[idx].error = err;
|
|
||||||
queueOfUpload[idx].isUploading = false;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
_progressOfUpload = (idx + 1) / queueOfUpload.length;
|
_progressOfUpload = (idx + 1) / queueOfUpload.length;
|
||||||
if (result != null) onData(result);
|
onData(result);
|
||||||
|
|
||||||
queueOfUpload[idx].isUploading = false;
|
queueOfUpload[idx].isUploading = false;
|
||||||
queueOfUpload[idx].isCompleted = true;
|
queueOfUpload[idx].isCompleted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
queueOfUpload.value =
|
queueOfUpload.clear();
|
||||||
queueOfUpload.where((x) => x.error == null).toList(growable: true);
|
|
||||||
_stopProgressSyncTimer();
|
_stopProgressSyncTimer();
|
||||||
_syncProgress();
|
_syncProgress();
|
||||||
|
|
||||||
@ -151,7 +135,7 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
String path,
|
String path,
|
||||||
String usage,
|
String usage,
|
||||||
Map<String, dynamic>? metadata,
|
Map<String, dynamic>? metadata,
|
||||||
Function(Attachment?) callback,
|
Function(Attachment) callback,
|
||||||
) async {
|
) async {
|
||||||
if (isUploading.value) throw Exception('uploading blocked');
|
if (isUploading.value) throw Exception('uploading blocked');
|
||||||
|
|
||||||
@ -169,7 +153,7 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
callback(result);
|
callback(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Attachment?> uploadAttachment(
|
Future<Attachment> uploadAttachment(
|
||||||
Uint8List data,
|
Uint8List data,
|
||||||
String path,
|
String path,
|
||||||
String usage,
|
String usage,
|
||||||
@ -191,9 +175,9 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Attachment?> _rawUploadAttachment(
|
Future<Attachment> _rawUploadAttachment(
|
||||||
Uint8List data, String path, String usage, Map<String, dynamic>? metadata,
|
Uint8List data, String path, String usage, Map<String, dynamic>? metadata,
|
||||||
{Function(double)? onProgress, Function(dynamic err)? onError}) async {
|
{Function(double)? onProgress}) async {
|
||||||
final AttachmentProvider provider = Get.find();
|
final AttachmentProvider provider = Get.find();
|
||||||
try {
|
try {
|
||||||
final result = await provider.createAttachment(
|
final result = await provider.createAttachment(
|
||||||
@ -205,10 +189,7 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (onError != null) {
|
rethrow;
|
||||||
onError(err);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,6 @@ class AttachmentProvider extends GetConnect {
|
|||||||
List<int> id, {
|
List<int> id, {
|
||||||
noCache = false,
|
noCache = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (id.isEmpty) return List.empty();
|
|
||||||
|
|
||||||
List<Attachment?> result = List.filled(id.length, null);
|
List<Attachment?> result = List.filled(id.length, null);
|
||||||
List<int> pendingQuery = List.empty(growable: true);
|
List<int> pendingQuery = List.empty(growable: true);
|
||||||
if (!noCache) {
|
if (!noCache) {
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
class StickerProvider extends GetxController {
|
|
||||||
|
|
||||||
}
|
|
@ -7,7 +7,6 @@ import 'package:solian/screens/account.dart';
|
|||||||
import 'package:solian/screens/account/friend.dart';
|
import 'package:solian/screens/account/friend.dart';
|
||||||
import 'package:solian/screens/account/personalize.dart';
|
import 'package:solian/screens/account/personalize.dart';
|
||||||
import 'package:solian/screens/account/profile_page.dart';
|
import 'package:solian/screens/account/profile_page.dart';
|
||||||
import 'package:solian/screens/account/stickers.dart';
|
|
||||||
import 'package:solian/screens/channel/channel_chat.dart';
|
import 'package:solian/screens/channel/channel_chat.dart';
|
||||||
import 'package:solian/screens/channel/channel_detail.dart';
|
import 'package:solian/screens/channel/channel_detail.dart';
|
||||||
import 'package:solian/screens/channel/channel_organize.dart';
|
import 'package:solian/screens/channel/channel_organize.dart';
|
||||||
@ -227,14 +226,6 @@ abstract class AppRouter {
|
|||||||
name: 'accountFriend',
|
name: 'accountFriend',
|
||||||
builder: (context, state) => const FriendScreen(),
|
builder: (context, state) => const FriendScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
path: '/account/stickers',
|
|
||||||
name: 'accountStickers',
|
|
||||||
builder: (context, state) => TitleShell(
|
|
||||||
state: state,
|
|
||||||
child: const StickerScreen(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/account/personalize',
|
path: '/account/personalize',
|
||||||
name: 'accountPersonalize',
|
name: 'accountPersonalize',
|
||||||
|
@ -46,11 +46,6 @@ class _AccountScreenState extends State<AccountScreen> {
|
|||||||
'accountFriend'.tr,
|
'accountFriend'.tr,
|
||||||
'accountFriend',
|
'accountFriend',
|
||||||
),
|
),
|
||||||
(
|
|
||||||
const Icon(Icons.emoji_symbols),
|
|
||||||
'accountStickers'.tr,
|
|
||||||
'accountStickers',
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
|
@ -43,7 +43,7 @@ class _FriendScreenState extends State<FriendScreen>
|
|||||||
_relations.where((x) => x.status == 0).length;
|
_relations.where((x) => x.status == 0).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _promptAddFriend() async {
|
void promptAddFriend() async {
|
||||||
final RelationshipProvider provider = Get.find();
|
final RelationshipProvider provider = Get.find();
|
||||||
|
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
@ -146,7 +146,7 @@ class _FriendScreenState extends State<FriendScreen>
|
|||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
onPressed: () => _promptAddFriend(),
|
onPressed: () => promptAddFriend(),
|
||||||
),
|
),
|
||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
|
@ -86,17 +86,11 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
toolbarTitle: 'cropImage'.tr,
|
toolbarTitle: 'cropImage'.tr,
|
||||||
toolbarColor: Theme.of(context).colorScheme.primary,
|
toolbarColor: Theme.of(context).colorScheme.primary,
|
||||||
toolbarWidgetColor: Theme.of(context).colorScheme.onPrimary,
|
toolbarWidgetColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
aspectRatioPresets: [
|
aspectRatioPresets: [CropAspectRatioPreset.square],
|
||||||
if (position == 'avatar') CropAspectRatioPreset.square,
|
|
||||||
if (position == 'banner') _BannerCropAspectRatioPreset(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
IOSUiSettings(
|
IOSUiSettings(
|
||||||
title: 'cropImage'.tr,
|
title: 'cropImage'.tr,
|
||||||
aspectRatioPresets: [
|
aspectRatioPresets: [CropAspectRatioPreset.square],
|
||||||
if (position == 'avatar') CropAspectRatioPreset.square,
|
|
||||||
if (position == 'banner') _BannerCropAspectRatioPreset(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
WebUiSettings(
|
WebUiSettings(
|
||||||
context: context,
|
context: context,
|
||||||
@ -352,11 +346,3 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BannerCropAspectRatioPreset extends CropAspectRatioPresetData {
|
|
||||||
@override
|
|
||||||
(int, int)? get data => (16, 7);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get name => '16x7';
|
|
||||||
}
|
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|
||||||
import 'package:solian/models/pagination.dart';
|
|
||||||
import 'package:solian/models/stickers.dart';
|
|
||||||
import 'package:solian/platform.dart';
|
|
||||||
import 'package:solian/providers/auth.dart';
|
|
||||||
import 'package:solian/services.dart';
|
|
||||||
import 'package:solian/widgets/stickers/sticker_uploader.dart';
|
|
||||||
|
|
||||||
class StickerScreen extends StatefulWidget {
|
|
||||||
const StickerScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StickerScreen> createState() => _StickerScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _StickerScreenState extends State<StickerScreen> {
|
|
||||||
final PagingController<int, StickerPack> _pagingController =
|
|
||||||
PagingController(firstPageKey: 0);
|
|
||||||
|
|
||||||
Future<bool> _promptDelete(Sticker item, String prefix) async {
|
|
||||||
final AuthProvider auth = Get.find();
|
|
||||||
if (auth.isAuthorized.isFalse) return false;
|
|
||||||
|
|
||||||
final confirm = await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: Text('stickerDeletionConfirm'.tr),
|
|
||||||
content: Text(
|
|
||||||
'stickerDeletionConfirmCaption'.trParams({
|
|
||||||
'name': ':${'$prefix${item.alias}'.camelCase}:',
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: Text('cancel'.tr),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, true),
|
|
||||||
child: Text('confirm'.tr),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (confirm != true) return false;
|
|
||||||
|
|
||||||
final client = auth.configureClient('files');
|
|
||||||
final resp = await client.delete('/stickers/${item.id}');
|
|
||||||
|
|
||||||
return resp.statusCode == 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool?> _promptUploadSticker({Sticker? edit}) {
|
|
||||||
return showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => StickerUploadDialog(
|
|
||||||
edit: edit,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildEmoteEntry(Sticker item, String prefix) {
|
|
||||||
final imageUrl = ServiceFinder.buildUrl(
|
|
||||||
'files',
|
|
||||||
'/attachments/${item.attachmentId}',
|
|
||||||
);
|
|
||||||
return ListTile(
|
|
||||||
title: Text(item.name),
|
|
||||||
subtitle: Text(':${'$prefix${item.alias}'.camelCase}:'),
|
|
||||||
contentPadding: const EdgeInsets.only(left: 16, right: 14),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.edit_square),
|
|
||||||
onPressed: () {
|
|
||||||
_promptUploadSticker(edit: item).then((value) {
|
|
||||||
if (value == true) _pagingController.refresh();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.delete),
|
|
||||||
onPressed: () {
|
|
||||||
_promptDelete(item, prefix).then((value) {
|
|
||||||
if (value == true) _pagingController.refresh();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
leading: PlatformInfo.canCacheImage
|
|
||||||
? CachedNetworkImage(
|
|
||||||
imageUrl: imageUrl,
|
|
||||||
width: 28,
|
|
||||||
height: 28,
|
|
||||||
)
|
|
||||||
: Image.network(
|
|
||||||
imageUrl,
|
|
||||||
width: 28,
|
|
||||||
height: 28,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
final AuthProvider auth = Get.find();
|
|
||||||
final name = auth.userProfile.value!['name'];
|
|
||||||
_pagingController.addPageRequestListener((pageKey) async {
|
|
||||||
final client = ServiceFinder.configureClient('files');
|
|
||||||
final resp = await client.get(
|
|
||||||
'/stickers/manifest?take=10&offset=$pageKey&author=$name',
|
|
||||||
);
|
|
||||||
if (resp.statusCode == 200) {
|
|
||||||
final result = PaginationResult.fromJson(resp.body);
|
|
||||||
final out = result.data?.map((e) => StickerPack.fromJson(e)).toList();
|
|
||||||
if (out != null && result.data!.length >= 10) {
|
|
||||||
_pagingController.appendPage(out, pageKey + out.length);
|
|
||||||
} else if (out != null) {
|
|
||||||
_pagingController.appendLastPage(out);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_pagingController.error = resp.bodyString;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_pagingController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
onPressed: () {
|
|
||||||
_promptUploadSticker().then((value) {
|
|
||||||
if (value == true) _pagingController.refresh();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
body: RefreshIndicator(
|
|
||||||
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
|
||||||
child: CustomScrollView(
|
|
||||||
slivers: [
|
|
||||||
PagedSliverList<int, StickerPack>(
|
|
||||||
pagingController: _pagingController,
|
|
||||||
builderDelegate: PagedChildBuilderDelegate(
|
|
||||||
itemBuilder: (BuildContext context, item, int index) {
|
|
||||||
return ExpansionTile(
|
|
||||||
title: Text(item.name),
|
|
||||||
subtitle: Text(
|
|
||||||
item.description,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
children: item.stickers
|
|
||||||
?.map((x) => _buildEmoteEntry(x, item.prefix))
|
|
||||||
.toList() ??
|
|
||||||
List.empty(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -52,7 +52,6 @@ const i18nEnglish = {
|
|||||||
'account': 'Account',
|
'account': 'Account',
|
||||||
'accountPersonalize': 'Personalize',
|
'accountPersonalize': 'Personalize',
|
||||||
'accountPersonalizeApplied': 'Account personalize settings has been saved.',
|
'accountPersonalizeApplied': 'Account personalize settings has been saved.',
|
||||||
'accountStickers': 'Stickers',
|
|
||||||
'accountFriend': 'Friend',
|
'accountFriend': 'Friend',
|
||||||
'accountFriendNew': 'New friend',
|
'accountFriendNew': 'New friend',
|
||||||
'accountFriendNewHint':
|
'accountFriendNewHint':
|
||||||
@ -163,11 +162,9 @@ const i18nEnglish = {
|
|||||||
'attachmentAutoUpload': 'Auto Upload',
|
'attachmentAutoUpload': 'Auto Upload',
|
||||||
'attachmentUploadQueue': 'Upload Queue',
|
'attachmentUploadQueue': 'Upload Queue',
|
||||||
'attachmentUploadQueueStart': 'Start All',
|
'attachmentUploadQueueStart': 'Start All',
|
||||||
'attachmentUploadInProgress':
|
'attachmentUploadInProgress': 'There are attachments being uploaded. Please wait until all attachments have been uploaded before proceeding...',
|
||||||
'There are attachments being uploaded. Please wait until all attachments have been uploaded before proceeding...',
|
|
||||||
'attachmentAttached': 'Exists Files',
|
'attachmentAttached': 'Exists Files',
|
||||||
'attachmentUploadBlocked':
|
'attachmentUploadBlocked': 'Upload blocked, there is currently a task in progress...',
|
||||||
'Upload blocked, there is currently a task in progress...',
|
|
||||||
'attachmentAdd': 'Attach attachments',
|
'attachmentAdd': 'Attach attachments',
|
||||||
'attachmentAddGalleryPhoto': 'Gallery photo',
|
'attachmentAddGalleryPhoto': 'Gallery photo',
|
||||||
'attachmentAddGalleryVideo': 'Gallery video',
|
'attachmentAddGalleryVideo': 'Gallery video',
|
||||||
@ -176,8 +173,7 @@ const i18nEnglish = {
|
|||||||
'attachmentAddClipboard': 'Paste file',
|
'attachmentAddClipboard': 'Paste file',
|
||||||
'attachmentAddFile': 'Attach file',
|
'attachmentAddFile': 'Attach file',
|
||||||
'attachmentAddLink': 'Link attachments',
|
'attachmentAddLink': 'Link attachments',
|
||||||
'attachmentAddLinkHint':
|
'attachmentAddLinkHint': 'Enter attachment serial number to link that attachment',
|
||||||
'Enter attachment serial number to link that attachment',
|
|
||||||
'attachmentAddLinkInput': 'Serial number',
|
'attachmentAddLinkInput': 'Serial number',
|
||||||
'attachmentSetting': 'Adjust attachment',
|
'attachmentSetting': 'Adjust attachment',
|
||||||
'attachmentAlt': 'Alternative text',
|
'attachmentAlt': 'Alternative text',
|
||||||
@ -321,10 +317,8 @@ const i18nEnglish = {
|
|||||||
'bsCheckForUpdate': 'Checking For Updates',
|
'bsCheckForUpdate': 'Checking For Updates',
|
||||||
'bsCheckForUpdateFailed': 'Unable to Check Updates',
|
'bsCheckForUpdateFailed': 'Unable to Check Updates',
|
||||||
'bsCheckForUpdateNew': 'Found New Version',
|
'bsCheckForUpdateNew': 'Found New Version',
|
||||||
'bsCheckForUpdateDescApple':
|
'bsCheckForUpdateDescApple': 'Please head to TestFlight and update your app to latest version to prevent error happens and get latest functions.',
|
||||||
'Please head to TestFlight and update your app to latest version to prevent error happens and get latest functions.',
|
'bsCheckForUpdateDescCommon': 'Please head to our website download and install latest version of application to prevent error happens and get latest functions.',
|
||||||
'bsCheckForUpdateDescCommon':
|
|
||||||
'Please head to our website download and install latest version of application to prevent error happens and get latest functions.',
|
|
||||||
'bsCheckingServer': 'Checking Server Status',
|
'bsCheckingServer': 'Checking Server Status',
|
||||||
'bsCheckingServerFail':
|
'bsCheckingServerFail':
|
||||||
'Unable connect to server, check your network connection',
|
'Unable connect to server, check your network connection',
|
||||||
@ -342,22 +336,7 @@ const i18nEnglish = {
|
|||||||
'themeColorMiku': 'Miku Blue',
|
'themeColorMiku': 'Miku Blue',
|
||||||
'themeColorKagamine': 'Kagamine Yellow',
|
'themeColorKagamine': 'Kagamine Yellow',
|
||||||
'themeColorLuka': 'Luka Pink',
|
'themeColorLuka': 'Luka Pink',
|
||||||
'stickerDeletionConfirm': 'Confirm sticker delete',
|
|
||||||
'stickerDeletionConfirmCaption':
|
|
||||||
'Are you sure to delete sticker @name? This action cannot be undo.',
|
|
||||||
'themeColorApplied': 'Global theme color has been applied.',
|
'themeColorApplied': 'Global theme color has been applied.',
|
||||||
'attachmentSaved': 'Attachment saved to your system album.',
|
'attachmentSaved': 'Attachment saved to your system album.',
|
||||||
'cropImage': 'Crop Image',
|
'cropImage': 'Crop Image',
|
||||||
'stickerUploader': 'Upload sticker',
|
|
||||||
'stickerUploaderAttachmentNew': 'Upload new attachment',
|
|
||||||
'stickerUploaderAttachment': 'Attachment serial number',
|
|
||||||
'stickerUploaderPack': 'Sticker pack serial number',
|
|
||||||
'stickerUploaderPackHint':
|
|
||||||
'Don\'t have pack id? Head to creator platform and create one!',
|
|
||||||
'stickerUploaderAlias': 'Alias',
|
|
||||||
'stickerUploaderAliasHint':
|
|
||||||
'Will be used as a placeholder with the sticker pack prefix when entered.',
|
|
||||||
'stickerUploaderName': 'Name',
|
|
||||||
'stickerUploaderNameHint':
|
|
||||||
'A human-friendly name given to the user in the sticker selection interface.',
|
|
||||||
};
|
};
|
||||||
|
@ -52,7 +52,6 @@ const i18nSimplifiedChinese = {
|
|||||||
'account': '账号',
|
'account': '账号',
|
||||||
'accountPersonalize': '个性化',
|
'accountPersonalize': '个性化',
|
||||||
'accountPersonalizeApplied': '账户的个性化设置已保存。',
|
'accountPersonalizeApplied': '账户的个性化设置已保存。',
|
||||||
'accountStickers': '贴图',
|
|
||||||
'accountFriend': '好友',
|
'accountFriend': '好友',
|
||||||
'accountFriendNew': '添加好友',
|
'accountFriendNew': '添加好友',
|
||||||
'accountFriendNewHint': '使用他人的用户名来发送一个好友请求吧!',
|
'accountFriendNewHint': '使用他人的用户名来发送一个好友请求吧!',
|
||||||
@ -315,17 +314,6 @@ const i18nSimplifiedChinese = {
|
|||||||
'themeColorKagamine': '镜音黄',
|
'themeColorKagamine': '镜音黄',
|
||||||
'themeColorLuka': '流音粉',
|
'themeColorLuka': '流音粉',
|
||||||
'themeColorApplied': '全局主题颜色已应用',
|
'themeColorApplied': '全局主题颜色已应用',
|
||||||
'stickerDeletionConfirm': '确认删除贴图',
|
|
||||||
'stickerDeletionConfirmCaption': '你确认要删除贴图 @name 吗?该操作不可撤销。',
|
|
||||||
'attachmentSaved': '附件已保存到系统相册',
|
'attachmentSaved': '附件已保存到系统相册',
|
||||||
'cropImage': '裁剪图片',
|
'cropImage': '裁剪图片',
|
||||||
'stickerUploader': '上传贴图',
|
|
||||||
'stickerUploaderAttachmentNew': '上传附件',
|
|
||||||
'stickerUploaderAttachment': '附件序列号',
|
|
||||||
'stickerUploaderPack': '贴图包序号',
|
|
||||||
'stickerUploaderPackHint': '没有该序号?请转到我们的创作者平台创建一个贴图包。',
|
|
||||||
'stickerUploaderAlias': '贴图别名',
|
|
||||||
'stickerUploaderAliasHint': '将会在输入时使用和贴图包前缀组成占位符。',
|
|
||||||
'stickerUploaderName': '贴图名称',
|
|
||||||
'stickerUploaderNameHint': '在贴图选择界面提供给用户的人类友好名称。',
|
|
||||||
};
|
};
|
||||||
|
@ -226,7 +226,7 @@ class _AccountStatusEditorDialogState extends State<AccountStatusEditorDialog> {
|
|||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 5),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _clearAtController,
|
controller: _clearAtController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@ -238,7 +238,7 @@ class _AccountStatusEditorDialogState extends State<AccountStatusEditorDialog> {
|
|||||||
),
|
),
|
||||||
onTap: () => selectClearAt(),
|
onTap: () => selectClearAt(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 5),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
@ -281,7 +281,7 @@ class _AccountStatusEditorDialogState extends State<AccountStatusEditorDialog> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 5),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
|
@ -23,26 +23,16 @@ import 'package:solian/widgets/attachments/attachment_fullscreen.dart';
|
|||||||
|
|
||||||
class AttachmentEditorPopup extends StatefulWidget {
|
class AttachmentEditorPopup extends StatefulWidget {
|
||||||
final String usage;
|
final String usage;
|
||||||
final bool singleMode;
|
final List<int> initialAttachments;
|
||||||
final bool imageOnly;
|
|
||||||
final bool autoUpload;
|
|
||||||
final double? imageMaxWidth;
|
|
||||||
final double? imageMaxHeight;
|
|
||||||
final List<int>? initialAttachments;
|
|
||||||
final void Function(int) onAdd;
|
final void Function(int) onAdd;
|
||||||
final void Function(int) onRemove;
|
final void Function(int) onRemove;
|
||||||
|
|
||||||
const AttachmentEditorPopup({
|
const AttachmentEditorPopup({
|
||||||
super.key,
|
super.key,
|
||||||
required this.usage,
|
required this.usage,
|
||||||
|
required this.initialAttachments,
|
||||||
required this.onAdd,
|
required this.onAdd,
|
||||||
required this.onRemove,
|
required this.onRemove,
|
||||||
this.singleMode = false,
|
|
||||||
this.imageOnly = false,
|
|
||||||
this.autoUpload = false,
|
|
||||||
this.imageMaxWidth,
|
|
||||||
this.imageMaxHeight,
|
|
||||||
this.initialAttachments,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -53,7 +43,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
final _imagePicker = ImagePicker();
|
final _imagePicker = ImagePicker();
|
||||||
final AttachmentUploaderController _uploadController = Get.find();
|
final AttachmentUploaderController _uploadController = Get.find();
|
||||||
|
|
||||||
late bool _isAutoUpload = widget.autoUpload;
|
bool _isAutoUpload = false;
|
||||||
|
|
||||||
bool _isBusy = false;
|
bool _isBusy = false;
|
||||||
bool _isFirstTimeBusy = true;
|
bool _isFirstTimeBusy = true;
|
||||||
@ -64,28 +54,13 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return;
|
if (auth.isAuthorized.isFalse) return;
|
||||||
|
|
||||||
if (widget.singleMode) {
|
final medias = await _imagePicker.pickMultiImage();
|
||||||
final medias = await _imagePicker.pickMultiImage(
|
if (medias.isEmpty) return;
|
||||||
maxWidth: widget.imageMaxWidth,
|
|
||||||
maxHeight: widget.imageMaxHeight,
|
|
||||||
);
|
|
||||||
if (medias.isEmpty) return;
|
|
||||||
|
|
||||||
_enqueueTaskBatch(medias.map((x) {
|
_enqueueTaskBatch(medias.map((x) {
|
||||||
final file = File(x.path);
|
final file = File(x.path);
|
||||||
return AttachmentUploadTask(file: file, usage: widget.usage);
|
return AttachmentUploadTask(file: file, usage: widget.usage);
|
||||||
}));
|
}));
|
||||||
} else {
|
|
||||||
final media = await _imagePicker.pickMedia(
|
|
||||||
maxWidth: widget.imageMaxWidth,
|
|
||||||
maxHeight: widget.imageMaxHeight,
|
|
||||||
);
|
|
||||||
if (media == null) return;
|
|
||||||
|
|
||||||
_enqueueTask(
|
|
||||||
AttachmentUploadTask(file: File(media.path), usage: widget.usage),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pickVideoToUpload() async {
|
Future<void> _pickVideoToUpload() async {
|
||||||
@ -189,7 +164,6 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
if (result != null) {
|
if (result != null) {
|
||||||
widget.onAdd(result.id);
|
widget.onAdd(result.id);
|
||||||
setState(() => _attachments.add(result));
|
setState(() => _attachments.add(result));
|
||||||
if (widget.singleMode) Navigator.pop(context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,11 +179,9 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
widget.usage,
|
widget.usage,
|
||||||
null,
|
null,
|
||||||
(item) {
|
(item) {
|
||||||
if (item == null) return;
|
|
||||||
widget.onAdd(item.id);
|
widget.onAdd(item.id);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _attachments.add(item));
|
setState(() => _attachments.add(item));
|
||||||
if (widget.singleMode) Navigator.pop(context);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -237,12 +209,12 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
void _revertMetadataList() {
|
void _revertMetadataList() {
|
||||||
final AttachmentProvider attach = Get.find();
|
final AttachmentProvider attach = Get.find();
|
||||||
|
|
||||||
if (widget.initialAttachments?.isEmpty ?? true) {
|
if (widget.initialAttachments.isEmpty) {
|
||||||
_isFirstTimeBusy = false;
|
_isFirstTimeBusy = false;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
_attachments = List.filled(
|
_attachments = List.filled(
|
||||||
widget.initialAttachments!.length,
|
widget.initialAttachments.length,
|
||||||
null,
|
null,
|
||||||
growable: true,
|
growable: true,
|
||||||
);
|
);
|
||||||
@ -250,9 +222,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
|
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
attach
|
attach.listMetadata(widget.initialAttachments).then((result) {
|
||||||
.listMetadata(widget.initialAttachments ?? List.empty())
|
|
||||||
.then((result) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_attachments = result;
|
_attachments = result;
|
||||||
_isBusy = false;
|
_isBusy = false;
|
||||||
@ -379,13 +349,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
child: Icon(Icons.check),
|
child: Icon(Icons.check),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (element.error != null)
|
if (!element.isCompleted && canBeCrop)
|
||||||
IconButton(
|
|
||||||
tooltip: element.error!.toString(),
|
|
||||||
icon: const Icon(Icons.warning),
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
if (!element.isCompleted && element.error == null && canBeCrop)
|
|
||||||
Obx(
|
Obx(
|
||||||
() => IconButton(
|
() => IconButton(
|
||||||
color: Colors.teal,
|
color: Colors.teal,
|
||||||
@ -398,7 +362,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!element.isCompleted && !element.isUploading && element.error == null)
|
if (!element.isCompleted && !element.isUploading)
|
||||||
Obx(
|
Obx(
|
||||||
() => IconButton(
|
() => IconButton(
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
@ -410,13 +374,9 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
_uploadController
|
_uploadController
|
||||||
.performSingleTask(index)
|
.performSingleTask(index)
|
||||||
.then((r) {
|
.then((r) {
|
||||||
if (r == null) return;
|
|
||||||
widget.onAdd(r.id);
|
widget.onAdd(r.id);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _attachments.add(r));
|
setState(() => _attachments.add(r));
|
||||||
if (widget.singleMode) {
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -559,7 +519,6 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
widget.onAdd(r.id);
|
widget.onAdd(r.id);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _attachments.add(r));
|
setState(() => _attachments.add(r));
|
||||||
if (widget.singleMode) Navigator.pop(context);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -711,7 +670,6 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
ignoring: _uploadController.isUploading.value,
|
ignoring: _uploadController.isUploading.value,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 64,
|
height: 64,
|
||||||
width: MediaQuery.of(context).size.width,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
top: BorderSide(
|
top: BorderSide(
|
||||||
@ -728,10 +686,9 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
runAlignment: WrapAlignment.center,
|
runAlignment: WrapAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if ((PlatformInfo.isDesktop ||
|
if (PlatformInfo.isDesktop ||
|
||||||
PlatformInfo.isIOS ||
|
PlatformInfo.isIOS ||
|
||||||
PlatformInfo.isWeb) &&
|
PlatformInfo.isWeb)
|
||||||
!widget.imageOnly)
|
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
icon: const Icon(Icons.paste),
|
icon: const Icon(Icons.paste),
|
||||||
label: Text('attachmentAddClipboard'.tr),
|
label: Text('attachmentAddClipboard'.tr),
|
||||||
@ -744,40 +701,36 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
style: const ButtonStyle(visualDensity: density),
|
style: const ButtonStyle(visualDensity: density),
|
||||||
onPressed: () => _pickPhotoToUpload(),
|
onPressed: () => _pickPhotoToUpload(),
|
||||||
),
|
),
|
||||||
if (!widget.imageOnly)
|
ElevatedButton.icon(
|
||||||
ElevatedButton.icon(
|
icon: const Icon(Icons.add_road),
|
||||||
icon: const Icon(Icons.add_road),
|
label: Text('attachmentAddGalleryVideo'.tr),
|
||||||
label: Text('attachmentAddGalleryVideo'.tr),
|
style: const ButtonStyle(visualDensity: density),
|
||||||
style: const ButtonStyle(visualDensity: density),
|
onPressed: () => _pickVideoToUpload(),
|
||||||
onPressed: () => _pickVideoToUpload(),
|
),
|
||||||
),
|
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
icon: const Icon(Icons.photo_camera_back),
|
icon: const Icon(Icons.photo_camera_back),
|
||||||
label: Text('attachmentAddCameraPhoto'.tr),
|
label: Text('attachmentAddCameraPhoto'.tr),
|
||||||
style: const ButtonStyle(visualDensity: density),
|
style: const ButtonStyle(visualDensity: density),
|
||||||
onPressed: () => _takeMediaToUpload(false),
|
onPressed: () => _takeMediaToUpload(false),
|
||||||
),
|
),
|
||||||
if (!widget.imageOnly)
|
ElevatedButton.icon(
|
||||||
ElevatedButton.icon(
|
icon: const Icon(Icons.video_camera_back_outlined),
|
||||||
icon: const Icon(Icons.video_camera_back_outlined),
|
label: Text('attachmentAddCameraVideo'.tr),
|
||||||
label: Text('attachmentAddCameraVideo'.tr),
|
style: const ButtonStyle(visualDensity: density),
|
||||||
style: const ButtonStyle(visualDensity: density),
|
onPressed: () => _takeMediaToUpload(true),
|
||||||
onPressed: () => _takeMediaToUpload(true),
|
),
|
||||||
),
|
ElevatedButton.icon(
|
||||||
if (!widget.imageOnly)
|
icon: const Icon(Icons.file_present_rounded),
|
||||||
ElevatedButton.icon(
|
label: Text('attachmentAddFile'.tr),
|
||||||
icon: const Icon(Icons.file_present_rounded),
|
style: const ButtonStyle(visualDensity: density),
|
||||||
label: Text('attachmentAddFile'.tr),
|
onPressed: () => _pickFileToUpload(),
|
||||||
style: const ButtonStyle(visualDensity: density),
|
),
|
||||||
onPressed: () => _pickFileToUpload(),
|
ElevatedButton.icon(
|
||||||
),
|
icon: const Icon(Icons.link),
|
||||||
if (!widget.imageOnly)
|
label: Text('attachmentAddFile'.tr),
|
||||||
ElevatedButton.icon(
|
style: const ButtonStyle(visualDensity: density),
|
||||||
icon: const Icon(Icons.link),
|
onPressed: () => _linkAttachments(),
|
||||||
label: Text('attachmentAddFile'.tr),
|
),
|
||||||
style: const ButtonStyle(visualDensity: density),
|
|
||||||
onPressed: () => _linkAttachments(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 12),
|
).paddingSymmetric(horizontal: 12),
|
||||||
),
|
),
|
||||||
|
@ -257,10 +257,6 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
|
|||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
'#${widget.item.id}',
|
|
||||||
style: metaTextStyle,
|
|
||||||
),
|
|
||||||
if (widget.item.metadata?['width'] != null &&
|
if (widget.item.metadata?['width'] != null &&
|
||||||
widget.item.metadata?['height'] != null)
|
widget.item.metadata?['height'] != null)
|
||||||
Text(
|
Text(
|
||||||
|
@ -57,7 +57,7 @@ class _ChannelMemberListPopupState extends State<ChannelMemberListPopup> {
|
|||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _promptAddMember() async {
|
void promptAddMember() async {
|
||||||
final input = await showModalBottomSheet(
|
final input = await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
@ -141,7 +141,7 @@ class _ChannelMemberListPopupState extends State<ChannelMemberListPopup> {
|
|||||||
'channelMembersAddHint'
|
'channelMembersAddHint'
|
||||||
.trParams({'channel': '#${widget.channel.alias}'}),
|
.trParams({'channel': '#${widget.channel.alias}'}),
|
||||||
),
|
),
|
||||||
onTap: () => _promptAddMember(),
|
onTap: () => promptAddMember(),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
@ -1,211 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:solian/exts.dart';
|
|
||||||
import 'package:solian/models/stickers.dart';
|
|
||||||
import 'package:solian/providers/auth.dart';
|
|
||||||
import 'package:solian/widgets/attachments/attachment_editor.dart';
|
|
||||||
|
|
||||||
class StickerUploadDialog extends StatefulWidget {
|
|
||||||
final Sticker? edit;
|
|
||||||
|
|
||||||
const StickerUploadDialog({super.key, this.edit});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StickerUploadDialog> createState() => _StickerUploadDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _StickerUploadDialogState extends State<StickerUploadDialog> {
|
|
||||||
final TextEditingController _attachmentController = TextEditingController();
|
|
||||||
final TextEditingController _packController = TextEditingController();
|
|
||||||
final TextEditingController _aliasController = TextEditingController();
|
|
||||||
final TextEditingController _nameController = TextEditingController();
|
|
||||||
|
|
||||||
Color get _unFocusColor =>
|
|
||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
|
||||||
|
|
||||||
bool _isBusy = false;
|
|
||||||
|
|
||||||
void _promptUploadNewAttachment() {
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AttachmentEditorPopup(
|
|
||||||
usage: 'sticker',
|
|
||||||
singleMode: true,
|
|
||||||
imageOnly: true,
|
|
||||||
autoUpload: true,
|
|
||||||
imageMaxHeight: 28,
|
|
||||||
imageMaxWidth: 28,
|
|
||||||
onAdd: (value) {
|
|
||||||
setState(() {
|
|
||||||
_attachmentController.text = value.toString();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
initialAttachments: const [],
|
|
||||||
onRemove: (_) {},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _applySticker() async {
|
|
||||||
final AuthProvider auth = Get.find();
|
|
||||||
if (auth.isAuthorized.isFalse) return;
|
|
||||||
|
|
||||||
if ([
|
|
||||||
_nameController.text.isEmpty,
|
|
||||||
_aliasController.text.isEmpty,
|
|
||||||
_packController.text.isEmpty,
|
|
||||||
_attachmentController.text.isEmpty,
|
|
||||||
].any((x) => x)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
|
||||||
|
|
||||||
Response resp;
|
|
||||||
final client = auth.configureClient('files');
|
|
||||||
if (widget.edit == null) {
|
|
||||||
resp = await client.post('/stickers', {
|
|
||||||
'name': _nameController.text,
|
|
||||||
'alias': _aliasController.text,
|
|
||||||
'pack_id': int.tryParse(_packController.text),
|
|
||||||
'attachment_id': int.tryParse(_attachmentController.text),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resp = await client.put('/stickers/${widget.edit!.id}', {
|
|
||||||
'name': _nameController.text,
|
|
||||||
'alias': _aliasController.text,
|
|
||||||
'pack_id': int.tryParse(_packController.text),
|
|
||||||
'attachment_id': int.tryParse(_attachmentController.text),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() => _isBusy = false);
|
|
||||||
|
|
||||||
if (resp.statusCode != 200) {
|
|
||||||
context.showErrorDialog(resp.bodyString);
|
|
||||||
} else {
|
|
||||||
Navigator.pop(context, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
if (widget.edit != null) {
|
|
||||||
_attachmentController.text = widget.edit!.attachmentId.toString();
|
|
||||||
_packController.text = widget.edit!.packId.toString();
|
|
||||||
_aliasController.text = widget.edit!.alias;
|
|
||||||
_nameController.text = widget.edit!.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_attachmentController.dispose();
|
|
||||||
_packController.dispose();
|
|
||||||
_aliasController.dispose();
|
|
||||||
_nameController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text('stickerUploader'.tr),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
title: Text('stickerUploaderAttachmentNew'.tr),
|
|
||||||
contentPadding: const EdgeInsets.only(left: 16, right: 13),
|
|
||||||
trailing: const Icon(Icons.chevron_right),
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
_promptUploadNewAttachment();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _attachmentController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
isDense: true,
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
prefixText: '#',
|
|
||||||
labelText: 'stickerUploaderAttachment'.tr,
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _packController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
isDense: true,
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
prefixText: '#',
|
|
||||||
labelText: 'stickerUploaderPack'.tr,
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 6),
|
|
||||||
child: Text(
|
|
||||||
'stickerUploaderPackHint'.tr,
|
|
||||||
style: TextStyle(color: _unFocusColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
controller: _aliasController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
isDense: true,
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
labelText: 'stickerUploaderAlias'.tr,
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 6),
|
|
||||||
child: Text(
|
|
||||||
'stickerUploaderAliasHint'.tr,
|
|
||||||
style: TextStyle(color: _unFocusColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
controller: _nameController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
isDense: true,
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
labelText: 'stickerUploaderName'.tr,
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 4),
|
|
||||||
child: Text(
|
|
||||||
'stickerUploaderNameHint'.tr,
|
|
||||||
style: TextStyle(color: _unFocusColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
|
||||||
),
|
|
||||||
onPressed: _isBusy ? null : () => Navigator.pop(context),
|
|
||||||
child: Text('cancel'.tr),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: _isBusy ? null : () => _applySticker(),
|
|
||||||
child: Text('apply'.tr),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user