Implement single comment view

This commit is contained in:
John Wesley 2024-01-17 16:40:14 -05:00
parent eecfa1498a
commit ebc5dcedbe
8 changed files with 208 additions and 0 deletions

View File

@ -24,6 +24,20 @@ Future<EntryCommentListModel> fetchComments(
jsonDecode(response.body) as Map<String, dynamic>);
}
Future<EntryCommentModel> fetchComment(
http.Client client,
String instanceHost,
int commentId,
) async {
final response =
await client.get(Uri.https(instanceHost, '/api/comments/$commentId'));
httpErrorHandler(response, message: 'Failed to load comment');
return EntryCommentModel.fromJson(
jsonDecode(response.body) as Map<String, dynamic>);
}
Future<EntryCommentModel> putVote(
http.Client client,
String instanceHost,

View File

@ -24,6 +24,20 @@ Future<PostCommentListModel> fetchComments(
jsonDecode(response.body) as Map<String, dynamic>);
}
Future<PostCommentModel> fetchComment(
http.Client client,
String instanceHost,
int commentId,
) async {
final response = await client
.get(Uri.https(instanceHost, '/api/post-comments/$commentId'));
httpErrorHandler(response, message: 'Failed to load comment');
return PostCommentModel.fromJson(
jsonDecode(response.body) as Map<String, dynamic>);
}
Future<PostCommentModel> putVote(
http.Client client,
String instanceHost,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:interstellar/src/api/comments.dart' as api_comments;
import 'package:interstellar/src/models/entry_comment.dart';
import 'package:interstellar/src/screens/entries/entry_comment_screen.dart';
import 'package:interstellar/src/screens/settings/settings_controller.dart';
import 'package:interstellar/src/utils/utils.dart';
import 'package:interstellar/src/widgets/content_item.dart';
@ -72,6 +73,13 @@ class _EntryCommentState extends State<EntryComment> {
children: widget.comment.children,
));
}),
openContentLabel: 'Open comment',
onOpenContent: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
EntryCommentScreen(widget.comment.commentId),
),
),
onReply: whenLoggedIn(context, (body) async {
var newSubComment = await api_comments.postComment(
context.read<SettingsController>().httpClient,
@ -125,6 +133,19 @@ class _EntryCommentState extends State<EntryComment> {
: null,
),
),
if (widget.comment.childCount > 0 &&
!_isCollapsed &&
(widget.comment.children?.isEmpty ?? false))
TextButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
EntryCommentScreen(widget.comment.commentId),
),
),
child: Text(
'Open ${widget.comment.childCount} reply${widget.comment.childCount == 1 ? '' : 's'}'),
),
if (widget.comment.childCount > 0 && !_isCollapsed)
Container(
margin: const EdgeInsets.only(left: 1),

View File

@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:interstellar/src/api/comments.dart';
import 'package:interstellar/src/models/entry_comment.dart';
import 'package:interstellar/src/screens/entries/entry_comment.dart';
import 'package:interstellar/src/screens/settings/settings_controller.dart';
import 'package:provider/provider.dart';
class EntryCommentScreen extends StatefulWidget {
const EntryCommentScreen(
this.commentId, {
super.key,
});
final int commentId;
@override
State<EntryCommentScreen> createState() => _EntryCommentScreenState();
}
class _EntryCommentScreenState extends State<EntryCommentScreen> {
EntryCommentModel? _comment;
@override
void initState() {
super.initState();
fetchComment(context.read<SettingsController>().httpClient,
context.read<SettingsController>().instanceHost, widget.commentId)
.then((value) => setState(() {
_comment = value;
}));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Comment${_comment != null ? ' by ${_comment!.user.username}' : ''}'),
),
body: _comment != null
? ListView(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 4,
),
child: EntryComment(
_comment!,
(newComment) => setState(() {
_comment = newComment;
}),
),
)
],
)
: const Center(
child: CircularProgressIndicator(),
),
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:interstellar/src/api/post_comments.dart' as api_comments;
import 'package:interstellar/src/models/post_comment.dart';
import 'package:interstellar/src/screens/posts/post_comment_screen.dart';
import 'package:interstellar/src/screens/settings/settings_controller.dart';
import 'package:interstellar/src/utils/utils.dart';
import 'package:interstellar/src/widgets/content_item.dart';
@ -72,6 +73,13 @@ class _EntryCommentState extends State<PostComment> {
children: widget.comment.children,
));
}),
openContentLabel: 'Open comment',
onOpenContent: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
PostCommentScreen(widget.comment.commentId),
),
),
onReply: whenLoggedIn(context, (body) async {
var newSubComment = await api_comments.postComment(
context.read<SettingsController>().httpClient,
@ -125,6 +133,19 @@ class _EntryCommentState extends State<PostComment> {
: null,
),
),
if (widget.comment.childCount > 0 &&
!_isCollapsed &&
(widget.comment.children?.isEmpty ?? false))
TextButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
PostCommentScreen(widget.comment.commentId),
),
),
child: Text(
'Open ${widget.comment.childCount} reply${widget.comment.childCount == 1 ? '' : 's'}'),
),
if (widget.comment.childCount > 0 && !_isCollapsed)
Container(
margin: const EdgeInsets.only(left: 1),

View File

@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:interstellar/src/api/post_comments.dart';
import 'package:interstellar/src/models/post_comment.dart';
import 'package:interstellar/src/screens/posts/post_comment.dart';
import 'package:interstellar/src/screens/settings/settings_controller.dart';
import 'package:provider/provider.dart';
class PostCommentScreen extends StatefulWidget {
const PostCommentScreen(
this.commentId, {
super.key,
});
final int commentId;
@override
State<PostCommentScreen> createState() => _PostCommentScreenState();
}
class _PostCommentScreenState extends State<PostCommentScreen> {
PostCommentModel? _comment;
@override
void initState() {
super.initState();
fetchComment(context.read<SettingsController>().httpClient,
context.read<SettingsController>().instanceHost, widget.commentId)
.then((value) => setState(() {
_comment = value;
}));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Comment${_comment != null ? ' by ${_comment!.user.username}' : ''}'),
),
body: _comment != null
? ListView(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 4,
),
child: PostComment(
_comment!,
(newComment) => setState(() {
_comment = newComment;
}),
),
)
],
)
: const Center(
child: CircularProgressIndicator(),
),
);
}
}

View File

@ -46,6 +46,8 @@ class ContentItem extends StatefulWidget {
final int? numComments;
final Future<void> Function(String)? onReply;
final String? openContentLabel;
final Future<void> Function()? onOpenContent;
final Future<void> Function(String)? onEdit;
final Future<void> Function()? onDelete;
@ -79,6 +81,8 @@ class ContentItem extends StatefulWidget {
this.isDownVoted = false,
this.onDownVote,
this.numComments,
this.openContentLabel,
this.onOpenContent,
this.onReply,
this.onEdit,
this.onDelete,
@ -313,6 +317,13 @@ class _ContentItemState extends State<ContentItem> {
},
controller: _menuController,
menuChildren: [
if (widget.openContentLabel != null)
MenuItemButton(
onPressed: widget.onOpenContent,
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(widget.openContentLabel!)),
),
MenuItemButton(
onPressed: widget.onEdit != null
? () => setState(() {

View File

@ -22,6 +22,7 @@ class TextEditor extends StatelessWidget {
controller: controller,
keyboardType:
keyboardType ?? (isMarkdown ? TextInputType.multiline : null),
minLines: isMarkdown ? 2 : null,
maxLines: isMarkdown ? null : 1,
decoration: InputDecoration(
border: const OutlineInputBorder(),