Skip to content

Commit 6aa3df0

Browse files
committed
wip
1 parent 2f1b23c commit 6aa3df0

File tree

6 files changed

+105
-25
lines changed

6 files changed

+105
-25
lines changed
Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import React, {useState} from 'react';
1+
import React, {useState, useCallback, memo} from 'react';
22
import styled from 'styled-components';
33
import {useSelector} from 'react-redux';
44
import {RootState} from '../store';
5+
56
// Debug logging utility
6-
const DEBUG = true;
7-
const log = (message: string, data?: any) => {
7+
const DEBUG = process.env.NODE_ENV === 'development';
8+
const log = (message: string, data?: unknown) => {
89
if (DEBUG) {
910
if (data) {
1011
console.log(`[InputArea] ${message}`, data);
@@ -20,8 +21,17 @@ const InputContainer = styled.div`
2021
border-top: 1px solid ${(props) => props.theme.colors.border};
2122
display: ${({theme}) => theme.config?.singleInput ? 'none' : 'block'};
2223
max-height: 10vh;
24+
position: sticky;
25+
bottom: 0;
26+
z-index: 10;
27+
`;
28+
const StyledForm = styled.form`
29+
display: flex;
30+
gap: 1rem;
31+
align-items: flex-start;
2332
`;
2433

34+
2535
const TextArea = styled.textarea`
2636
width: 100%;
2737
padding: 0.5rem;
@@ -31,44 +41,77 @@ const TextArea = styled.textarea`
3141
resize: vertical;
3242
min-height: 40px;
3343
max-height: ${({theme}) => theme.sizing.console.maxHeight};
44+
&:focus {
45+
outline: 2px solid ${(props) => props.theme.colors.primary};
46+
border-color: ${(props) => props.theme.colors.primary};
47+
}
48+
&:disabled {
49+
background-color: ${(props) => props.theme.colors.disabled};
50+
}
51+
`;
52+
const SendButton = styled.button`
53+
padding: 0.5rem 1rem;
54+
background-color: ${(props) => props.theme.colors.primary};
55+
color: white;
56+
border: none;
57+
border-radius: ${(props) => props.theme.sizing.borderRadius.md};
58+
cursor: pointer;
59+
transition: opacity 0.2s;
60+
&:disabled {
61+
opacity: 0.5;
62+
cursor: not-allowed;
63+
}
64+
&:hover:not(:disabled) {
65+
opacity: 0.9;
66+
}
3467
`;
3568

3669
interface InputAreaProps {
3770
onSendMessage: (message: string) => void;
3871
}
3972

40-
const InputArea: React.FC<InputAreaProps> = ({onSendMessage}) => {
73+
const InputArea = memo(function InputArea({onSendMessage}: InputAreaProps) {
4174
log('Initializing component');
4275
const [message, setMessage] = useState('');
4376
const config = useSelector((state: RootState) => state.config);
4477
const [isSubmitting, setIsSubmitting] = useState(false);
4578

46-
const handleSubmit = (e: React.FormEvent) => {
79+
const handleSubmit = useCallback((e: React.FormEvent) => {
4780
e.preventDefault();
81+
if (isSubmitting) return;
82+
4883
log('Submit attempt');
4984
if (message.trim()) {
5085
setIsSubmitting(true);
5186
log('Sending message', {
5287
messageLength: message.length,
5388
message: message.substring(0, 100) + (message.length > 100 ? '...' : '')
5489
});
55-
onSendMessage(message);
56-
setMessage('');
57-
setIsSubmitting(false);
58-
log('Message sent and form reset');
90+
Promise.resolve(onSendMessage(message)).finally(() => {
91+
setMessage('');
92+
setIsSubmitting(false);
93+
log('Message sent and form reset');
94+
});
5995
} else {
6096
log('Empty message, not sending');
6197
}
62-
};
98+
}, [message, onSendMessage]);
6399

64-
const handleMessageChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
100+
const handleMessageChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
65101
const newMessage = e.target.value;
66102
log('Message changed', {
67103
length: newMessage.length,
68104
isEmpty: newMessage.trim().length === 0
69105
});
70106
setMessage(newMessage);
71-
};
107+
}, []);
108+
109+
const handleKeyPress = useCallback((e: React.KeyboardEvent<HTMLTextAreaElement>) => {
110+
if (e.key === 'Enter' && !e.shiftKey) {
111+
e.preventDefault();
112+
handleSubmit(e);
113+
}
114+
}, [handleSubmit]);
72115

73116
React.useEffect(() => {
74117
log('Component mounted', {configState: config});
@@ -80,20 +123,27 @@ const InputArea: React.FC<InputAreaProps> = ({onSendMessage}) => {
80123

81124
return (
82125
<InputContainer>
83-
<form onSubmit={handleSubmit}>
126+
<StyledForm onSubmit={handleSubmit}>
84127
<TextArea
85128
value={message}
86129
onChange={handleMessageChange}
130+
onKeyPress={handleKeyPress}
87131
placeholder="Type a message..."
88132
rows={3}
133+
aria-label="Message input"
134+
disabled={isSubmitting}
89135
/>
90-
<button type="submit">Send</button>
91-
</form>
136+
<SendButton
137+
type="submit"
138+
disabled={isSubmitting || !message.trim()}
139+
aria-label="Send message"
140+
>
141+
Send
142+
</SendButton>
143+
</StyledForm>
92144
</InputContainer>
93145
);
94-
};
95-
// Log when module is imported
96-
log('Module loaded');
146+
});
97147

98148

99149
export default InputArea;

webapp/chat-app/src/components/Menu/Menu.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {ThemeMenu} from "./ThemeMenu";
88
import {WebSocketMenu} from "./WebSocketMenu";
99
import {RootState} from "../../store/index";
1010
import {toggleVerbose} from "../../store/slices/uiSlice";
11+
const isDevelopment = process.env.NODE_ENV === 'development';
1112

1213
const MenuContainer = styled.div`
1314
display: flex;
@@ -140,12 +141,16 @@ export const Menu: React.FC = () => {
140141
</DropdownContent>
141142
</Dropdown>
142143

143-
<Dropdown>
144-
<DropButton onClick={() => console.log('[Menu] Config menu clicked')}>Config</DropButton>
145-
<DropdownContent>
146-
<WebSocketMenu/>
147-
</DropdownContent>
148-
</Dropdown>
144+
{isDevelopment && (
145+
<Dropdown>
146+
<DropButton onClick={() => console.log('[Menu] Config menu clicked')}>
147+
Config
148+
</DropButton>
149+
<DropdownContent>
150+
<WebSocketMenu/>
151+
</DropdownContent>
152+
</Dropdown>
153+
)}
149154

150155
</ToolbarLeft>
151156

@@ -163,4 +168,4 @@ export const Menu: React.FC = () => {
163168
</Dropdown>
164169
</MenuContainer>
165170
);
166-
};
171+
};

webapp/chat-app/src/store/slices/configSlice.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ const loadSavedTheme = (): ThemeName => {
1313
};
1414
// Load websocket config from localStorage or use defaults
1515
const loadWebSocketConfig = () => {
16+
// In production, always use the current host
17+
if (process.env.NODE_ENV !== 'development') {
18+
return {
19+
url: window.location.hostname,
20+
port: window.location.port || (window.location.protocol === 'https:' ? '443' : '80'),
21+
protocol: window.location.protocol === 'https:' ? 'wss:' : 'ws:',
22+
retryAttempts: 3,
23+
timeout: 5000
24+
};
25+
}
26+
1627
try {
1728
const savedConfig = localStorage.getItem('websocketConfig');
1829
if (savedConfig) {
@@ -134,6 +145,12 @@ export const configSlice = createSlice({
134145
state.theme.autoSwitch = !state.theme.autoSwitch;
135146
},
136147
updateWebSocketConfig: (state, action: PayloadAction<Partial<AppConfig['websocket']>>) => {
148+
// Only allow WebSocket config updates in development mode
149+
if (process.env.NODE_ENV !== 'development') {
150+
console.warn('[ConfigSlice] WebSocket config updates are only allowed in development mode');
151+
return;
152+
}
153+
137154
console.log('[ConfigSlice] Updating WebSocket config:', {
138155
previous: state.websocket,
139156
updates: action.payload,

webapp/chat-app/src/styled.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ declare module 'styled-components' {
100100
info: string;
101101
primaryDark?: string;
102102
hover?: string;
103+
disabled: string;
103104
};
104105
}
105106
}

webapp/chat-app/src/themes/themes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ interface ThemeColors {
5454
info: string;
5555
hover?: string;
5656
primaryDark?: string;
57+
disabled: string; // Remove optional marker to make it required
5758
}
5859

5960
interface ThemeSizing {
@@ -277,6 +278,7 @@ export const mainTheme: ExtendedTheme = {
277278
success: '#34C759',
278279
warning: '#FF9500',
279280
info: '#5856D6',
281+
disabled: '#E5E5EA', // Make sure this is defined in all themes
280282
primaryDark: '#0056b3', // Add darker shade of primary
281283
hover: '#2C5282', // Add hover color
282284
},
@@ -301,6 +303,7 @@ export const nightTheme: ExtendedTheme = {
301303
warning: '#FF9F0A',
302304
info: '#5E5CE6',
303305
primaryDark: '#0066cc',
306+
disabled: '#2C2C2E', // Ensure consistent property definition
304307
},
305308
...baseTheme,
306309
};
@@ -323,6 +326,7 @@ export const forestTheme: ExtendedTheme = {
323326
warning: '#F77F00',
324327
info: '#4895EF',
325328
primaryDark: '#1b4332',
329+
disabled: '#2D3B35', // Ensure consistent property definition
326330
},
327331
...baseTheme,
328332
};
@@ -345,6 +349,7 @@ export const ponyTheme: ExtendedTheme = {
345349
warning: '#FFB6C1',
346350
info: '#DB7093',
347351
primaryDark: '#ff1493',
352+
disabled: '#F8E1E7', // Ensure consistent property definition
348353
},
349354
...baseTheme,
350355
};
@@ -367,6 +372,7 @@ export const alienTheme: ExtendedTheme = {
367372
warning: '#FFFF00',
368373
info: '#00FFFF',
369374
primaryDark: '#2bbb0e',
375+
disabled: '#1C1C1C', // Ensure consistent property definition
370376
},
371377
...baseTheme,
372378
};

webapp/chat-app/src/types/styled.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ declare module 'styled-components' {
8484
info: string;
8585
hover?: string;
8686
primaryDark?: string;
87+
disabled: string; // Keep as required to match ExtendedTheme
8788
};
8889
name: string;
8990
activeTab?: string;

0 commit comments

Comments
 (0)