feat(directory): add comment submission functionality and UI components
This commit is contained in:
parent
be71544ae4
commit
549d8cce3c
74
lib/controller/directory/add_comment_controller.dart
Normal file
74
lib/controller/directory/add_comment_controller.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:marco/helpers/services/api_service.dart';
|
||||||
|
import 'package:marco/helpers/services/app_logger.dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||||
|
import 'package:marco/controller/directory/directory_controller.dart';
|
||||||
|
|
||||||
|
class AddCommentController extends GetxController {
|
||||||
|
final String contactId;
|
||||||
|
|
||||||
|
AddCommentController({required this.contactId});
|
||||||
|
|
||||||
|
final RxString note = ''.obs;
|
||||||
|
final RxBool isSubmitting = false.obs;
|
||||||
|
|
||||||
|
Future<void> submitComment() async {
|
||||||
|
if (note.value.trim().isEmpty) {
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Validation",
|
||||||
|
message: "Comment cannot be empty.",
|
||||||
|
type: SnackbarType.warning,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubmitting.value = true;
|
||||||
|
try {
|
||||||
|
logSafe("Submitting comment for contactId: $contactId");
|
||||||
|
|
||||||
|
final success = await ApiService.addContactComment(
|
||||||
|
note.value.trim(),
|
||||||
|
contactId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
logSafe("Comment added successfully.");
|
||||||
|
|
||||||
|
// Get the directory controller
|
||||||
|
final directoryController = Get.find<DirectoryController>();
|
||||||
|
|
||||||
|
// Fetch latest comments for the contact to refresh UI
|
||||||
|
await directoryController.fetchCommentsForContact(contactId);
|
||||||
|
|
||||||
|
Get.back(result: true);
|
||||||
|
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Success",
|
||||||
|
message: "Comment added successfully.",
|
||||||
|
type: SnackbarType.success,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logSafe("Comment submission failed", level: LogLevel.error);
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to add comment.",
|
||||||
|
type: SnackbarType.error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logSafe("Error while submitting comment: $e", level: LogLevel.error);
|
||||||
|
showAppSnackbar(
|
||||||
|
title: "Error",
|
||||||
|
message: "Something went wrong.",
|
||||||
|
type: SnackbarType.error,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
isSubmitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateNote(String value) {
|
||||||
|
note.value = value;
|
||||||
|
logSafe("Note updated: ${value.trim()}");
|
||||||
|
}
|
||||||
|
}
|
@ -221,6 +221,46 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Directory calling the API
|
/// Directory calling the API
|
||||||
|
static Future<bool> addContactComment(String note, String contactId) async {
|
||||||
|
final payload = {
|
||||||
|
"note": note,
|
||||||
|
"contactId": contactId,
|
||||||
|
};
|
||||||
|
|
||||||
|
final endpoint = ApiEndpoints.updateDirectoryNotes;
|
||||||
|
|
||||||
|
logSafe("Adding new comment with payload: $payload");
|
||||||
|
logSafe("Sending add comment request to $endpoint");
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _postRequest(endpoint, payload);
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
logSafe("Add comment failed: null response", level: LogLevel.error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logSafe("Add comment response status: ${response.statusCode}");
|
||||||
|
logSafe("Add comment response body: ${response.body}");
|
||||||
|
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
|
|
||||||
|
if (json['success'] == true) {
|
||||||
|
logSafe("Comment added successfully for contactId: $contactId");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logSafe("Failed to add comment: ${json['message']}",
|
||||||
|
level: LogLevel.warning);
|
||||||
|
}
|
||||||
|
} catch (e, stack) {
|
||||||
|
logSafe("Exception during addComment API: ${e.toString()}",
|
||||||
|
level: LogLevel.error);
|
||||||
|
logSafe("StackTrace: ${stack.toString()}", level: LogLevel.debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static Future<bool> updateContactComment(
|
static Future<bool> updateContactComment(
|
||||||
String commentId, String note, String contactId) async {
|
String commentId, String note, String contactId) async {
|
||||||
final payload = {
|
final payload = {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_quill/flutter_quill.dart' as quill;
|
import 'package:flutter_quill/flutter_quill.dart' as quill;
|
||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
class CommentEditorCard extends StatelessWidget {
|
class CommentEditorCard extends StatelessWidget {
|
||||||
final quill.QuillController controller;
|
final quill.QuillController controller;
|
||||||
@ -16,60 +15,39 @@ class CommentEditorCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final RxBool _showFullToolbar = false.obs;
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Obx(() {
|
quill.QuillSimpleToolbar(
|
||||||
final showFull = _showFullToolbar.value;
|
controller: controller,
|
||||||
|
configurations: const quill.QuillSimpleToolbarConfigurations(
|
||||||
return Column(
|
showBoldButton: true,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
showItalicButton: true,
|
||||||
children: [
|
showUnderLineButton: true,
|
||||||
quill.QuillSimpleToolbar(
|
showListBullets: true,
|
||||||
controller: controller,
|
showListNumbers: true,
|
||||||
configurations: quill.QuillSimpleToolbarConfigurations(
|
showAlignmentButtons: true,
|
||||||
showBoldButton: true,
|
showLink: true,
|
||||||
showItalicButton: true,
|
showFontSize: false,
|
||||||
showUnderLineButton: showFull,
|
showFontFamily: false,
|
||||||
showListBullets: true,
|
showColorButton: false,
|
||||||
showListNumbers: true,
|
showBackgroundColorButton: false,
|
||||||
showAlignmentButtons: showFull,
|
showUndo: false,
|
||||||
showLink: true,
|
showRedo: false,
|
||||||
showFontSize: showFull,
|
showCodeBlock: false,
|
||||||
showFontFamily: showFull,
|
showQuote: false,
|
||||||
showColorButton: showFull,
|
showSuperscript: false,
|
||||||
showBackgroundColorButton: showFull,
|
showSubscript: false,
|
||||||
showUndo: false,
|
showInlineCode: false,
|
||||||
showRedo: false,
|
showDirection: false,
|
||||||
showCodeBlock: showFull,
|
showListCheck: false,
|
||||||
showQuote: showFull,
|
showStrikeThrough: false,
|
||||||
showSuperscript: false,
|
showClearFormat: false,
|
||||||
showSubscript: false,
|
showDividers: false,
|
||||||
showInlineCode: false,
|
showHeaderStyle: false,
|
||||||
showDirection: false,
|
multiRowsDisplay: false,
|
||||||
showListCheck: false,
|
),
|
||||||
showStrikeThrough: false,
|
),
|
||||||
showClearFormat: showFull,
|
|
||||||
showDividers: false,
|
|
||||||
showHeaderStyle: showFull,
|
|
||||||
multiRowsDisplay: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => _showFullToolbar.toggle(),
|
|
||||||
child: Text(
|
|
||||||
showFull ? "Hide Formatting" : "More Formatting",
|
|
||||||
style: const TextStyle(color: Colors.indigo),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Container(
|
Container(
|
||||||
height: 120,
|
height: 120,
|
||||||
|
142
lib/model/directory/add_comment_bottom_sheet.dart
Normal file
142
lib/model/directory/add_comment_bottom_sheet.dart
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:flutter_quill/flutter_quill.dart' as quill;
|
||||||
|
import 'package:marco/controller/directory/add_comment_controller.dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
|
import 'package:marco/helpers/widgets/Directory/comment_editor_card.dart';
|
||||||
|
|
||||||
|
class AddCommentBottomSheet extends StatefulWidget {
|
||||||
|
final String contactId;
|
||||||
|
|
||||||
|
const AddCommentBottomSheet({super.key, required this.contactId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AddCommentBottomSheet> createState() => _AddCommentBottomSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddCommentBottomSheetState extends State<AddCommentBottomSheet> {
|
||||||
|
late final AddCommentController controller;
|
||||||
|
late final quill.QuillController quillController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
controller = Get.put(AddCommentController(contactId: widget.contactId));
|
||||||
|
// Initialize empty editor for new comment
|
||||||
|
quillController = quill.QuillController.basic();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
quillController.dispose();
|
||||||
|
Get.delete<AddCommentController>();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: MediaQuery.of(context).viewInsets,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).cardColor,
|
||||||
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(color: Colors.black12, blurRadius: 12, offset: Offset(0, -2)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 16, 20, 32),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
width: 40,
|
||||||
|
height: 5,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MySpacing.height(12),
|
||||||
|
Center(child: MyText.titleMedium("Add Comment", fontWeight: 700)),
|
||||||
|
MySpacing.height(24),
|
||||||
|
CommentEditorCard(
|
||||||
|
controller: quillController,
|
||||||
|
onCancel: () => Get.back(),
|
||||||
|
onSave: (controller) async {
|
||||||
|
final delta = controller.document.toDelta();
|
||||||
|
final htmlOutput = _convertDeltaToHtml(delta);
|
||||||
|
this.controller.updateNote(htmlOutput);
|
||||||
|
await this.controller.submitComment();
|
||||||
|
if (mounted) Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _convertDeltaToHtml(dynamic delta) {
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
bool inList = false;
|
||||||
|
|
||||||
|
for (var op in delta.toList()) {
|
||||||
|
final data = op.data?.toString() ?? '';
|
||||||
|
final attr = op.attributes ?? {};
|
||||||
|
|
||||||
|
final isListItem = attr.containsKey('list');
|
||||||
|
|
||||||
|
// Start <ul> if list item starts
|
||||||
|
if (isListItem && !inList) {
|
||||||
|
buffer.write('<ul>');
|
||||||
|
inList = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close <ul> if list ended
|
||||||
|
if (!isListItem && inList) {
|
||||||
|
buffer.write('</ul>');
|
||||||
|
inList = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip empty list items
|
||||||
|
final trimmedData = data.trim();
|
||||||
|
if (isListItem && trimmedData.isEmpty) {
|
||||||
|
// don't write empty <li>
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isListItem) buffer.write('<li>');
|
||||||
|
|
||||||
|
if (attr.containsKey('bold')) buffer.write('<strong>');
|
||||||
|
if (attr.containsKey('italic')) buffer.write('<em>');
|
||||||
|
if (attr.containsKey('underline')) buffer.write('<u>');
|
||||||
|
if (attr.containsKey('strike')) buffer.write('<s>');
|
||||||
|
if (attr.containsKey('link')) buffer.write('<a href="${attr['link']}">');
|
||||||
|
|
||||||
|
// Use trimmedData instead of raw data (removes trailing/leading spaces/newlines)
|
||||||
|
buffer.write(trimmedData.replaceAll('\n', ''));
|
||||||
|
|
||||||
|
if (attr.containsKey('link')) buffer.write('</a>');
|
||||||
|
if (attr.containsKey('strike')) buffer.write('</s>');
|
||||||
|
if (attr.containsKey('underline')) buffer.write('</u>');
|
||||||
|
if (attr.containsKey('italic')) buffer.write('</em>');
|
||||||
|
if (attr.containsKey('bold')) buffer.write('</strong>');
|
||||||
|
|
||||||
|
if (isListItem) {
|
||||||
|
buffer.write('</li>');
|
||||||
|
} else if (data.contains('\n')) {
|
||||||
|
buffer.write('<br>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inList) buffer.write('</ul>');
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
@ -13,6 +13,7 @@ import 'package:marco/helpers/utils/launcher_utils.dart';
|
|||||||
import 'package:tab_indicator_styler/tab_indicator_styler.dart';
|
import 'package:tab_indicator_styler/tab_indicator_styler.dart';
|
||||||
import 'package:marco/helpers/widgets/Directory/comment_editor_card.dart';
|
import 'package:marco/helpers/widgets/Directory/comment_editor_card.dart';
|
||||||
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
|
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
|
||||||
|
import 'package:marco/model/directory/add_comment_bottom_sheet.dart';
|
||||||
|
|
||||||
class ContactDetailScreen extends StatefulWidget {
|
class ContactDetailScreen extends StatefulWidget {
|
||||||
final ContactModel contact;
|
final ContactModel contact;
|
||||||
@ -62,7 +63,8 @@ String _convertDeltaToHtml(dynamic delta) {
|
|||||||
if (attr.containsKey('italic')) buffer.write('</em>');
|
if (attr.containsKey('italic')) buffer.write('</em>');
|
||||||
if (attr.containsKey('bold')) buffer.write('</strong>');
|
if (attr.containsKey('bold')) buffer.write('</strong>');
|
||||||
|
|
||||||
if (isListItem) buffer.write('</li>');
|
if (isListItem)
|
||||||
|
buffer.write('</li>');
|
||||||
else if (data.contains('\n')) buffer.write('<br>');
|
else if (data.contains('\n')) buffer.write('<br>');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +73,6 @@ String _convertDeltaToHtml(dynamic delta) {
|
|||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
||||||
late final DirectoryController directoryController;
|
late final DirectoryController directoryController;
|
||||||
late final ProjectController projectController;
|
late final ProjectController projectController;
|
||||||
@ -203,10 +204,10 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
TabBar(
|
TabBar(
|
||||||
labelColor: Colors.indigo,
|
labelColor: Colors.red,
|
||||||
unselectedLabelColor: Colors.grey,
|
unselectedLabelColor: Colors.black,
|
||||||
indicator: MaterialIndicator(
|
indicator: MaterialIndicator(
|
||||||
color: Colors.indigo,
|
color: Colors.red,
|
||||||
height: 4,
|
height: 4,
|
||||||
topLeftRadius: 8,
|
topLeftRadius: 8,
|
||||||
topRightRadius: 8,
|
topRightRadius: 8,
|
||||||
@ -232,8 +233,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
? widget.contact.contactPhones.first.phoneNumber
|
? widget.contact.contactPhones.first.phoneNumber
|
||||||
: "-";
|
: "-";
|
||||||
|
|
||||||
final createdDate =
|
final createdDate = DateTime.now();
|
||||||
DateTime.now(); // TODO: Replace with actual creation date if available
|
|
||||||
final formattedDate = DateFormat('MMMM dd, yyyy').format(createdDate);
|
final formattedDate = DateFormat('MMMM dd, yyyy').format(createdDate);
|
||||||
final tags = widget.contact.tags.map((e) => e.name).join(", ");
|
final tags = widget.contact.tags.map((e) => e.name).join(", ");
|
||||||
|
|
||||||
@ -301,133 +301,163 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
|
|||||||
|
|
||||||
Widget _buildCommentsTab(BuildContext context) {
|
Widget _buildCommentsTab(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (!directoryController.contactCommentsMap
|
final contactId = widget.contact.id;
|
||||||
.containsKey(widget.contact.id)) {
|
|
||||||
|
if (!directoryController.contactCommentsMap.containsKey(contactId)) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
final comments =
|
final comments = directoryController
|
||||||
directoryController.getCommentsForContact(widget.contact.id);
|
.getCommentsForContact(contactId)
|
||||||
|
.reversed
|
||||||
|
.toList();
|
||||||
|
|
||||||
final editingId = directoryController.editingCommentId.value;
|
final editingId = directoryController.editingCommentId.value;
|
||||||
|
|
||||||
if (comments.isEmpty) {
|
return Stack(
|
||||||
return Center(
|
children: [
|
||||||
child: MyText.bodyLarge("No comments yet.", color: Colors.grey),
|
comments.isEmpty
|
||||||
);
|
? Center(
|
||||||
}
|
child:
|
||||||
|
MyText.bodyLarge("No comments yet.", color: Colors.grey),
|
||||||
return ListView.separated(
|
|
||||||
padding: MySpacing.xy(8, 8),
|
|
||||||
itemCount: comments.length,
|
|
||||||
separatorBuilder: (_, __) => MySpacing.height(12),
|
|
||||||
itemBuilder: (_, index) {
|
|
||||||
final comment = comments[index];
|
|
||||||
final isEditing = editingId == comment.id;
|
|
||||||
|
|
||||||
final initials = comment.createdBy.firstName.isNotEmpty
|
|
||||||
? comment.createdBy.firstName[0].toUpperCase()
|
|
||||||
: "?";
|
|
||||||
|
|
||||||
final decodedDelta = HtmlToDelta().convert(comment.note);
|
|
||||||
|
|
||||||
final quillController = isEditing
|
|
||||||
? quill.QuillController(
|
|
||||||
document: quill.Document.fromDelta(decodedDelta),
|
|
||||||
selection:
|
|
||||||
TextSelection.collapsed(offset: decodedDelta.length),
|
|
||||||
)
|
)
|
||||||
: null;
|
: Padding(
|
||||||
|
padding: MySpacing.xy(8, 8), // Same padding as Details tab
|
||||||
|
child: ListView.separated(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: 80, // Extra bottom padding to avoid FAB overlap
|
||||||
|
),
|
||||||
|
itemCount: comments.length,
|
||||||
|
separatorBuilder: (_, __) => MySpacing.height(12),
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final comment = comments[index];
|
||||||
|
final isEditing = editingId == comment.id;
|
||||||
|
|
||||||
return Container(
|
final initials = comment.createdBy.firstName.isNotEmpty
|
||||||
padding: MySpacing.xy(14, 12),
|
? comment.createdBy.firstName[0].toUpperCase()
|
||||||
decoration: BoxDecoration(
|
: "?";
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
final decodedDelta = HtmlToDelta().convert(comment.note);
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
final quillController = isEditing
|
||||||
color: Colors.black12,
|
? quill.QuillController(
|
||||||
blurRadius: 4,
|
document: quill.Document.fromDelta(decodedDelta),
|
||||||
offset: Offset(0, 2),
|
selection: TextSelection.collapsed(
|
||||||
)
|
offset: decodedDelta.length),
|
||||||
],
|
)
|
||||||
),
|
: null;
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
return Container(
|
||||||
children: [
|
padding: MySpacing.xy(14, 12),
|
||||||
Row(
|
decoration: BoxDecoration(
|
||||||
children: [
|
color: Colors.white,
|
||||||
Avatar(firstName: initials, lastName: '', size: 31),
|
borderRadius: BorderRadius.circular(12),
|
||||||
MySpacing.width(8),
|
boxShadow: const [
|
||||||
Expanded(
|
BoxShadow(
|
||||||
child: Column(
|
color: Colors.black12,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
blurRadius: 4,
|
||||||
children: [
|
offset: Offset(0, 2),
|
||||||
MyText.bodySmall("By: ${comment.createdBy.firstName}",
|
)
|
||||||
fontWeight: 600, color: Colors.indigo[700]),
|
],
|
||||||
MySpacing.height(2),
|
),
|
||||||
MyText.bodySmall(
|
child: Column(
|
||||||
DateFormat('dd MMM yyyy, hh:mm a')
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
.format(comment.createdAt),
|
children: [
|
||||||
fontWeight: 500,
|
Row(
|
||||||
color: Colors.grey[600],
|
children: [
|
||||||
),
|
Avatar(
|
||||||
],
|
firstName: initials,
|
||||||
),
|
lastName: '',
|
||||||
),
|
size: 31),
|
||||||
IconButton(
|
MySpacing.width(8),
|
||||||
icon: Icon(isEditing ? Icons.close : Icons.edit,
|
Expanded(
|
||||||
size: 20, color: Colors.grey[700]),
|
child: Column(
|
||||||
onPressed: () {
|
crossAxisAlignment:
|
||||||
directoryController.editingCommentId.value =
|
CrossAxisAlignment.start,
|
||||||
isEditing ? null : comment.id;
|
children: [
|
||||||
},
|
MyText.bodySmall(
|
||||||
),
|
"By: ${comment.createdBy.firstName}",
|
||||||
],
|
fontWeight: 600,
|
||||||
),
|
color: Colors.indigo[700]),
|
||||||
MySpacing.height(10),
|
MySpacing.height(2),
|
||||||
if (isEditing && quillController != null)
|
MyText.bodySmall(
|
||||||
CommentEditorCard(
|
DateFormat('dd MMM yyyy, hh:mm a')
|
||||||
controller: quillController,
|
.format(comment.createdAt),
|
||||||
onCancel: () {
|
fontWeight: 500,
|
||||||
directoryController.editingCommentId.value = null;
|
color: Colors.grey[600],
|
||||||
},
|
),
|
||||||
onSave: (controller) async {
|
],
|
||||||
final delta = controller.document.toDelta();
|
),
|
||||||
final htmlOutput = _convertDeltaToHtml(delta);
|
),
|
||||||
final updated = comment.copyWith(note: htmlOutput);
|
IconButton(
|
||||||
await directoryController.updateComment(updated);
|
icon: Icon(
|
||||||
directoryController.editingCommentId.value = null;
|
isEditing ? Icons.close : Icons.edit,
|
||||||
})
|
size: 20,
|
||||||
else
|
color: Colors.grey[700]),
|
||||||
html.Html(
|
onPressed: () {
|
||||||
data: comment.note,
|
directoryController.editingCommentId.value =
|
||||||
style: {
|
isEditing ? null : comment.id;
|
||||||
"body": html.Style(
|
},
|
||||||
margin: html.Margins.all(0),
|
),
|
||||||
padding: html.HtmlPaddings.all(0),
|
],
|
||||||
fontSize: html.FontSize.medium,
|
),
|
||||||
color: Colors.black87,
|
MySpacing.height(10),
|
||||||
),
|
if (isEditing && quillController != null)
|
||||||
"pre": html.Style(
|
CommentEditorCard(
|
||||||
padding: html.HtmlPaddings.all(8),
|
controller: quillController,
|
||||||
fontSize: html.FontSize.small,
|
onCancel: () {
|
||||||
fontFamily: 'monospace',
|
directoryController.editingCommentId.value =
|
||||||
backgroundColor: const Color(0xFFF1F1F1),
|
null;
|
||||||
border: Border.all(color: Colors.grey.shade300),
|
},
|
||||||
),
|
onSave: (controller) async {
|
||||||
"h3": html.Style(
|
final delta = controller.document.toDelta();
|
||||||
fontSize: html.FontSize.large,
|
final htmlOutput =
|
||||||
fontWeight: FontWeight.bold,
|
_convertDeltaToHtml(delta);
|
||||||
color: Colors.indigo[700],
|
final updated =
|
||||||
),
|
comment.copyWith(note: htmlOutput);
|
||||||
"strong": html.Style(fontWeight: FontWeight.w700),
|
await directoryController
|
||||||
"p": html.Style(margin: html.Margins.only(bottom: 8)),
|
.updateComment(updated);
|
||||||
|
directoryController.editingCommentId.value =
|
||||||
|
null;
|
||||||
|
})
|
||||||
|
else
|
||||||
|
html.Html(
|
||||||
|
data: comment.note,
|
||||||
|
style: {
|
||||||
|
"body": html.Style(
|
||||||
|
margin: html.Margins.all(0),
|
||||||
|
padding: html.HtmlPaddings.all(0),
|
||||||
|
fontSize: html.FontSize.medium,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
|
||||||
|
// Floating Action Button to Add Comment
|
||||||
|
if (directoryController.editingCommentId.value == null)
|
||||||
|
Positioned(
|
||||||
|
bottom: 16,
|
||||||
|
right: 16,
|
||||||
|
child: FloatingActionButton.extended(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
onPressed: () {
|
||||||
|
Get.bottomSheet(
|
||||||
|
AddCommentBottomSheet(contactId: contactId),
|
||||||
|
isScrollControlled: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add_comment, color: Colors.white),
|
||||||
|
label: const Text("Add Comment",
|
||||||
|
style: TextStyle(color: Colors.white)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
},
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user