frontend client for gemstone. decentralised workplace app
at main 7.6 kB view raw
1import { Message } from "@/components/Chat/Message"; 2import { Loading } from "@/components/primitives/Loading"; 3import { Text } from "@/components/primitives/Text"; 4import { useFacet } from "@/lib/facet"; 5import { useChannel } from "@/lib/hooks/useChannel"; 6import type { AtUri } from "@/lib/types/atproto"; 7import { useChannelRecordByAtUriObject } from "@/providers/authed/ChannelsProvider"; 8import { useProfile } from "@/providers/authed/ProfileProvider"; 9import { useCurrentPalette } from "@/providers/ThemeProvider"; 10import { ArrowUp, Dot, Hash } from "lucide-react-native"; 11import { useState } from "react"; 12import { View, TextInput, FlatList, Pressable } from "react-native"; 13 14export const Chat = ({ channelAtUri }: { channelAtUri: AtUri }) => { 15 const [inputText, setInputText] = useState(""); 16 const record = useChannelRecordByAtUriObject(channelAtUri); 17 const { semantic } = useCurrentPalette(); 18 const { typography, atoms } = useFacet(); 19 const channel = useChannel(channelAtUri); 20 const { isLoading } = useProfile(); 21 22 if (!channel) return <></>; 23 24 const { messages, sendMessageToChannel, isConnected } = channel; 25 26 const handleSend = () => { 27 if (inputText.trim()) { 28 sendMessageToChannel(inputText); 29 setInputText(""); 30 } 31 }; 32 33 if (!record) 34 return ( 35 <View> 36 <Text> 37 Something has gone wrong.Could not resolve channel record 38 from given at:// URI. 39 </Text> 40 </View> 41 ); 42 43 return isLoading ? ( 44 <Loading /> 45 ) : ( 46 <View 47 style={{ 48 flex: 1, 49 flexDirection: "column", 50 justifyContent: "center", 51 alignItems: "stretch", 52 backgroundColor: semantic.backgroundDark, 53 }} 54 > 55 <View 56 style={{ 57 paddingHorizontal: 16, 58 paddingVertical: 12, 59 flexDirection: "row", 60 boxShadow: atoms.boxShadows.lg, 61 justifyContent: "space-between", 62 backgroundColor: semantic.background, 63 alignItems: "center", 64 }} 65 > 66 <View 67 style={{ 68 flexDirection: "row", 69 gap: 2, 70 alignItems: "center", 71 }} 72 > 73 <View 74 style={{ 75 flexDirection: "row", 76 gap: 4, 77 alignItems: "center", 78 }} 79 > 80 <Hash 81 style={{ 82 height: 16, 83 width: 16, 84 }} 85 color={semantic.border} 86 /> 87 <Text 88 style={[ 89 { 90 color: semantic.textSecondary, 91 }, 92 typography.sizes.sm, 93 ]} 94 > 95 {record.channel.name} 96 </Text> 97 </View> 98 <View 99 style={{ 100 flexDirection: "row", 101 gap: 2, 102 alignItems: "center", 103 }} 104 > 105 <Dot 106 style={{ 107 height: 24, 108 width: 24, 109 }} 110 color={semantic.border} 111 /> 112 <Text 113 style={[ 114 { 115 color: semantic.textTertiary, 116 }, 117 typography.sizes.sm, 118 ]} 119 > 120 {record.channel.topic} 121 </Text> 122 </View> 123 </View> 124 <View 125 style={{ 126 width: 8, 127 height: 8, 128 borderRadius: atoms.radii.full, 129 backgroundColor: isConnected 130 ? semantic.positive 131 : semantic.negative, 132 }} 133 /> 134 </View> 135 136 <FlatList 137 inverted 138 data={messages.toReversed()} 139 renderItem={({ item }) => <Message message={item} />} 140 keyExtractor={(_, index) => index.toString()} 141 contentContainerStyle={{ 142 paddingLeft: 20, 143 flex: 1, 144 gap: 2, 145 }} 146 showsVerticalScrollIndicator={false} 147 /> 148 149 <View 150 style={{ 151 flexDirection: "row", 152 padding: 16, 153 alignItems: "center", 154 }} 155 > 156 <TextInput 157 editable={isConnected} 158 style={[ 159 { 160 flex: 1, 161 borderWidth: 1, 162 borderColor: semantic.borderVariant, 163 borderRadius: 8, 164 paddingHorizontal: 12, 165 paddingVertical: 12, 166 marginRight: 8, 167 color: semantic.text, 168 outline: "0", 169 cursor: isConnected ? "" : "not-allowed", 170 fontFamily: typography.families.primary, 171 }, 172 typography.weights.byName.extralight, 173 typography.sizes.sm, 174 ]} 175 cursorColor={ 176 isConnected ? semantic.textTertiary : "transparent" 177 } 178 selectionColor={semantic.primary} 179 value={inputText} 180 onChangeText={setInputText} 181 placeholder={`Message #${record.channel.name}`} 182 placeholderTextColor={semantic.textPlaceholder} 183 onSubmitEditing={handleSend} 184 // eslint-disable-next-line @typescript-eslint/no-deprecated -- can't get it working with the new prop. 185 blurOnSubmit={false} 186 /> 187 <Pressable 188 style={{ 189 backgroundColor: inputText.trim() 190 ? semantic.primary 191 : semantic.border, 192 borderRadius: atoms.radii.full, 193 justifyContent: "center", 194 height: 36, 195 width: 36, 196 alignItems: "center", 197 }} 198 onPress={handleSend} 199 disabled={!isConnected} 200 > 201 <ArrowUp height={20} width={20} /> 202 </Pressable> 203 </View> 204 </View> 205 ); 206};