Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create comment page refactor #1165

Merged
merged 10 commits into from
Apr 17, 2024
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
Loading