Skip to content

Commit

Permalink
Allow editing posts
Browse files Browse the repository at this point in the history
  • Loading branch information
micahmo committed Oct 25, 2023
1 parent 12797d6 commit 98db4c2
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 40 deletions.
71 changes: 52 additions & 19 deletions lib/community/pages/create_post_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@ class CreatePostPage extends StatefulWidget {
final CommunityView? communityView;
final void Function(DraftPost? draftPost)? onUpdateDraft;
final DraftPost? previousDraftPost;
final bool isEdit;
final PostView? postView;

const CreatePostPage({
super.key,
required this.communityId,
this.communityView,
this.previousDraftPost,
this.onUpdateDraft,
});
this.isEdit = false,
this.postView,
}) :
// If we're editing, we need a post to edit
assert(!isEdit || postView != null);

@override
State<CreatePostPage> createState() => _CreatePostPageState();
Expand Down Expand Up @@ -84,7 +90,10 @@ class _CreatePostPageState extends State<CreatePostPage> {
widget.onUpdateDraft?.call(newDraftPost..text = _bodyTextController.text);
});

if (widget.previousDraftPost != null) {
if (widget.previousDraftPost != null &&
(_titleTextController.text != (widget.previousDraftPost!.title ?? '') ||
_urlTextController.text != (widget.previousDraftPost!.url ?? '') ||
_bodyTextController.text != (widget.previousDraftPost!.text ?? ''))) {
_titleTextController.text = widget.previousDraftPost!.title ?? '';
_urlTextController.text = widget.previousDraftPost!.url ?? '';
_bodyTextController.text = widget.previousDraftPost!.text ?? '';
Expand All @@ -93,6 +102,11 @@ class _CreatePostPageState extends State<CreatePostPage> {
await Future.delayed(const Duration(milliseconds: 300));
showSnackbar(context, AppLocalizations.of(context)!.restoredPostFromDraft);
});
} else if (widget.isEdit && widget.postView != null) {
_titleTextController.text = widget.postView!.post.name;
_urlTextController.text = widget.postView!.post.url ?? '';
_bodyTextController.text = widget.postView!.post.body ?? '';
isNSFW = widget.postView!.post.nsfw;
}
}

Expand Down Expand Up @@ -120,17 +134,34 @@ class _CreatePostPageState extends State<CreatePostPage> {
},
child: Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.createPost),
title: Text(widget.isEdit ? 'Edit Post' : AppLocalizations.of(context)!.createPost), // TODO
toolbarHeight: 70.0,
actions: [
IconButton(
onPressed: isSubmitButtonDisabled
? null
: () {
newDraftPost.saveAsDraft = false;

url != ''
? context.read<FeedBloc>().add(CreatePostEvent(communityId: communityId!, name: _titleTextController.text, body: _bodyTextController.text, nsfw: isNSFW, url: url))
: context.read<FeedBloc>().add(CreatePostEvent(communityId: communityId!, name: _titleTextController.text, body: _bodyTextController.text, nsfw: isNSFW));
? context.read<FeedBloc>().add(CreatePostEvent(
communityId: communityId!,
name: _titleTextController.text,
body: _bodyTextController.text,
nsfw: isNSFW,
isEdit: widget.isEdit,
postId: widget.postView?.post.id,
url: url,
))
: context.read<FeedBloc>().add(CreatePostEvent(
communityId: communityId!,
name: _titleTextController.text,
body: _bodyTextController.text,
nsfw: isNSFW,
isEdit: widget.isEdit,
postId: widget.postView?.post.id,
));

Navigator.of(context).pop();
},
icon: Icon(
Expand Down Expand Up @@ -178,20 +209,22 @@ class _CreatePostPageState extends State<CreatePostPage> {
Transform.translate(
offset: const Offset(-8, 0),
child: InkWell(
onTap: () {
showCommunityInputDialog(
context,
title: AppLocalizations.of(context)!.community,
onCommunitySelected: (cv) {
setState(() {
communityId = cv.community.id;
communityView = cv;
});
_validateSubmission();
},
emptySuggestions: accountState.subsciptions,
);
},
onTap: widget.isEdit
? null
: () {
showCommunityInputDialog(
context,
title: AppLocalizations.of(context)!.community,
onCommunitySelected: (cv) {
setState(() {
communityId = cv.community.id;
communityView = cv;
});
_validateSubmission();
},
emptySuggestions: accountState.subsciptions,
);
},
borderRadius: const BorderRadius.all(Radius.circular(50)),
child: Padding(
padding: const EdgeInsets.only(left: 8, top: 12, bottom: 12),
Expand Down
2 changes: 2 additions & 0 deletions lib/feed/bloc/feed_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ class FeedBloc extends Bloc<FeedEvent, FeedState> {
body: event.body,
url: event.url,
nsfw: event.nsfw,
isEdit: event.isEdit,
postId: event.postId,
);

// Parse the newly created post
Expand Down
4 changes: 3 additions & 1 deletion lib/feed/bloc/feed_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ final class CreatePostEvent extends FeedEvent {
final String? body;
final String? url;
final bool? nsfw;
final bool? isEdit;
final int? postId;

const CreatePostEvent({required this.communityId, required this.name, this.body, this.url, this.nsfw});
const CreatePostEvent({required this.communityId, required this.name, this.body, this.url, this.nsfw, this.isEdit, this.postId}) : assert(isEdit != true || postId != null);
}
33 changes: 24 additions & 9 deletions lib/feed/utils/post.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:flutter_launcher_icons/android.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:thunder/account/models/account.dart';
import 'package:thunder/core/auth/helpers/fetch_account.dart';
Expand Down Expand Up @@ -50,20 +51,34 @@ Future<Map<String, dynamic>> fetchPosts({
}

/// Logic to create a post
Future<PostView> createPost({required int communityId, required String name, String? body, String? url, bool? nsfw}) async {
Future<PostView> createPost({required int communityId, required String name, String? body, String? url, bool? nsfw, bool? isEdit, int? postId}) async {
assert(isEdit != true || postId != null);

Account? account = await fetchActiveProfileAccount();
LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3;

if (account?.jwt == null) throw Exception('User not logged in');

PostView postView = await lemmy.run(CreatePost(
auth: account!.jwt!,
communityId: communityId,
name: name,
body: body,
url: url,
nsfw: nsfw,
));
PostView postView;
if (isEdit == true) {
postView = await lemmy.run(EditPost(
auth: account!.jwt!,
name: name,
body: body,
url: url,
nsfw: nsfw,
postId: postId!,
));
} else {
postView = await lemmy.run(CreatePost(
auth: account!.jwt!,
communityId: communityId,
name: name,
body: body,
url: url,
nsfw: nsfw,
));
}

return postView;
}
94 changes: 83 additions & 11 deletions lib/post/widgets/post_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:swipeable_page_route/swipeable_page_route.dart';

import 'package:thunder/account/bloc/account_bloc.dart' as account_bloc;
import 'package:thunder/account/bloc/account_bloc.dart';
import 'package:thunder/community/pages/create_post_page.dart';
import 'package:thunder/community/utils/post_card_action_helpers.dart';
import 'package:thunder/community/widgets/post_card_metadata.dart';
import 'package:thunder/core/enums/font_scale.dart';
import 'package:thunder/core/enums/local_settings.dart';
import 'package:thunder/core/singletons/lemmy_client.dart';
import 'package:thunder/core/singletons/preferences.dart';
import 'package:thunder/feed/bloc/feed_bloc.dart';
import 'package:thunder/feed/utils/utils.dart';
import 'package:thunder/feed/view/feed_page.dart';
import 'package:thunder/post/pages/create_comment_page.dart';
Expand Down Expand Up @@ -65,7 +69,7 @@ class PostSubview extends StatelessWidget {
final bool hideNsfwPreviews = thunderState.hideNsfwPreviews;
final bool markPostReadOnMediaView = thunderState.markPostReadOnMediaView;

final bool isOwnComment = postView.creator.id == context.read<AuthBloc>().state.account?.userId;
final bool isOwnPost = postView.creator.id == context.read<AuthBloc>().state.account?.userId;

return Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0, bottom: 8.0),
Expand Down Expand Up @@ -97,19 +101,19 @@ class PostSubview extends StatelessWidget {
),
),
Padding(
padding: EdgeInsets.only(left: isSpecialUser(context, isOwnComment, post, null, postView.creator, moderators) ? 8.0 : 3.0, right: 8.0, top: 16.0),
padding: EdgeInsets.only(left: isSpecialUser(context, isOwnPost, post, null, postView.creator, moderators) ? 8.0 : 3.0, right: 8.0, top: 16.0),
child: Row(
// Row for post view: author, community, comment count and post time
children: [
Tooltip(
excludeFromSemantics: true,
message: '${postView.creator.name}@${fetchInstanceNameFromUrl(postView.creator.actorId) ?? '-'}${fetchUsernameDescriptor(isOwnComment, post, null, postView.creator, moderators)}',
message: '${postView.creator.name}@${fetchInstanceNameFromUrl(postView.creator.actorId) ?? '-'}${fetchUsernameDescriptor(isOwnPost, post, null, postView.creator, moderators)}',
preferBelow: false,
child: Material(
color: isSpecialUser(context, isOwnComment, post, null, postView.creator, moderators)
? fetchUsernameColor(context, isOwnComment, post, null, postView.creator, moderators) ?? theme.colorScheme.onBackground
color: isSpecialUser(context, isOwnPost, post, null, postView.creator, moderators)
? fetchUsernameColor(context, isOwnPost, post, null, postView.creator, moderators) ?? theme.colorScheme.onBackground
: Colors.transparent,
borderRadius: isSpecialUser(context, isOwnComment, post, null, postView.creator, moderators) ? const BorderRadius.all(Radius.elliptical(5, 5)) : null,
borderRadius: isSpecialUser(context, isOwnPost, post, null, postView.creator, moderators) ? const BorderRadius.all(Radius.elliptical(5, 5)) : null,
child: InkWell(
borderRadius: BorderRadius.circular(5),
onTap: () {
Expand All @@ -123,12 +127,12 @@ class PostSubview extends StatelessWidget {
postView.creator.displayName != null && useDisplayNames ? postView.creator.displayName! : postView.creator.name,
textScaleFactor: MediaQuery.of(context).textScaleFactor * thunderState.metadataFontSizeScale.textScaleFactor,
style: theme.textTheme.bodyMedium?.copyWith(
color: (isSpecialUser(context, isOwnComment, post, null, postView.creator, moderators) ? theme.colorScheme.onBackground : theme.textTheme.bodyMedium?.color)
color: (isSpecialUser(context, isOwnPost, post, null, postView.creator, moderators) ? theme.colorScheme.onBackground : theme.textTheme.bodyMedium?.color)
?.withOpacity(0.75),
),
),
if (isSpecialUser(context, isOwnComment, post, null, postView.creator, moderators)) const SizedBox(width: 2.0),
if (isOwnComment)
if (isSpecialUser(context, isOwnPost, post, null, postView.creator, moderators)) const SizedBox(width: 2.0),
if (isOwnPost)
Padding(
padding: const EdgeInsets.only(left: 1),
child: Icon(
Expand Down Expand Up @@ -170,7 +174,7 @@ class PostSubview extends StatelessWidget {
),
),
),
if (isSpecialUser(context, isOwnComment, post, null, postView.creator, moderators)) const SizedBox(width: 8.0),
if (isSpecialUser(context, isOwnPost, post, null, postView.creator, moderators)) const SizedBox(width: 8.0),
Text(
'to',
textScaleFactor: MediaQuery.of(context).textScaleFactor * thunderState.metadataFontSizeScale.textScaleFactor,
Expand Down Expand Up @@ -313,6 +317,72 @@ class PostSubview extends StatelessWidget {
return;
}

if (isOwnPost) {
ThunderBloc thunderBloc = context.read<ThunderBloc>();
AccountBloc accountBloc = context.read<AccountBloc>();

final ThunderState thunderState = context.read<ThunderBloc>().state;
final bool reduceAnimations = thunderState.reduceAnimations;

SharedPreferences prefs = (await UserPreferences.instance).sharedPreferences;
DraftPost? newDraftPost;
DraftPost? previousDraftPost;
String draftId = '${LocalSettings.draftsCache.name}-post-edit-${postViewMedia.postView.post.id}';
String? draftPostJson = prefs.getString(draftId);
if (draftPostJson != null) {
previousDraftPost = DraftPost.fromJson(jsonDecode(draftPostJson));
}
Timer timer = Timer.periodic(const Duration(seconds: 10), (Timer t) {
if (newDraftPost?.isNotEmpty == true) {
prefs.setString(draftId, jsonEncode(newDraftPost!.toJson()));
}
});

if (context.mounted) {
Navigator.of(context)
.push(
SwipeablePageRoute(
transitionDuration: reduceAnimations ? const Duration(milliseconds: 100) : null,
canOnlySwipeFromEdge: true,
backGestureDetectionWidth: 45,
builder: (context) {
return MultiBlocProvider(
providers: [
BlocProvider<FeedBloc>.value(value: FeedBloc(lemmyClient: LemmyClient.instance)),
BlocProvider<ThunderBloc>.value(value: thunderBloc),
BlocProvider<AccountBloc>.value(value: accountBloc),
],
child: CreatePostPage(
communityId: postViewMedia.postView.community.id,
//communityView: postViewMedia.postView.community, // TODO
previousDraftPost: previousDraftPost,
onUpdateDraft: (p) => newDraftPost = p,
isEdit: true,
postView: postViewMedia.postView,
),
);
},
),
)
.whenComplete(() async {
timer.cancel();

if (newDraftPost?.saveAsDraft == true &&
newDraftPost?.isNotEmpty == true &&
(newDraftPost?.title != postViewMedia.postView.post.name ||
newDraftPost?.text != postViewMedia.postView.post.body ||
newDraftPost?.url != postViewMedia.postView.post.url)) {
await Future.delayed(const Duration(milliseconds: 300));
if (context.mounted) showSnackbar(context, AppLocalizations.of(context)!.postSavedAsDraft);
prefs.setString(draftId, jsonEncode(newDraftPost!.toJson()));
} else {
prefs.remove(draftId);
}
});
}
return;
}

PostBloc postBloc = context.read<PostBloc>();
ThunderBloc thunderBloc = context.read<ThunderBloc>();
account_bloc.AccountBloc accountBloc = context.read<account_bloc.AccountBloc>();
Expand Down Expand Up @@ -371,7 +441,9 @@ class PostSubview extends StatelessWidget {
: null,
icon: postView.post.locked
? Icon(Icons.lock, semanticLabel: AppLocalizations.of(context)!.postLocked, color: Colors.red)
: Icon(Icons.reply_rounded, semanticLabel: AppLocalizations.of(context)!.reply(0)),
: isOwnPost
? const Icon(Icons.edit_rounded)
: Icon(Icons.reply_rounded, semanticLabel: AppLocalizations.of(context)!.reply(0)),
),
),
Expanded(
Expand Down

0 comments on commit 98db4c2

Please sign in to comment.