File size: 6,249 Bytes
3382f47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import 'package:auto_gpt_flutter_client/viewmodels/chat_viewmodel.dart';
import 'package:auto_gpt_flutter_client/views/chat/continuous_mode_dialog.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class ChatInputField extends StatefulWidget {
  // Callback to be triggered when the send button is pressed
  final Function(String) onSendPressed;
  final Function() onContinuousModePressed;
  final bool isContinuousMode;
  // TODO: Create a view model for this class and remove the ChatViewModel
  final ChatViewModel viewModel;

  const ChatInputField({
    Key? key,
    required this.onSendPressed,
    required this.onContinuousModePressed,
    this.isContinuousMode = false,
    required this.viewModel,
  }) : super(key: key);

  @override
  _ChatInputFieldState createState() => _ChatInputFieldState();
}

class _ChatInputFieldState extends State<ChatInputField> {
  // Controller for the TextField to manage its content
  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  final FocusNode _throwawayFocusNode = FocusNode();

  @override
  void initState() {
    super.initState();
    _focusNode.addListener(() {
      if (_focusNode.hasFocus && widget.isContinuousMode) {
        widget.onContinuousModePressed();
      }
    });
  }

  @override
  void dispose() {
    _focusNode.dispose(); // Dispose of the FocusNode when you're done.
    super.dispose();
  }

  Future<void> _presentContinuousModeDialogIfNeeded() async {
    final showContinuousModeDialog = await widget.viewModel.prefsService
            .getBool('showContinuousModeDialog') ??
        true;

    FocusScope.of(context).requestFocus(_throwawayFocusNode);
    if (showContinuousModeDialog) {
      showDialog(
        context: context,
        builder: (BuildContext context) {
          return ContinuousModeDialog(
            onProceed: () {
              Navigator.of(context).pop();
              _executeContinuousMode();
            },
            onCheckboxChanged: (bool value) async {
              await widget.viewModel.prefsService
                  .setBool('showContinuousModeDialog', !value);
            },
          );
        },
      );
    } else {
      _executeContinuousMode();
    }
  }

  void _executeContinuousMode() {
    if (!widget.isContinuousMode) {
      widget.onSendPressed(_controller.text);
      _controller.clear();
      _focusNode.unfocus();
    }
    widget.onContinuousModePressed();
  }

  @override
  Widget build(BuildContext context) {
    // Using LayoutBuilder to provide the current constraints of the widget,
    // ensuring it rebuilds when the window size changes
    return LayoutBuilder(
      builder: (context, constraints) {
        // Calculate the width of the chat view based on the constraints provided
        double chatViewWidth = constraints.maxWidth;

        // Determine the width of the input field based on the chat view width.
        // If the chat view width is 1000 or more, the input width will be 900.
        // Otherwise, the input width will be the chat view width minus 40.
        double inputWidth = (chatViewWidth >= 1000) ? 900 : chatViewWidth - 40;

        return Container(
          width: inputWidth,
          // Defining the minimum and maximum height for the TextField container
          constraints: const BoxConstraints(
            minHeight: 50,
            maxHeight: 400,
          ),
          // Styling the container with a border and rounded corners
          decoration: BoxDecoration(
            color: Colors.white,
            border: Border.all(color: Colors.black, width: 0.5),
            borderRadius: BorderRadius.circular(8),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 8),
          // Using SingleChildScrollView to ensure the TextField can scroll
          // when the content exceeds its maximum height
          child: SingleChildScrollView(
            reverse: true,
            child: TextField(
              controller: _controller,
              focusNode: _focusNode,
              // Enable enter key stroke to send the message
              onSubmitted: (_) {
                widget.onSendPressed(_controller.text);
                _controller.clear();
              },
              // Allowing the TextField to expand vertically and accommodate multiple lines
              maxLines: null,
              decoration: InputDecoration(
                hintText: 'Type a message...',
                border: InputBorder.none,
                suffixIcon: Row(
                  mainAxisSize: MainAxisSize.min, // Set to minimum space
                  children: [
                    if (!widget.isContinuousMode)
                      Tooltip(
                        message: 'Send a single message',
                        child: IconButton(
                          splashRadius: 0.1,
                          icon: const Icon(Icons.send),
                          onPressed: () {
                            widget.onSendPressed(_controller.text);
                            _controller.clear();
                          },
                        ),
                      ),
                    Tooltip(
                      message: widget.isContinuousMode
                          ? ''
                          : 'Enable continuous mode',
                      child: IconButton(
                        splashRadius: 0.1,
                        icon: Icon(widget.isContinuousMode
                            ? Icons.pause
                            : Icons.fast_forward),
                        onPressed: () {
                          // TODO: All of this logic should be handled at a higher level in the widget tree. Temporary
                          if (!widget.isContinuousMode) {
                            _presentContinuousModeDialogIfNeeded();
                          } else {
                            widget.onContinuousModePressed();
                          }
                        },
                      ),
                    )
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
  }
}