Compare commits
6 Commits
a319bd7f8c
...
9ac4a940dd
Author | SHA1 | Date | |
---|---|---|---|
9ac4a940dd | |||
ec050ab712 | |||
77e3ce8bcc | |||
f5dcf71e10 | |||
7fc18b40db | |||
8c8ab24c9e |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 736 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.5 KiB |
@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#FFFFFFFF</color>
|
<color name="ic_launcher_background">#FFFFFFFF</color>
|
||||||
|
<color name="ic_notification_background">#00000000</color>
|
||||||
</resources>
|
</resources>
|
@ -139,6 +139,8 @@
|
|||||||
"fieldPostTitle": "Title",
|
"fieldPostTitle": "Title",
|
||||||
"fieldPostDescription": "Description",
|
"fieldPostDescription": "Description",
|
||||||
"fieldPostTags": "Tags",
|
"fieldPostTags": "Tags",
|
||||||
|
"fieldPostAlias": "Alias",
|
||||||
|
"fieldPostAliasHint": "Optional, used to represent the post in URL, should follow URL-Safe.",
|
||||||
"postPublish": "Publish",
|
"postPublish": "Publish",
|
||||||
"postPosted": "Post has been posted.",
|
"postPosted": "Post has been posted.",
|
||||||
"postPublishedAt": "Published At",
|
"postPublishedAt": "Published At",
|
||||||
|
@ -123,6 +123,8 @@
|
|||||||
"fieldPostTitle": "标题",
|
"fieldPostTitle": "标题",
|
||||||
"fieldPostDescription": "描述",
|
"fieldPostDescription": "描述",
|
||||||
"fieldPostTags": "标签",
|
"fieldPostTags": "标签",
|
||||||
|
"fieldPostAlias": "别名",
|
||||||
|
"fieldPostAliasHint": "可选项,用于在 URL 中表示该帖子,应遵循 URL-Safe 的原则。",
|
||||||
"postPublish": "发布",
|
"postPublish": "发布",
|
||||||
"postPublishedAt": "发布于",
|
"postPublishedAt": "发布于",
|
||||||
"postPublishedUntil": "取消发布于",
|
"postPublishedUntil": "取消发布于",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -152,6 +153,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
final TextEditingController contentController = TextEditingController();
|
final TextEditingController contentController = TextEditingController();
|
||||||
final TextEditingController titleController = TextEditingController();
|
final TextEditingController titleController = TextEditingController();
|
||||||
final TextEditingController descriptionController = TextEditingController();
|
final TextEditingController descriptionController = TextEditingController();
|
||||||
|
final TextEditingController aliasController = TextEditingController();
|
||||||
|
|
||||||
PostWriteController() {
|
PostWriteController() {
|
||||||
titleController.addListener(() => notifyListeners());
|
titleController.addListener(() => notifyListeners());
|
||||||
@ -198,6 +200,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
titleController.text = post.body['title'] ?? '';
|
titleController.text = post.body['title'] ?? '';
|
||||||
descriptionController.text = post.body['description'] ?? '';
|
descriptionController.text = post.body['description'] ?? '';
|
||||||
contentController.text = post.body['content'] ?? '';
|
contentController.text = post.body['content'] ?? '';
|
||||||
|
aliasController.text = post.alias ?? '';
|
||||||
publishedAt = post.publishedAt;
|
publishedAt = post.publishedAt;
|
||||||
publishedUntil = post.publishedUntil;
|
publishedUntil = post.publishedUntil;
|
||||||
visibleUsers = List.from(post.visibleUsersList ?? []);
|
visibleUsers = List.from(post.visibleUsersList ?? []);
|
||||||
@ -269,7 +272,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> post(BuildContext context) async {
|
Future<void> sendPost(BuildContext context) async {
|
||||||
if (isBusy || publisher == null) return;
|
if (isBusy || publisher == null) return;
|
||||||
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
@ -305,12 +308,14 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
place.$2,
|
place.$2,
|
||||||
onProgress: (progress) {
|
onProgress: (progress) {
|
||||||
// Calculate overall progress for attachments
|
// Calculate overall progress for attachments
|
||||||
progress = ((i + progress) / attachments.length) * kAttachmentProgressWeight;
|
progress = math.max(((i + progress) / attachments.length) * kAttachmentProgressWeight, progress);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
progress = (i + 1) / attachments.length * kAttachmentProgressWeight;
|
||||||
attachments[i] = PostWriteMedia(item);
|
attachments[i] = PostWriteMedia(item);
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
isBusy = false;
|
isBusy = false;
|
||||||
@ -334,6 +339,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
data: {
|
data: {
|
||||||
'publisher': publisher!.id,
|
'publisher': publisher!.id,
|
||||||
'content': contentController.text,
|
'content': contentController.text,
|
||||||
|
if (aliasController.text.isNotEmpty) 'alias': aliasController.text,
|
||||||
if (titleController.text.isNotEmpty) 'title': titleController.text,
|
if (titleController.text.isNotEmpty) 'title': titleController.text,
|
||||||
if (descriptionController.text.isNotEmpty) 'description': descriptionController.text,
|
if (descriptionController.text.isNotEmpty) 'description': descriptionController.text,
|
||||||
if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.rid,
|
if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.rid,
|
||||||
|
@ -41,8 +41,7 @@ class SnAttachmentProvider {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SnAttachment?>> getMultiple(List<String> rids,
|
Future<List<SnAttachment?>> getMultiple(List<String> rids, {noCache = false}) async {
|
||||||
{noCache = false}) async {
|
|
||||||
final result = List<SnAttachment?>.filled(rids.length, null);
|
final result = List<SnAttachment?>.filled(rids.length, null);
|
||||||
final Map<String, int> randomMapping = {};
|
final Map<String, int> randomMapping = {};
|
||||||
for (int i = 0; i < rids.length; i++) {
|
for (int i = 0; i < rids.length; i++) {
|
||||||
@ -63,9 +62,7 @@ class SnAttachmentProvider {
|
|||||||
'id': pendingFetch.join(','),
|
'id': pendingFetch.join(','),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final out = resp.data['data']
|
final out = resp.data['data'].map((e) => e['id'] == 0 ? null : SnAttachment.fromJson(e)).toList();
|
||||||
.map((e) => e['id'] == 0 ? null : SnAttachment.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (final item in out) {
|
for (final item in out) {
|
||||||
if (item == null) continue;
|
if (item == null) continue;
|
||||||
@ -79,10 +76,7 @@ class SnAttachmentProvider {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, String> mimetypeOverrides = {
|
static Map<String, String> mimetypeOverrides = {'mov': 'video/quicktime', 'mp4': 'video/mp4'};
|
||||||
'mov': 'video/quicktime',
|
|
||||||
'mp4': 'video/mp4'
|
|
||||||
};
|
|
||||||
|
|
||||||
Future<SnAttachment> directUploadOne(
|
Future<SnAttachment> directUploadOne(
|
||||||
Uint8List data,
|
Uint8List data,
|
||||||
@ -93,11 +87,8 @@ class SnAttachmentProvider {
|
|||||||
Function(double progress)? onProgress,
|
Function(double progress)? onProgress,
|
||||||
}) async {
|
}) async {
|
||||||
final filePayload = MultipartFile.fromBytes(data, filename: filename);
|
final filePayload = MultipartFile.fromBytes(data, filename: filename);
|
||||||
final fileAlt = filename.contains('.')
|
final fileAlt = filename.contains('.') ? filename.substring(0, filename.lastIndexOf('.')) : filename;
|
||||||
? filename.substring(0, filename.lastIndexOf('.'))
|
final fileExt = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||||
: filename;
|
|
||||||
final fileExt =
|
|
||||||
filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
|
||||||
|
|
||||||
String? mimetypeOverride;
|
String? mimetypeOverride;
|
||||||
if (mimetype != null) {
|
if (mimetype != null) {
|
||||||
@ -133,11 +124,8 @@ class SnAttachmentProvider {
|
|||||||
Map<String, dynamic>? metadata, {
|
Map<String, dynamic>? metadata, {
|
||||||
String? mimetype,
|
String? mimetype,
|
||||||
}) async {
|
}) async {
|
||||||
final fileAlt = filename.contains('.')
|
final fileAlt = filename.contains('.') ? filename.substring(0, filename.lastIndexOf('.')) : filename;
|
||||||
? filename.substring(0, filename.lastIndexOf('.'))
|
final fileExt = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||||
: filename;
|
|
||||||
final fileExt =
|
|
||||||
filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
|
||||||
|
|
||||||
String? mimetypeOverride;
|
String? mimetypeOverride;
|
||||||
if (mimetype == null && mimetypeOverrides.keys.contains(fileExt)) {
|
if (mimetype == null && mimetypeOverrides.keys.contains(fileExt)) {
|
||||||
@ -155,10 +143,7 @@ class SnAttachmentProvider {
|
|||||||
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (SnAttachment.fromJson(resp.data['meta']), resp.data['chunk_size'] as int);
|
||||||
SnAttachment.fromJson(resp.data['meta']),
|
|
||||||
resp.data['chunk_size'] as int
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SnAttachment> _chunkedUploadOnePart(
|
Future<SnAttachment> _chunkedUploadOnePart(
|
||||||
@ -200,24 +185,17 @@ class SnAttachmentProvider {
|
|||||||
(entry.value + 1) * chunkSize,
|
(entry.value + 1) * chunkSize,
|
||||||
await file.length(),
|
await file.length(),
|
||||||
);
|
);
|
||||||
final data = Uint8List.fromList(await file
|
final data = Uint8List.fromList(await file.openRead(beginCursor, endCursor).expand((chunk) => chunk).toList());
|
||||||
.openRead(beginCursor, endCursor)
|
|
||||||
.expand((chunk) => chunk)
|
|
||||||
.toList());
|
|
||||||
|
|
||||||
place = await _chunkedUploadOnePart(
|
place = await _chunkedUploadOnePart(
|
||||||
data,
|
data,
|
||||||
place.rid,
|
place.rid,
|
||||||
entry.key,
|
entry.key,
|
||||||
onProgress: (chunkProgress) {
|
|
||||||
final overallProgress =
|
|
||||||
(currentTask + chunkProgress) / chunks.length;
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(overallProgress);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final overallProgress = currentTask / chunks.length;
|
||||||
|
onProgress?.call(overallProgress);
|
||||||
|
|
||||||
currentTask++;
|
currentTask++;
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ class _HomeDashUpdateWidget extends StatelessWidget {
|
|||||||
final model = UpdateModel(
|
final model = UpdateModel(
|
||||||
'https://files.solsynth.dev/d/production01/solian/app-arm64-v8a-release.apk',
|
'https://files.solsynth.dev/d/production01/solian/app-arm64-v8a-release.apk',
|
||||||
'solian-app-release-${config.updatableVersion!}.apk',
|
'solian-app-release-${config.updatableVersion!}.apk',
|
||||||
'ic_notification',
|
'ic_launcher',
|
||||||
'https://apps.apple.com/us/app/solian/id6499032345',
|
'https://apps.apple.com/us/app/solian/id6499032345',
|
||||||
);
|
);
|
||||||
AzhonAppUpdate.update(model);
|
AzhonAppUpdate.update(model);
|
||||||
|
@ -13,6 +13,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import 'package:pasteboard/pasteboard.dart';
|
import 'package:pasteboard/pasteboard.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/controllers/post_write_controller.dart';
|
import 'package:surface/controllers/post_write_controller.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
@ -71,11 +72,14 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
final resp = await sn.client.get('/cgi/co/publishers/me');
|
final resp = await sn.client.get('/cgi/co/publishers/me');
|
||||||
_publishers = List<SnPublisher>.from(
|
_publishers = List<SnPublisher>.from(
|
||||||
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
||||||
);
|
);
|
||||||
_writeController.setPublisher(_publishers?.firstOrNull);
|
final beforeId = config.prefs.getInt('int_last_publisher_id');
|
||||||
|
_writeController
|
||||||
|
.setPublisher(_publishers?.where((ele) => ele.id == beforeId).firstOrNull ?? _publishers?.firstOrNull);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -265,6 +269,8 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_writeController.setPublisher(value);
|
_writeController.setPublisher(value);
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
|
config.prefs.setInt('int_last_publisher_id', value.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buttonStyleData: const ButtonStyleData(
|
buttonStyleData: const ButtonStyleData(
|
||||||
@ -496,7 +502,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
onPressed: (_writeController.isBusy || _writeController.publisher == null)
|
onPressed: (_writeController.isBusy || _writeController.publisher == null)
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
_writeController.post(context).then((_) {
|
_writeController.sendPost(context).then((_) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,8 @@ import 'package:responsive_framework/responsive_framework.dart';
|
|||||||
import 'package:screenshot/screenshot.dart';
|
import 'package:screenshot/screenshot.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
|
import 'package:surface/providers/link_preview.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
@ -83,6 +85,8 @@ class PostItem extends StatelessWidget {
|
|||||||
child: MultiProvider(
|
child: MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
Provider<SnNetworkProvider>(create: (_) => context.read()),
|
Provider<SnNetworkProvider>(create: (_) => context.read()),
|
||||||
|
Provider<SnLinkPreviewProvider>(create: (_) => context.read()),
|
||||||
|
ChangeNotifierProvider<ConfigProvider>(create: (_) => context.read()),
|
||||||
],
|
],
|
||||||
child: ResponsiveBreakpoints.builder(
|
child: ResponsiveBreakpoints.builder(
|
||||||
breakpoints: ResponsiveBreakpoints.of(context).breakpoints,
|
breakpoints: ResponsiveBreakpoints.of(context).breakpoints,
|
||||||
@ -410,7 +414,7 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
size: Size(28, 28),
|
size: Size(28, 28),
|
||||||
),
|
),
|
||||||
eyeStyle: QrEyeStyle(
|
eyeStyle: QrEyeStyle(
|
||||||
eyeShape: QrEyeShape.square,
|
eyeShape: QrEyeShape.circle,
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
dataModuleStyle: QrDataModuleStyle(
|
dataModuleStyle: QrDataModuleStyle(
|
||||||
|
@ -189,16 +189,19 @@ class PostMediaPendingList extends StatelessWidget {
|
|||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: switch (thumbnail!.type) {
|
child: switch (thumbnail!.type) {
|
||||||
PostWriteMediaType.image => LayoutBuilder(builder: (context, constraints) {
|
PostWriteMediaType.image => Container(
|
||||||
return Image(
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
image: thumbnail!.getImageProvider(
|
child: LayoutBuilder(builder: (context, constraints) {
|
||||||
context,
|
return Image(
|
||||||
width: (constraints.maxWidth * devicePixelRatio).round(),
|
image: thumbnail!.getImageProvider(
|
||||||
height: (constraints.maxHeight * devicePixelRatio).round(),
|
context,
|
||||||
)!,
|
width: (constraints.maxWidth * devicePixelRatio).round(),
|
||||||
fit: BoxFit.cover,
|
height: (constraints.maxHeight * devicePixelRatio).round(),
|
||||||
);
|
)!,
|
||||||
}),
|
fit: BoxFit.contain,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
_ => Container(
|
_ => Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: const Icon(Symbols.docs).center(),
|
child: const Icon(Symbols.docs).center(),
|
||||||
@ -236,18 +239,21 @@ class PostMediaPendingList extends StatelessWidget {
|
|||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: switch (media.type) {
|
child: switch (media.type) {
|
||||||
PostWriteMediaType.image => LayoutBuilder(builder: (context, constraints) {
|
PostWriteMediaType.image => Container(
|
||||||
return Image(
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
image: media.getImageProvider(
|
child: LayoutBuilder(builder: (context, constraints) {
|
||||||
context,
|
return Image(
|
||||||
width: (constraints.maxWidth * devicePixelRatio).round(),
|
image: media.getImageProvider(
|
||||||
height: (constraints.maxHeight * devicePixelRatio).round(),
|
context,
|
||||||
)!,
|
width: (constraints.maxWidth * devicePixelRatio).round(),
|
||||||
fit: BoxFit.cover,
|
height: (constraints.maxHeight * devicePixelRatio).round(),
|
||||||
);
|
)!,
|
||||||
}),
|
fit: BoxFit.contain,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
_ => Container(
|
_ => Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
child: const Icon(Symbols.docs).center(),
|
child: const Icon(Symbols.docs).center(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -114,6 +114,18 @@ class PostMetaEditor extends StatelessWidget {
|
|||||||
controller.setTags(value);
|
controller.setTags(value);
|
||||||
},
|
},
|
||||||
).padding(horizontal: 24),
|
).padding(horizontal: 24),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: controller.aliasController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'fieldPostAlias'.tr(),
|
||||||
|
helperText: 'fieldPostAliasHint'.tr(),
|
||||||
|
helperMaxLines: 2,
|
||||||
|
border: UnderlineInputBorder(),
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
).padding(horizontal: 24),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
@ -7,6 +7,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/controllers/post_write_controller.dart';
|
import 'package:surface/controllers/post_write_controller.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
@ -16,6 +17,7 @@ import 'package:surface/widgets/loading_indicator.dart';
|
|||||||
class PostMiniEditor extends StatefulWidget {
|
class PostMiniEditor extends StatefulWidget {
|
||||||
final int? postReplyId;
|
final int? postReplyId;
|
||||||
final Function? onPost;
|
final Function? onPost;
|
||||||
|
|
||||||
const PostMiniEditor({super.key, this.postReplyId, this.onPost});
|
const PostMiniEditor({super.key, this.postReplyId, this.onPost});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -26,6 +28,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
final PostWriteController _writeController = PostWriteController();
|
final PostWriteController _writeController = PostWriteController();
|
||||||
|
|
||||||
bool _isFetching = false;
|
bool _isFetching = false;
|
||||||
|
|
||||||
bool get _isLoading => _isFetching || _writeController.isLoading;
|
bool get _isLoading => _isFetching || _writeController.isLoading;
|
||||||
|
|
||||||
List<SnPublisher>? _publishers;
|
List<SnPublisher>? _publishers;
|
||||||
@ -35,11 +38,14 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
final resp = await sn.client.get('/cgi/co/publishers/me');
|
final resp = await sn.client.get('/cgi/co/publishers/me');
|
||||||
_publishers = List<SnPublisher>.from(
|
_publishers = List<SnPublisher>.from(
|
||||||
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
||||||
);
|
);
|
||||||
_writeController.setPublisher(_publishers?.firstOrNull);
|
final beforeId = config.prefs.getInt('int_last_publisher_id');
|
||||||
|
_writeController
|
||||||
|
.setPublisher(_publishers?.where((ele) => ele.id == beforeId).firstOrNull ?? _publishers?.firstOrNull);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -93,17 +99,11 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Text(item.nick).textStyle(
|
Text(item.nick).textStyle(Theme.of(context).textTheme.bodyMedium!),
|
||||||
Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!),
|
|
||||||
Text('@${item.name}')
|
Text('@${item.name}')
|
||||||
.textStyle(Theme.of(context)
|
.textStyle(Theme.of(context).textTheme.bodySmall!)
|
||||||
.textTheme
|
|
||||||
.bodySmall!)
|
|
||||||
.fontSize(12),
|
.fontSize(12),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -120,8 +120,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor:
|
foregroundColor: Theme.of(context).colorScheme.onSurface,
|
||||||
Theme.of(context).colorScheme.onSurface,
|
|
||||||
child: const Icon(Symbols.add),
|
child: const Icon(Symbols.add),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@ -130,8 +129,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('publishersNew').tr().textStyle(
|
Text('publishersNew').tr().textStyle(Theme.of(context).textTheme.bodyMedium!),
|
||||||
Theme.of(context).textTheme.bodyMedium!),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -142,9 +140,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
value: _writeController.publisher,
|
value: _writeController.publisher,
|
||||||
onChanged: (SnPublisher? value) {
|
onChanged: (SnPublisher? value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
GoRouter.of(context)
|
GoRouter.of(context).pushNamed('accountPublisherNew').then((value) {
|
||||||
.pushNamed('accountPublisherNew')
|
|
||||||
.then((value) {
|
|
||||||
if (value == true) {
|
if (value == true) {
|
||||||
_publishers = null;
|
_publishers = null;
|
||||||
_fetchPublishers();
|
_fetchPublishers();
|
||||||
@ -152,6 +148,8 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_writeController.setPublisher(value);
|
_writeController.setPublisher(value);
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
|
config.prefs.setInt('int_last_publisher_id', value.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buttonStyleData: const ButtonStyleData(
|
buttonStyleData: const ButtonStyleData(
|
||||||
@ -178,8 +176,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
),
|
),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@ -188,8 +185,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
TweenAnimationBuilder<double>(
|
TweenAnimationBuilder<double>(
|
||||||
tween: Tween(begin: 0, end: _writeController.progress),
|
tween: Tween(begin: 0, end: _writeController.progress),
|
||||||
duration: Duration(milliseconds: 300),
|
duration: Duration(milliseconds: 300),
|
||||||
builder: (context, value, _) =>
|
builder: (context, value, _) => LinearProgressIndicator(value: value, minHeight: 2),
|
||||||
LinearProgressIndicator(value: value, minHeight: 2),
|
|
||||||
)
|
)
|
||||||
else if (_writeController.isBusy)
|
else if (_writeController.isBusy)
|
||||||
const LinearProgressIndicator(value: null, minHeight: 2),
|
const LinearProgressIndicator(value: null, minHeight: 2),
|
||||||
@ -206,18 +202,16 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
'postEditor',
|
'postEditor',
|
||||||
pathParameters: {'mode': 'stories'},
|
pathParameters: {'mode': 'stories'},
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
if (widget.postReplyId != null)
|
if (widget.postReplyId != null) 'replying': widget.postReplyId.toString(),
|
||||||
'replying': widget.postReplyId.toString(),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: (_writeController.isBusy ||
|
onPressed: (_writeController.isBusy || _writeController.publisher == null)
|
||||||
_writeController.publisher == null)
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
_writeController.post(context).then((_) {
|
_writeController.sendPost(context).then((_) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
if (widget.onPost != null) widget.onPost!();
|
if (widget.onPost != null) widget.onPost!();
|
||||||
context.showSnackbar('postPosted'.tr());
|
context.showSnackbar('postPosted'.tr());
|
||||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 2.1.1+36
|
version: 2.1.1+37
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
|