Skip to content

Commit 5df38dd

Browse files
authored
Merge pull request #59 from chrisglein/niceties
Small quality of life changes: tooltips, copy responses, click images, initial About page
2 parents 1ec04b1 + ce1cf74 commit 5df38dd

File tree

6 files changed

+92
-23
lines changed

6 files changed

+92
-23
lines changed

src/About.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
import {
3+
Button,
4+
Text,
5+
View,
6+
} from 'react-native';
7+
import {Hyperlink} from './Controls';
8+
import {Popup} from 'react-native-windows';
9+
import {StylesContext} from './Styles';
10+
11+
type AboutPopupProps = {
12+
show: boolean;
13+
close: () => void;
14+
}
15+
function AboutPopup({show, close}: AboutPopupProps): JSX.Element {
16+
const styles = React.useContext(StylesContext);
17+
18+
return (
19+
<Popup
20+
isOpen={show}
21+
isLightDismissEnabled={true}
22+
onDismiss={() => close()}>
23+
<View style={[styles.feedbackDialog, {gap: 12}]}>
24+
<View style={{flexDirection: 'row', marginBottom: 4}}>
25+
<View style={{backgroundColor: 'gray', borderRadius: 4, marginRight: 4}}>
26+
<Text></Text>
27+
</View>
28+
<Text style={{fontWeight: 'bold'}}>About</Text>
29+
</View>
30+
<Hyperlink url='https://github.com/chrisglein/artificial-chat/'/>
31+
<View style={{marginTop: 12, alignSelf: 'flex-end'}}>
32+
<Button
33+
title="OK"
34+
onPress={() => {
35+
close();
36+
}}/>
37+
</View>
38+
</View>
39+
</Popup>
40+
);
41+
}
42+
43+
export { AboutPopup }

src/AiResponse.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import type {PropsWithChildren} from 'react';
33
import {
44
ActivityIndicator,
55
Button,
6-
Pressable,
76
Image,
7+
Linking,
8+
Pressable,
89
Text,
910
View,
1011
} from 'react-native';
@@ -19,6 +20,7 @@ import {
1920
} from './Chat';
2021
import { StylesContext } from './Styles';
2122
import { FeedbackContext } from './Feedback';
23+
import Clipboard from '@react-native-clipboard/clipboard';
2224

2325
type AiImageResponseProps = {
2426
imageUrl?: string;
@@ -29,10 +31,17 @@ function AiImageResponse({imageUrl, prompt, rejectImage}: AiImageResponseProps):
2931
const styles = React.useContext(StylesContext);
3032
return (
3133
<View style={[styles.horizontalContainer, {flexWrap: 'nowrap', alignItems: 'flex-start'}]}>
32-
<Image
33-
source={{uri: imageUrl}}
34-
alt={prompt}
35-
style={[{flexGrow: 0}, styles.dalleImage]}/>
34+
<Pressable
35+
onPress={() => {
36+
if (imageUrl) {
37+
Linking.openURL(imageUrl);
38+
}
39+
}}>
40+
<Image
41+
source={{uri: imageUrl}}
42+
alt={prompt}
43+
style={styles.dalleImage}/>
44+
</Pressable>
3645
<View
3746
style={{flexShrink: 1, gap: 8}}>
3847
<Text>Here is an image created using the following requirements "{prompt}"</Text>
@@ -108,16 +117,17 @@ function AiTextResponse({text}: AiTextResponseProps): JSX.Element {
108117

109118
type AiSectionProps = PropsWithChildren<{
110119
isLoading?: boolean;
120+
copyValue?: string;
111121
contentShownOnHover?: JSX.Element;
112122
}>;
113-
function AiSection({children, isLoading, contentShownOnHover}: AiSectionProps): JSX.Element {
123+
function AiSection({children, isLoading, copyValue, contentShownOnHover}: AiSectionProps): JSX.Element {
114124
const feedbackContext = React.useContext(FeedbackContext);
115125
const styles = React.useContext(StylesContext);
116126
const [hovering, setHovering] = React.useState(false);
117127

118128
const showFeedbackPopup = (positive: boolean) => {
119129
if (feedbackContext) {
120-
feedbackContext.showFeedback(positive);
130+
feedbackContext.showFeedback(positive, copyValue);
121131
}
122132
}
123133

@@ -129,9 +139,9 @@ function AiSection({children, isLoading, contentShownOnHover}: AiSectionProps):
129139
<View style={{flexDirection: 'row'}}>
130140
<Text style={[styles.sectionTitle, {flexGrow: 1}]}>AI</Text>
131141
{hovering && contentShownOnHover}
132-
{hovering && <HoverButton content="📋" onPress={() => console.log("Copy: Not yet implemented")}/>}
133-
<HoverButton content="👍" onPress={() => { showFeedbackPopup(true); }}/>
134-
<HoverButton content="👎" onPress={() => { showFeedbackPopup(false); }}/>
142+
{hovering && copyValue && <HoverButton content="📋" tooltip="Copy to clipboard" onPress={() => Clipboard.setString(copyValue)}/>}
143+
<HoverButton content="👍" tooltip="Give positive feedback" onPress={() => { showFeedbackPopup(true); }}/>
144+
<HoverButton content="👎" tooltip="Give negative feedback" onPress={() => { showFeedbackPopup(false); }}/>
135145
</View>
136146
{isLoading &&
137147
<ActivityIndicator/>
@@ -150,7 +160,7 @@ type AiSectionContentProps = {
150160
function AiSectionContent({id, content}: AiSectionContentProps): JSX.Element {
151161
const chatHistory = React.useContext(ChatHistoryContext);
152162
return (
153-
<AiSection>
163+
<AiSection copyValue={content.text}>
154164
{(() => {
155165
switch (content.contentType) {
156166
case ChatContent.Error:

src/Chat.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
FeedbackPopup,
1515
} from './Feedback';
1616
import { SettingsPopup } from './Settings';
17+
import { AboutPopup } from './About';
1718
import { HoverButton } from './Controls';
1819

1920
enum ChatSource {
@@ -81,11 +82,9 @@ function ChatEntry({submit, defaultText, clearConversation}: ChatEntryProps): JS
8182
onSubmitEditing={submitValue}
8283
value={defaultText ?? value}/>
8384
<Button
84-
style={{flexShrink: 0}}
8585
title="Submit"
8686
onPress={submitValue}/>
8787
<Button
88-
style={{flexShrink: 0}}
8988
title="💣"
9089
onPress={clearConversation}/>
9190
</View>
@@ -103,14 +102,19 @@ function Chat({entries, humanText, onPrompt, clearConversation}: ChatProps): JSX
103102
const styles = React.useContext(StylesContext);
104103
const chatHistory = React.useContext(ChatHistoryContext);
105104
const [showFeedbackPopup, setShowFeedbackPopup] = React.useState(false);
105+
const [feedbackTargetResponse, setFeedbackTargetResponse] = React.useState<string | undefined>(undefined);
106106
const [showSettingsPopup, setShowSettingsPopup] = React.useState(false);
107+
const [showAboutPopup, setShowAboutPopup] = React.useState(false);
107108
const [feedbackIsPositive, setFeedbackIsPositive] = React.useState(false);
108109
const scrollViewRef : React.RefObject<ScrollView> = React.useRef(null);
109110

111+
let showingAnyPopups = (showFeedbackPopup || showSettingsPopup || showAboutPopup);
112+
110113
const feedbackContext = {
111-
showFeedback: (positive: boolean) => {
114+
showFeedback: (positive: boolean, response?: string) => {
112115
setFeedbackIsPositive(positive);
113116
setShowFeedbackPopup(true);
117+
setFeedbackTargetResponse(response);
114118
}
115119
}
116120

@@ -173,7 +177,10 @@ function Chat({entries, humanText, onPrompt, clearConversation}: ChatProps): JSX
173177
disableEdit={true}
174178
disableCopy={true}
175179
contentShownOnHover={
176-
<HoverButton content="⚙️" onPress={() => setShowSettingsPopup(true)}/>
180+
<>
181+
<HoverButton content="❔" tooltip="About" onPress={() => setShowAboutPopup(true)}/>
182+
<HoverButton content="⚙️" tooltip="Settings" onPress={() => setShowSettingsPopup(true)}/>
183+
</>
177184
}>
178185
<ChatEntry
179186
defaultText={humanText}
@@ -184,14 +191,18 @@ function Chat({entries, humanText, onPrompt, clearConversation}: ChatProps): JSX
184191
clearConversation={clearConversation}/>
185192
</HumanSection>
186193
</View>
187-
{ (showFeedbackPopup || showSettingsPopup) && <View style={styles.popupBackground}/> }
194+
{ showingAnyPopups && <View style={styles.popupBackground}/> }
188195
<FeedbackPopup
189196
show={showFeedbackPopup}
190197
isPositive={feedbackIsPositive}
198+
response={feedbackTargetResponse}
191199
close={() => setShowFeedbackPopup(false)}/>
192200
<SettingsPopup
193201
show={showSettingsPopup}
194202
close={() => setShowSettingsPopup(false)}/>
203+
<AboutPopup
204+
show={showAboutPopup}
205+
close={() => setShowAboutPopup(false)}/>
195206
</View>
196207
</ChatScrollContext.Provider>
197208
</FeedbackContext.Provider>

src/Controls.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import {
33
Button,
44
Image,
5+
ImageSourcePropType,
56
Linking,
67
Pressable,
78
Text,
@@ -13,22 +14,24 @@ import { CodeBlock } from './CodeBlock';
1314

1415
type HoverButtonProps = {
1516
content: string;
17+
tooltip: string,
1618
onPress: () => void;
1719
};
18-
function HoverButton({content, onPress}: HoverButtonProps): JSX.Element {
20+
function HoverButton({content, tooltip, onPress}: HoverButtonProps): JSX.Element {
1921
const [hovering, setHovering] = React.useState(false);
2022

2123
const backgroundBaseStyle = {padding: 2, borderRadius: 8, borderWidth: 1, borderColor: 'transparent'};
2224
const backgroundPressedStyle = {borderColor: 'white', backgroundColor: 'black'};
2325
const backgroundHoverStyle = {borderColor: 'white', backgroundColor: 'gray'};
2426
return (
2527
<Pressable
28+
tooltip={tooltip}
2629
onPress={onPress}
2730
onHoverIn={() => setHovering(true)}
2831
onHoverOut={() => setHovering(false)}>
2932
{({pressed}) => (
3033
<View style={[backgroundBaseStyle, pressed ? backgroundPressedStyle : hovering ? backgroundHoverStyle : null]}>
31-
<Text >{content}</Text>
34+
<Text>{content}</Text>
3235
</View>
3336
)}
3437
</Pressable>

src/Feedback.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ import { Popup } from 'react-native-windows';
1010
import { StylesContext } from './Styles';
1111

1212
const FeedbackContext = React.createContext<{
13-
showFeedback : (positive: boolean) => void;
13+
showFeedback : (positive: boolean, response?: string) => void;
1414
}>({showFeedback: () => {}});
1515

1616
type FeedbackPopupProps = {
1717
show: boolean;
1818
close: () => void;
1919
isPositive: boolean;
20+
response?: string;
2021
}
21-
function FeedbackPopup({show, close, isPositive}: FeedbackPopupProps): JSX.Element {
22+
function FeedbackPopup({show, close, isPositive, response}: FeedbackPopupProps): JSX.Element {
2223
const styles = React.useContext(StylesContext);
2324
const [feedbackText, setFeedbackText] = React.useState("");
2425

@@ -61,6 +62,7 @@ function FeedbackPopup({show, close, isPositive}: FeedbackPopupProps): JSX.Eleme
6162
title="Submit feedback"
6263
onPress={() => {
6364
console.log(isPositive ? "like" : "dislike");
65+
console.log(response);
6466
console.log(feedbackText);
6567
close();
6668
}}/>

src/HumanQuery.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type HumanSectionProps = PropsWithChildren<{
1414
content?: string;
1515
disableEdit?: boolean;
1616
disableCopy?: boolean;
17-
contentShownOnHover?: JSX.Element;
17+
contentShownOnHover?: React.ReactNode;
1818
}>;
1919
function HumanSection({children, content, disableEdit, disableCopy, contentShownOnHover}: HumanSectionProps): JSX.Element {
2020
const [hovering, setHovering] = React.useState(false);
@@ -27,8 +27,8 @@ function HumanSection({children, content, disableEdit, disableCopy, contentShown
2727
onHoverOut={() => setHovering(false)}>
2828
<View style={{flexDirection: 'row', minHeight: 26}}>
2929
<Text style={[styles.sectionTitle, {flexGrow: 1}]}>HUMAN</Text>
30-
{hovering && !disableCopy && <HoverButton content="📋" onPress={() => Clipboard.setString(content ?? "")}/>}
31-
{hovering && !disableEdit && <HoverButton content="📝" onPress={() => {console.log("Edit: Not yet implemented")}}/>}
30+
{hovering && !disableCopy && <HoverButton content="📋" tooltip="Copy to clipboard" onPress={() => Clipboard.setString(content ?? "")}/>}
31+
{hovering && !disableEdit && <HoverButton content="📝" tooltip="Edit" onPress={() => {console.log("Edit: Not yet implemented")}}/>}
3232
{hovering && contentShownOnHover}
3333
</View>
3434
{content ? <Text>{content}</Text> : null}

0 commit comments

Comments
 (0)