144 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:dio/dio.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:island/models/post.dart';
 | 
						|
import 'package:island/pods/network.dart';
 | 
						|
import 'package:island/widgets/post/compose_settings_sheet.dart';
 | 
						|
import 'package:island/widgets/post/compose_shared.dart';
 | 
						|
 | 
						|
/// Utility class for common compose submit logic.
 | 
						|
class ComposeSubmitUtils {
 | 
						|
  /// Performs the submit action for posts.
 | 
						|
  static Future<SnPost> performSubmit(
 | 
						|
    WidgetRef ref,
 | 
						|
    ComposeState state,
 | 
						|
    BuildContext context, {
 | 
						|
    SnPost? originalPost,
 | 
						|
    SnPost? repliedPost,
 | 
						|
    SnPost? forwardedPost,
 | 
						|
    required Function() onSuccess,
 | 
						|
  }) async {
 | 
						|
    if (state.submitting.value) {
 | 
						|
      throw Exception('Already submitting');
 | 
						|
    }
 | 
						|
 | 
						|
    // Don't submit empty posts (no content and no attachments)
 | 
						|
    final hasContent =
 | 
						|
        state.titleController.text.trim().isNotEmpty ||
 | 
						|
        state.descriptionController.text.trim().isNotEmpty ||
 | 
						|
        state.contentController.text.trim().isNotEmpty;
 | 
						|
    final hasAttachments = state.attachments.value.isNotEmpty;
 | 
						|
 | 
						|
    if (!hasContent && !hasAttachments) {
 | 
						|
      // Show error message if context is mounted
 | 
						|
      if (context.mounted) {
 | 
						|
        ScaffoldMessenger.of(
 | 
						|
          context,
 | 
						|
        ).showSnackBar(SnackBar(content: Text('postContentEmpty')));
 | 
						|
      }
 | 
						|
      throw Exception('Post content is empty'); // Don't submit empty posts
 | 
						|
    }
 | 
						|
 | 
						|
    try {
 | 
						|
      state.submitting.value = true;
 | 
						|
 | 
						|
      // Upload any local attachments first
 | 
						|
      await Future.wait(
 | 
						|
        state.attachments.value
 | 
						|
            .asMap()
 | 
						|
            .entries
 | 
						|
            .where((entry) => entry.value.isOnDevice)
 | 
						|
            .map(
 | 
						|
              (entry) => ComposeLogic.uploadAttachment(ref, state, entry.key),
 | 
						|
            ),
 | 
						|
      );
 | 
						|
 | 
						|
      // Prepare API request
 | 
						|
      final client = ref.read(apiClientProvider);
 | 
						|
      final isNewPost = originalPost == null;
 | 
						|
      final endpoint =
 | 
						|
          '/sphere${isNewPost ? '/posts' : '/posts/${originalPost.id}'}';
 | 
						|
 | 
						|
      // Create request payload
 | 
						|
      final payload = {
 | 
						|
        'title': state.titleController.text,
 | 
						|
        'description': state.descriptionController.text,
 | 
						|
        'content': state.contentController.text,
 | 
						|
        if (state.slugController.text.isNotEmpty)
 | 
						|
          'slug': state.slugController.text,
 | 
						|
        'visibility': state.visibility.value,
 | 
						|
        'attachments':
 | 
						|
            state.attachments.value
 | 
						|
                .where((e) => e.isOnCloud)
 | 
						|
                .map((e) => e.data.id)
 | 
						|
                .toList(),
 | 
						|
        'type': state.postType,
 | 
						|
        if (repliedPost != null) 'replied_post_id': repliedPost.id,
 | 
						|
        if (forwardedPost != null) 'forwarded_post_id': forwardedPost.id,
 | 
						|
        'tags': state.tags.value,
 | 
						|
        'categories': state.categories.value.map((e) => e.slug).toList(),
 | 
						|
        if (state.realm.value != null) 'realm_id': state.realm.value?.id,
 | 
						|
        if (state.pollId.value != null) 'poll_id': state.pollId.value,
 | 
						|
        if (state.embedView.value != null)
 | 
						|
          'embed_view': state.embedView.value!.toJson(),
 | 
						|
      };
 | 
						|
 | 
						|
      // Send request
 | 
						|
      final response = await client.request(
 | 
						|
        endpoint,
 | 
						|
        queryParameters: {'pub': state.currentPublisher.value?.name},
 | 
						|
        data: payload,
 | 
						|
        options: Options(method: isNewPost ? 'POST' : 'PATCH'),
 | 
						|
      );
 | 
						|
 | 
						|
      // Parse the response into a SnPost
 | 
						|
      final post = SnPost.fromJson(response.data);
 | 
						|
 | 
						|
      // Call the success callback
 | 
						|
      onSuccess();
 | 
						|
 | 
						|
      return post;
 | 
						|
    } catch (err) {
 | 
						|
      // Show error message if context is mounted
 | 
						|
      if (context.mounted) {
 | 
						|
        ScaffoldMessenger.of(
 | 
						|
          context,
 | 
						|
        ).showSnackBar(SnackBar(content: Text('Error: $err')));
 | 
						|
      }
 | 
						|
      rethrow;
 | 
						|
    } finally {
 | 
						|
      state.submitting.value = false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /// Shows the settings sheet modal.
 | 
						|
  static void showSettingsSheet(BuildContext context, ComposeState state) {
 | 
						|
    showModalBottomSheet(
 | 
						|
      context: context,
 | 
						|
      isScrollControlled: true,
 | 
						|
      builder: (context) => ComposeSettingsSheet(state: state),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  /// Handles keyboard press events for compose shortcuts.
 | 
						|
  static void handleKeyPress(
 | 
						|
    KeyEvent event,
 | 
						|
    ComposeState state,
 | 
						|
    WidgetRef ref,
 | 
						|
    BuildContext context, {
 | 
						|
    SnPost? originalPost,
 | 
						|
    SnPost? repliedPost,
 | 
						|
    SnPost? forwardedPost,
 | 
						|
  }) {
 | 
						|
    ComposeLogic.handleKeyPress(
 | 
						|
      event,
 | 
						|
      state,
 | 
						|
      ref,
 | 
						|
      context,
 | 
						|
      originalPost: originalPost,
 | 
						|
      repliedPost: repliedPost,
 | 
						|
      forwardedPost: forwardedPost,
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |