169 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:easy_localization/easy_localization.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter_hooks/flutter_hooks.dart';
 | |
| import 'package:gap/gap.dart';
 | |
| import 'package:hooks_riverpod/hooks_riverpod.dart';
 | |
| import 'package:island/models/post.dart';
 | |
| import 'package:island/models/publisher.dart';
 | |
| import 'package:island/pods/network.dart';
 | |
| import 'package:island/screens/creators/publishers_form.dart';
 | |
| import 'package:island/screens/posts/compose.dart';
 | |
| import 'package:island/widgets/alert.dart';
 | |
| import 'package:island/widgets/content/cloud_files.dart';
 | |
| import 'package:island/widgets/post/compose_sheet.dart';
 | |
| import 'package:island/widgets/post/publishers_modal.dart';
 | |
| import 'package:material_symbols_icons/symbols.dart';
 | |
| import 'package:styled_widget/styled_widget.dart';
 | |
| 
 | |
| class PostQuickReply extends HookConsumerWidget {
 | |
|   final SnPost parent;
 | |
|   final VoidCallback? onPosted;
 | |
|   final VoidCallback? onLaunch;
 | |
|   const PostQuickReply({
 | |
|     super.key,
 | |
|     required this.parent,
 | |
|     this.onPosted,
 | |
|     this.onLaunch,
 | |
|   });
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context, WidgetRef ref) {
 | |
|     final publishers = ref.watch(publishersManagedProvider);
 | |
| 
 | |
|     final currentPublisher = useState<SnPublisher?>(null);
 | |
| 
 | |
|     useEffect(() {
 | |
|       if (publishers.value?.isNotEmpty ?? false) {
 | |
|         currentPublisher.value = publishers.value!.first;
 | |
|       }
 | |
|       return null;
 | |
|     }, [publishers]);
 | |
| 
 | |
|     final submitting = useState(false);
 | |
| 
 | |
|     final contentController = useTextEditingController();
 | |
| 
 | |
|     Future<void> performAction() async {
 | |
|       if (!contentController.text.isNotEmpty) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       submitting.value = true;
 | |
|       try {
 | |
|         final client = ref.watch(apiClientProvider);
 | |
|         await client.post(
 | |
|           '/sphere/posts',
 | |
|           data: {
 | |
|             'content': contentController.text,
 | |
|             'replied_post_id': parent.id,
 | |
|           },
 | |
|           queryParameters: {'pub': currentPublisher.value?.name},
 | |
|         );
 | |
|         contentController.clear();
 | |
|         onPosted?.call();
 | |
|       } catch (err) {
 | |
|         showErrorAlert(err);
 | |
|       } finally {
 | |
|         submitting.value = false;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const kInputChipHeight = 54.0;
 | |
| 
 | |
|     return publishers.when(
 | |
|       data:
 | |
|           (data) => Material(
 | |
|             elevation: 2,
 | |
|             color: Theme.of(context).colorScheme.surfaceContainerHighest,
 | |
|             borderRadius: BorderRadius.circular(28),
 | |
|             child: Container(
 | |
|               constraints: BoxConstraints(minHeight: kInputChipHeight),
 | |
|               padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
 | |
|               child: Row(
 | |
|                 crossAxisAlignment: CrossAxisAlignment.start,
 | |
|                 children: [
 | |
|                   GestureDetector(
 | |
|                     child: ProfilePictureWidget(
 | |
|                       fileId: currentPublisher.value?.picture?.id,
 | |
|                       radius: (kInputChipHeight * 0.5) - 6,
 | |
|                     ),
 | |
|                     onTap: () {
 | |
|                       showModalBottomSheet(
 | |
|                         isScrollControlled: true,
 | |
|                         context: context,
 | |
|                         builder: (context) => PublisherModal(),
 | |
|                       ).then((value) {
 | |
|                         if (value is SnPublisher) {
 | |
|                           currentPublisher.value = value;
 | |
|                         }
 | |
|                       });
 | |
|                     },
 | |
|                   ).padding(right: 12),
 | |
|                   Expanded(
 | |
|                     child: TextField(
 | |
|                       controller: contentController,
 | |
|                       decoration: InputDecoration(
 | |
|                         hintText: 'postReplyPlaceholder'.tr(),
 | |
|                         border: InputBorder.none,
 | |
|                         isDense: true,
 | |
|                         isCollapsed: true,
 | |
|                         contentPadding: EdgeInsets.symmetric(
 | |
|                           horizontal: 12,
 | |
|                           vertical: 14,
 | |
|                         ),
 | |
|                         visualDensity: VisualDensity.compact,
 | |
|                       ),
 | |
|                       style: TextStyle(fontSize: 14),
 | |
|                       minLines: 1,
 | |
|                       maxLines: 5,
 | |
|                       onTapOutside:
 | |
|                           (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | |
|                     ),
 | |
|                   ),
 | |
|                   const Gap(8),
 | |
|                   IconButton(
 | |
|                     onPressed: () async {
 | |
|                       onLaunch?.call();
 | |
|                       final value = await PostComposeSheet.show(
 | |
|                         context,
 | |
|                         initialState: PostComposeInitialState(
 | |
|                           content: contentController.text,
 | |
|                           replyingTo: parent,
 | |
|                         ),
 | |
|                       );
 | |
|                       if (value != null) onPosted?.call();
 | |
|                     },
 | |
|                     icon: const Icon(Symbols.launch, size: 20),
 | |
|                     visualDensity: VisualDensity.compact,
 | |
|                     constraints: BoxConstraints(
 | |
|                       maxHeight: kInputChipHeight - 6,
 | |
|                       minHeight: kInputChipHeight - 6,
 | |
|                     ),
 | |
|                   ),
 | |
|                   IconButton(
 | |
|                     icon:
 | |
|                         submitting.value
 | |
|                             ? SizedBox(
 | |
|                               width: 28,
 | |
|                               height: 28,
 | |
|                               child: CircularProgressIndicator(strokeWidth: 3),
 | |
|                             )
 | |
|                             : Icon(Symbols.send, size: 20),
 | |
|                     color: Theme.of(context).colorScheme.primary,
 | |
|                     onPressed: submitting.value ? null : performAction,
 | |
|                     visualDensity: VisualDensity.compact,
 | |
|                     constraints: BoxConstraints(
 | |
|                       maxHeight: kInputChipHeight - 6,
 | |
|                       minHeight: kInputChipHeight - 6,
 | |
|                     ),
 | |
|                   ),
 | |
|                 ],
 | |
|               ),
 | |
|             ),
 | |
|           ),
 | |
|       loading: () => const SizedBox.shrink(),
 | |
|       error: (e, _) => const SizedBox.shrink(),
 | |
|     );
 | |
|   }
 | |
| }
 |