Skip to content

Commit

Permalink
Improved create comment page, added language selector when creating a…
Browse files Browse the repository at this point in the history
… comment (#1165)

* applied initial comment page refactor

* applied proper parameters to create comment, organized imports for affected files

* adjusted UI and visuals for create comment page

* added logic to handle updating post page, and other areas

* fixed issues with swipe actions not refreshing comment, disabled comment actions on search page

* disable actions on comments from create comment page, user feed page

* simplified callback, added back missing spoiler action

* adjusted snackbar to match material specs more closely

* adjusted padding on post preview
  • Loading branch information
hjiangsu authored Apr 17, 2024
1 parent 3e7f0b9 commit 034ffe9
Show file tree
Hide file tree
Showing 19 changed files with 1,122 additions and 1,319 deletions.
74 changes: 74 additions & 0 deletions lib/comment/cubit/create_comment_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:lemmy_api_client/pictrs.dart';

import 'package:thunder/utils/error_messages.dart';
import 'package:thunder/account/models/account.dart';
import 'package:thunder/core/singletons/lemmy_client.dart';
import 'package:thunder/core/auth/helpers/fetch_account.dart';

part 'create_comment_state.dart';

class CreateCommentCubit extends Cubit<CreateCommentState> {
CreateCommentCubit() : super(const CreateCommentState(status: CreateCommentStatus.initial));

Future<void> clearMessage() async {
emit(state.copyWith(status: CreateCommentStatus.initial, message: null));
}

Future<void> uploadImage(String imageFile) async {
Account? account = await fetchActiveProfileAccount();
if (account == null) return;

PictrsApi pictrs = PictrsApi(account.instance!);
emit(state.copyWith(status: CreateCommentStatus.imageUploadInProgress));

try {
PictrsUpload result = await pictrs.upload(filePath: imageFile, auth: account.jwt);
String url = "https://${account.instance!}/pictrs/image/${result.files[0].file}";

emit(state.copyWith(status: CreateCommentStatus.imageUploadSuccess, imageUrl: url));
} catch (e) {
emit(state.copyWith(status: CreateCommentStatus.imageUploadFailure, message: e.toString()));
}
}

/// Creates or edits a comment. When successful, it emits the newly created/updated comment in the form of a [CommentView]
/// and returns the newly created comment id.
Future<int?> createOrEditComment({int? postId, int? parentCommentId, required String content, int? commentIdBeingEdited, int? languageId}) async {
assert(!(postId == null && commentIdBeingEdited == null));
emit(state.copyWith(status: CreateCommentStatus.submitting));

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

CommentResponse commentResponse;

if (commentIdBeingEdited != null) {
commentResponse = await lemmy.run(EditComment(
commentId: commentIdBeingEdited,
content: content,
languageId: languageId ?? 0,
auth: account!.jwt!,
));
} else {
commentResponse = await lemmy.run(CreateComment(
postId: postId!,
content: content,
parentId: parentCommentId,
languageId: languageId ?? 0,
auth: account!.jwt!,
));
}

emit(state.copyWith(status: CreateCommentStatus.success, commentView: commentResponse.commentView));
return commentResponse.commentView.comment.id;
} catch (e) {
emit(state.copyWith(status: CreateCommentStatus.error, message: getExceptionErrorMessage(e)));
}

return null;
}
}
51 changes: 51 additions & 0 deletions lib/comment/cubit/create_comment_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
part of 'create_comment_cubit.dart';

enum CreateCommentStatus {
initial,
loading,
submitting,
error,
success,
imageUploadInProgress,
imageUploadSuccess,
imageUploadFailure,
unknown,
}

class CreateCommentState extends Equatable {
const CreateCommentState({
this.status = CreateCommentStatus.initial,
this.commentView,
this.imageUrl,
this.message,
});

/// The status of the current cubit
final CreateCommentStatus status;

/// The result of the created or edited comment
final CommentView? commentView;

/// The url of the uploaded image
final String? imageUrl;

/// The info or error message to be displayed as a snackbar
final String? message;

CreateCommentState copyWith({
required CreateCommentStatus status,
CommentView? commentView,
String? imageUrl,
String? message,
}) {
return CreateCommentState(
status: status,
commentView: commentView ?? this.commentView,
imageUrl: imageUrl ?? this.imageUrl,
message: message ?? this.message,
);
}

@override
List<dynamic> get props => [status, commentView, imageUrl, message];
}
8 changes: 4 additions & 4 deletions lib/comment/utils/comment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,14 @@ List<int> findCommentIndexesFromCommentViewTree(List<CommentViewTree> commentTre
}

// Used for modifying the comment current comment tree so we don't have to refresh the whole thing
bool updateModifiedComment(List<CommentViewTree> commentTrees, CommentResponse moddedComment) {
bool updateModifiedComment(List<CommentViewTree> commentTrees, CommentView commentView) {
for (int i = 0; i < commentTrees.length; i++) {
if (commentTrees[i].commentView!.comment.id == moddedComment.commentView.comment.id) {
commentTrees[i].commentView = moddedComment.commentView;
if (commentTrees[i].commentView!.comment.id == commentView.comment.id) {
commentTrees[i].commentView = commentView;
return true;
}

bool done = updateModifiedComment(commentTrees[i].replies, moddedComment);
bool done = updateModifiedComment(commentTrees[i].replies, commentView);
if (done) {
return done;
}
Expand Down
136 changes: 93 additions & 43 deletions lib/comment/utils/navigate_comment.dart
Original file line number Diff line number Diff line change
@@ -1,43 +1,93 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:swipeable_page_route/swipeable_page_route.dart';
import 'package:thunder/account/bloc/account_bloc.dart';
import 'package:thunder/core/auth/bloc/auth_bloc.dart';
import 'package:thunder/core/models/post_view_media.dart';
import 'package:thunder/post/bloc/post_bloc.dart';
import 'package:thunder/post/pages/post_page.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/utils/swipe.dart';

Future<void> navigateToComment(BuildContext context, CommentView commentView) async {
AccountBloc accountBloc = context.read<AccountBloc>();
AuthBloc authBloc = context.read<AuthBloc>();
ThunderBloc thunderBloc = context.read<ThunderBloc>();

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

// To to specific post for now, in the future, will be best to scroll to the position of the comment
await Navigator.of(context).push(
SwipeablePageRoute(
transitionDuration: reduceAnimations ? const Duration(milliseconds: 100) : null,
backGestureDetectionWidth: 45,
canOnlySwipeFromEdge: disableFullPageSwipe(isUserLoggedIn: authBloc.state.isLoggedIn, state: thunderBloc.state, isPostPage: true) || !state.enableFullScreenSwipeNavigationGesture,
builder: (context) => MultiBlocProvider(
providers: [
BlocProvider.value(value: accountBloc),
BlocProvider.value(value: authBloc),
BlocProvider.value(value: thunderBloc),
BlocProvider(create: (context) => PostBloc()),
],
child: PostPage(
selectedCommentId: commentView.comment.id,
selectedCommentPath: commentView.comment.path,
postId: commentView.post.id,
onPostUpdated: (PostViewMedia postViewMedia) => {},
),
),
),
);
}
// Flutter imports
import 'package:flutter/material.dart';

// Package imports
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:swipeable_page_route/swipeable_page_route.dart';

// Project imports
import 'package:thunder/account/bloc/account_bloc.dart';
import 'package:thunder/comment/view/create_comment_page.dart';
import 'package:thunder/core/auth/bloc/auth_bloc.dart';
import 'package:thunder/core/models/post_view_media.dart';
import 'package:thunder/post/bloc/post_bloc.dart';
import 'package:thunder/post/pages/post_page.dart';
import 'package:thunder/shared/snackbar.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/utils/swipe.dart';

Future<void> navigateToComment(BuildContext context, CommentView commentView) async {
AccountBloc accountBloc = context.read<AccountBloc>();
AuthBloc authBloc = context.read<AuthBloc>();
ThunderBloc thunderBloc = context.read<ThunderBloc>();

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

// To to specific post for now, in the future, will be best to scroll to the position of the comment
await Navigator.of(context).push(
SwipeablePageRoute(
transitionDuration: reduceAnimations ? const Duration(milliseconds: 100) : null,
backGestureDetectionWidth: 45,
canOnlySwipeFromEdge: disableFullPageSwipe(isUserLoggedIn: authBloc.state.isLoggedIn, state: thunderBloc.state, isPostPage: true) || !state.enableFullScreenSwipeNavigationGesture,
builder: (context) => MultiBlocProvider(
providers: [
BlocProvider.value(value: accountBloc),
BlocProvider.value(value: authBloc),
BlocProvider.value(value: thunderBloc),
BlocProvider(create: (context) => PostBloc()),
],
child: PostPage(
selectedCommentId: commentView.comment.id,
selectedCommentPath: commentView.comment.path,
postId: commentView.post.id,
onPostUpdated: (PostViewMedia postViewMedia) => {},
),
),
),
);
}

Future<void> navigateToCreateCommentPage(
BuildContext context, {
PostViewMedia? postViewMedia,
CommentView? commentView,
CommentView? parentCommentView,
Function(CommentView commentView)? onCommentSuccess,
}) async {
assert(!(postViewMedia == null && parentCommentView == null && commentView == null));
assert(!(postViewMedia != null && (parentCommentView != null || commentView != null)));

final l10n = AppLocalizations.of(context)!;

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

final bool reduceAnimations = thunderBloc.state.reduceAnimations;

Navigator.of(context).push(SwipeablePageRoute(
transitionDuration: reduceAnimations ? const Duration(milliseconds: 100) : null,
canOnlySwipeFromEdge: true,
backGestureDetectionWidth: 45,
builder: (navigatorContext) {
return MultiBlocProvider(
providers: [
BlocProvider<ThunderBloc>.value(value: thunderBloc),
BlocProvider<AccountBloc>.value(value: accountBloc),
],
child: CreateCommentPage(
postViewMedia: postViewMedia,
commentView: commentView,
parentCommentView: parentCommentView,
onCommentSuccess: onCommentSuccess,
),
);
},
));
} catch (e) {
if (context.mounted) showSnackbar(l10n.unexpectedError);
}
}
Loading

0 comments on commit 034ffe9

Please sign in to comment.