frontend client for gemstone. decentralised workplace app

refactor: move chat component

serenity d10792b1 5227d5f7

Changed files
+165 -15
src
app
(protected)
components
+3 -5
src/app/(protected)/index.tsx
···
-
import ChatComponentProfiled from "@/components/ChatComponentProfiled";
+
import { Chat } from "@/components/Chat";
import { Loading } from "@/components/primitives/Loading";
import { useChannelRecords } from "@/providers/authed/ChannelsProvider";
import { useSessions } from "@/providers/authed/SessionsProvider";
···
style={{
flex: 1,
justifyContent: "center",
-
alignItems: "center",
+
alignItems: "stretch",
}}
>
{isAppReady ? (
-
<ChatComponentProfiled
-
channelAtUri={channels[0].channelAtUri}
-
/>
+
<Chat channelAtUri={channels[0].channelAtUri} />
) : (
<Loading />
)}
+156
src/components/Chat/index.tsx
···
+
import { Message } from "@/components/Message";
+
import { Loading } from "@/components/primitives/Loading";
+
import { Text } from "@/components/primitives/Text";
+
import { useChannel } from "@/lib/hooks/useChannel";
+
import type { AtUri } from "@/lib/types/atproto";
+
import { useProfile } from "@/providers/authed/ProfileProvider";
+
import { useState } from "react";
+
import {
+
View,
+
TextInput,
+
TouchableOpacity,
+
StyleSheet,
+
ScrollView,
+
Image,
+
} from "react-native";
+
+
export const Chat = ({ channelAtUri }: { channelAtUri: AtUri }) => {
+
const [inputText, setInputText] = useState("");
+
const { messages, sendMessageToChannel, isConnected } =
+
useChannel(channelAtUri);
+
+
const handleSend = () => {
+
if (inputText.trim()) {
+
sendMessageToChannel(inputText);
+
setInputText("");
+
}
+
};
+
+
const { profile, isLoading } = useProfile();
+
+
return isLoading ? (
+
<Loading />
+
) : (
+
<View style={{ flex: 1, flexDirection: "column" }}>
+
{profile && (
+
<View>
+
<View style={styles.profile}>
+
<Text>
+
Hi, {profile.displayName ?? profile.handle}!
+
</Text>
+
{profile.avatar && (
+
<Image
+
style={styles.avatar}
+
source={{ uri: profile.avatar }}
+
/>
+
)}
+
</View>
+
</View>
+
)}
+
<View style={styles.header}>
+
<Text style={styles.status}>
+
{isConnected ? "🟢 Connected" : "🔴 Disconnected"}
+
</Text>
+
</View>
+
+
<ScrollView style={styles.messagesContainer}>
+
{messages.map((msg, index) => (
+
<Message message={msg} key={index} />
+
))}
+
</ScrollView>
+
+
<View style={styles.inputContainer}>
+
<TextInput
+
style={styles.input}
+
value={inputText}
+
onChangeText={setInputText}
+
placeholder="boop"
+
onSubmitEditing={handleSend}
+
/>
+
<TouchableOpacity
+
style={styles.button}
+
onPress={handleSend}
+
disabled={!isConnected}
+
>
+
<Text style={styles.buttonText}>Send</Text>
+
</TouchableOpacity>
+
</View>
+
</View>
+
);
+
};
+
+
const styles = StyleSheet.create({
+
container: {
+
flex: 1,
+
backgroundColor: "#fff",
+
},
+
profile: {
+
display: "flex",
+
flexDirection: "row",
+
justifyContent: "space-between",
+
padding: 16,
+
borderBottomWidth: 1,
+
borderBottomColor: "#e0e0e0",
+
alignItems: "center",
+
},
+
avatar: {
+
width: 32,
+
height: 32,
+
borderRadius: 2000000000,
+
},
+
header: {
+
padding: 16,
+
borderBottomWidth: 1,
+
borderBottomColor: "#e0e0e0",
+
},
+
status: {
+
fontSize: 14,
+
fontWeight: "600",
+
},
+
messagesContainer: {
+
flex: 1,
+
padding: 16,
+
},
+
messageItem: {
+
marginBottom: 12,
+
padding: 12,
+
backgroundColor: "#f5f5f5",
+
borderRadius: 8,
+
},
+
messageText: {
+
fontSize: 16,
+
marginBottom: 4,
+
},
+
timestamp: {
+
fontSize: 12,
+
color: "#666",
+
},
+
inputContainer: {
+
flexDirection: "row",
+
padding: 16,
+
borderTopWidth: 1,
+
borderTopColor: "#e0e0e0",
+
},
+
input: {
+
flex: 1,
+
borderWidth: 1,
+
borderColor: "#ccc",
+
borderRadius: 8,
+
paddingHorizontal: 12,
+
paddingVertical: 8,
+
marginRight: 8,
+
fontSize: 16,
+
},
+
button: {
+
backgroundColor: "#007AFF",
+
paddingHorizontal: 20,
+
paddingVertical: 10,
+
borderRadius: 8,
+
justifyContent: "center",
+
},
+
buttonText: {
+
color: "#fff",
+
fontSize: 16,
+
fontWeight: "600",
+
},
+
});
+6 -10
src/components/ChatComponentProfiled.tsx src/components/Chat/index.web.tsx
···
-
import { Loading } from "@/components/Loading";
import { Message } from "@/components/Message";
+
import { Loading } from "@/components/primitives/Loading";
+
import { Text } from "@/components/primitives/Text";
import { useChannel } from "@/lib/hooks/useChannel";
import type { AtUri } from "@/lib/types/atproto";
import { useProfile } from "@/providers/authed/ProfileProvider";
import { useState } from "react";
import {
View,
-
Text,
TextInput,
TouchableOpacity,
StyleSheet,
···
Image,
} from "react-native";
-
export default function ChatComponentProfiled({
-
channelAtUri,
-
}: {
-
channelAtUri: AtUri;
-
}) {
+
export const Chat = ({ channelAtUri }: { channelAtUri: AtUri }) => {
const [inputText, setInputText] = useState("");
const { messages, sendMessageToChannel, isConnected } =
useChannel(channelAtUri);
···
return isLoading ? (
<Loading />
) : (
-
<View style={styles.container}>
+
<View style={{ flex: 1, flexDirection: "column" }}>
{profile && (
<View>
<View style={styles.profile}>
···
style={styles.input}
value={inputText}
onChangeText={setInputText}
-
placeholder="Type a message..."
+
placeholder="boop"
onSubmitEditing={handleSend}
/>
<TouchableOpacity
···
</View>
</View>
);
-
}
+
};
const styles = StyleSheet.create({
container: {