♻️ Refactored using controller as post editing
This commit is contained in:
354
lib/controllers/post_write_controller.dart
Normal file
354
lib/controllers/post_write_controller.dart
Normal file
@ -0,0 +1,354 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:surface/providers/sn_attachment.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
import 'package:surface/types/attachment.dart';
|
||||
import 'package:surface/types/post.dart';
|
||||
import 'package:surface/widgets/dialog.dart';
|
||||
import 'package:surface/widgets/universal_image.dart';
|
||||
|
||||
enum PostWriteMediaType {
|
||||
image,
|
||||
video,
|
||||
audio,
|
||||
file,
|
||||
}
|
||||
|
||||
class PostWriteMedia {
|
||||
late String name;
|
||||
late PostWriteMediaType type;
|
||||
final SnAttachment? attachment;
|
||||
final XFile? file;
|
||||
final Uint8List? raw;
|
||||
|
||||
PostWriteMedia(this.attachment, {this.file, this.raw}) {
|
||||
name = attachment!.name;
|
||||
|
||||
switch (attachment?.mimetype.split('/').firstOrNull) {
|
||||
case 'image':
|
||||
type = PostWriteMediaType.image;
|
||||
break;
|
||||
case 'video':
|
||||
type = PostWriteMediaType.video;
|
||||
break;
|
||||
case 'audio':
|
||||
type = PostWriteMediaType.audio;
|
||||
break;
|
||||
default:
|
||||
type = PostWriteMediaType.file;
|
||||
}
|
||||
}
|
||||
|
||||
PostWriteMedia.fromFile(this.file, {this.attachment, this.raw}) {
|
||||
name = file!.name;
|
||||
|
||||
switch (file?.mimeType?.split('/').firstOrNull) {
|
||||
case 'image':
|
||||
type = PostWriteMediaType.image;
|
||||
break;
|
||||
case 'video':
|
||||
type = PostWriteMediaType.video;
|
||||
break;
|
||||
case 'audio':
|
||||
type = PostWriteMediaType.audio;
|
||||
break;
|
||||
default:
|
||||
type = PostWriteMediaType.file;
|
||||
}
|
||||
}
|
||||
|
||||
PostWriteMedia.fromBytes(this.raw, this.name, this.type,
|
||||
{this.attachment, this.file});
|
||||
|
||||
bool get isEmpty => attachment == null && file == null && raw == null;
|
||||
|
||||
Future<int?> length() async {
|
||||
if (attachment != null) {
|
||||
return attachment!.size;
|
||||
} else if (file != null) {
|
||||
return await file!.length();
|
||||
} else if (raw != null) {
|
||||
return raw!.length;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
XFile? toFile() {
|
||||
if (file != null) {
|
||||
return file!;
|
||||
} else if (raw != null) {
|
||||
return XFile.fromData(raw!, name: name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ImageProvider? getImageProvider(
|
||||
BuildContext context, {
|
||||
int? width,
|
||||
int? height,
|
||||
}) {
|
||||
if (attachment != null) {
|
||||
final sn = context.read<SnNetworkProvider>();
|
||||
return UniversalImage.provider(sn.getAttachmentUrl(attachment!.rid));
|
||||
} else if (file != null) {
|
||||
final ImageProvider provider =
|
||||
kIsWeb ? NetworkImage(file!.path) : FileImage(File(file!.path));
|
||||
if (width != null && height != null) {
|
||||
return ResizeImage(
|
||||
provider,
|
||||
width: width,
|
||||
height: height,
|
||||
policy: ResizeImagePolicy.fit,
|
||||
);
|
||||
}
|
||||
return provider;
|
||||
} else if (raw != null) {
|
||||
final provider = MemoryImage(raw!);
|
||||
if (width != null && height != null) {
|
||||
return ResizeImage(
|
||||
provider,
|
||||
width: width,
|
||||
height: height,
|
||||
policy: ResizeImagePolicy.fit,
|
||||
);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PostWriteController extends ChangeNotifier {
|
||||
static const Map<String, String> kTitleMap = {
|
||||
'stories': 'writePostTypeStory',
|
||||
'articles': 'writePostTypeArticle',
|
||||
};
|
||||
|
||||
static const kAttachmentProgressWeight = 0.9;
|
||||
static const kPostingProgressWeight = 0.1;
|
||||
|
||||
final TextEditingController contentController = TextEditingController();
|
||||
final TextEditingController titleController = TextEditingController();
|
||||
final TextEditingController descriptionController = TextEditingController();
|
||||
|
||||
PostWriteController() {
|
||||
titleController.addListener(() => notifyListeners());
|
||||
descriptionController.addListener(() => notifyListeners());
|
||||
}
|
||||
|
||||
String mode = kTitleMap.keys.first;
|
||||
|
||||
String get title => titleController.text;
|
||||
String get description => descriptionController.text;
|
||||
bool get isRelatedNull =>
|
||||
![editingPost, repostingPost, replyingPost].any((ele) => ele != null);
|
||||
|
||||
bool isLoading = false, isBusy = false;
|
||||
double? progress;
|
||||
|
||||
SnPublisher? publisher;
|
||||
SnPost? editingPost, repostingPost, replyingPost;
|
||||
|
||||
List<PostWriteMedia> attachments = List.empty(growable: true);
|
||||
DateTime? publishedAt, publishedUntil;
|
||||
|
||||
Future<void> fetchRelatedPost(
|
||||
BuildContext context, {
|
||||
int? editing,
|
||||
int? reposting,
|
||||
int? replying,
|
||||
}) async {
|
||||
final sn = context.read<SnNetworkProvider>();
|
||||
final attach = context.read<SnAttachmentProvider>();
|
||||
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
if (editing != null) {
|
||||
final resp = await sn.client.get('/cgi/co/posts/$editing');
|
||||
final post = SnPost.fromJson(resp.data);
|
||||
final alts = await attach
|
||||
.getMultiple(post.body['attachments']?.cast<String>() ?? []);
|
||||
publisher = post.publisher;
|
||||
titleController.text = post.body['title'] ?? '';
|
||||
descriptionController.text = post.body['description'] ?? '';
|
||||
contentController.text = post.body['content'] ?? '';
|
||||
publishedAt = post.publishedAt;
|
||||
publishedUntil = post.publishedUntil;
|
||||
attachments.addAll(alts.map((ele) => PostWriteMedia(ele)));
|
||||
|
||||
editingPost = post.copyWith(
|
||||
preload: SnPostPreload(
|
||||
attachments: alts,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (replying != null) {
|
||||
final resp = await sn.client.get('/cgi/co/posts/$replying');
|
||||
final post = SnPost.fromJson(resp.data);
|
||||
replyingPost = post.copyWith(
|
||||
preload: SnPostPreload(
|
||||
attachments: await attach
|
||||
.getMultiple(post.body['attachments']?.cast<String>() ?? []),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (reposting != null) {
|
||||
final resp = await sn.client.get('/cgi/co/posts/$reposting');
|
||||
final post = SnPost.fromJson(resp.data);
|
||||
repostingPost = post.copyWith(
|
||||
preload: SnPostPreload(
|
||||
attachments: await attach
|
||||
.getMultiple(post.body['attachments']?.cast<String>() ?? []),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!context.mounted) return;
|
||||
context.showErrorDialog(err);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void post(BuildContext context) async {
|
||||
if (isBusy || publisher == null) return;
|
||||
|
||||
final sn = context.read<SnNetworkProvider>();
|
||||
final attach = context.read<SnAttachmentProvider>();
|
||||
|
||||
progress = 0;
|
||||
isBusy = true;
|
||||
notifyListeners();
|
||||
|
||||
// Uploading attachments
|
||||
try {
|
||||
for (int i = 0; i < attachments.length; i++) {
|
||||
final media = attachments[i];
|
||||
if (media.attachment != null) continue; // Already uploaded, skip
|
||||
if (media.isEmpty) continue; // Nothing to do, skip
|
||||
|
||||
final place = await attach.chunkedUploadInitialize(
|
||||
(await media.length())!,
|
||||
media.name,
|
||||
'interactive',
|
||||
null,
|
||||
);
|
||||
|
||||
final item = await attach.chunkedUploadParts(
|
||||
media.toFile()!,
|
||||
place.$1,
|
||||
place.$2,
|
||||
onProgress: (progress) {
|
||||
// Calculate overall progress for attachments
|
||||
progress = ((i + progress) / attachments.length) *
|
||||
kAttachmentProgressWeight;
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
|
||||
attachments[i] = PostWriteMedia(item);
|
||||
}
|
||||
} catch (err) {
|
||||
isBusy = false;
|
||||
notifyListeners();
|
||||
if (!context.mounted) return;
|
||||
context.showErrorDialog(err);
|
||||
return;
|
||||
}
|
||||
|
||||
progress = kAttachmentProgressWeight;
|
||||
notifyListeners();
|
||||
|
||||
// Posting the content
|
||||
try {
|
||||
final baseProgressVal = progress!;
|
||||
await sn.client.request(
|
||||
[
|
||||
'/cgi/co/$mode',
|
||||
if (editingPost != null) '${editingPost!.id}',
|
||||
].join('/'),
|
||||
data: {
|
||||
'publisher': publisher!.id,
|
||||
'content': contentController.text,
|
||||
'title': titleController.text,
|
||||
'description': descriptionController.text,
|
||||
'attachments': attachments
|
||||
.where((e) => e.attachment != null)
|
||||
.map((e) => e.attachment!.rid)
|
||||
.toList(),
|
||||
if (publishedAt != null)
|
||||
'published_at': publishedAt!.toUtc().toIso8601String(),
|
||||
if (publishedUntil != null)
|
||||
'published_until': publishedAt!.toUtc().toIso8601String(),
|
||||
if (replyingPost != null) 'reply_to': replyingPost!.id,
|
||||
if (repostingPost != null) 'repost_to': repostingPost!.id,
|
||||
},
|
||||
onSendProgress: (count, total) {
|
||||
progress =
|
||||
baseProgressVal + (count / total) * (kPostingProgressWeight / 2);
|
||||
notifyListeners();
|
||||
},
|
||||
onReceiveProgress: (count, total) {
|
||||
progress = baseProgressVal +
|
||||
(kPostingProgressWeight / 2) +
|
||||
(count / total) * (kPostingProgressWeight / 2);
|
||||
notifyListeners();
|
||||
},
|
||||
options: Options(
|
||||
method: editingPost != null ? 'PUT' : 'POST',
|
||||
),
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context, true);
|
||||
} catch (err) {
|
||||
if (!context.mounted) return;
|
||||
context.showErrorDialog(err);
|
||||
} finally {
|
||||
isBusy = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void addAttachments(Iterable<PostWriteMedia> items) {
|
||||
attachments.addAll(items);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void removeAttachmentAt(int idx) {
|
||||
attachments.removeAt(idx);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setPublisher(SnPublisher? item) {
|
||||
publisher = item;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setPublishedAt(DateTime? value) {
|
||||
publishedAt = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setPublishedUntil(DateTime? value) {
|
||||
publishedUntil = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
contentController.dispose();
|
||||
titleController.dispose();
|
||||
descriptionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user