@tellescope/chat 1.3.24 → 1.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/chat",
3
- "version": "1.3.24",
3
+ "version": "1.3.26",
4
4
  "description": "",
5
5
  "main": "./lib/cjs/index.js",
6
6
  "module": "./lib/esm/index.js",
@@ -35,12 +35,12 @@
35
35
  "@mui/icons-material": "^5.0.1",
36
36
  "@mui/material": "^5.0.2",
37
37
  "@tellescope/constants": "^1.3.24",
38
- "@tellescope/react-components": "^1.3.24",
39
- "@tellescope/sdk": "^1.3.24",
38
+ "@tellescope/react-components": "^1.3.26",
39
+ "@tellescope/sdk": "^1.3.25",
40
40
  "@tellescope/types-client": "^1.3.24",
41
41
  "@tellescope/types-models": "^1.3.24",
42
42
  "@tellescope/types-utilities": "^1.3.20",
43
- "@tellescope/utilities": "^1.3.24",
43
+ "@tellescope/utilities": "^1.3.25",
44
44
  "@typescript-eslint/eslint-plugin": "^4.33.0",
45
45
  "@typescript-eslint/parser": "^4.33.0",
46
46
  "eslint": "^7.32.0",
@@ -51,7 +51,7 @@
51
51
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
52
52
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
53
53
  },
54
- "gitHead": "1d459c0b21e448037812b8b956d19d7fdadbb6cc",
54
+ "gitHead": "c4626933b58bb8f6fc07f8ba747c31377827218f",
55
55
  "publishConfig": {
56
56
  "access": "public"
57
57
  }
package/src/chat.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, CSSProperties, useEffect, useRef } from "react"
1
+ import React, { useState, CSSProperties, useEffect, useRef, useCallback } from "react"
2
2
 
3
3
  import {
4
4
  DisplayPicture,
@@ -23,12 +23,17 @@ import {
23
23
  useUsers,
24
24
  useEndusers,
25
25
  useUserAndEnduserDisplayInfo,
26
+ UserAndEnduserSelectorProps,
27
+ UserAndEnduserSelector,
28
+ TextField,
26
29
  } from "@tellescope/react-components"
27
30
 
28
31
  import {
29
32
  ChatRoom,
30
33
  ChatMessage,
31
34
  UserDisplayInfo,
35
+ User,
36
+ Enduser,
32
37
  } from "@tellescope/types-client"
33
38
  import {
34
39
  UserActivityStatus,
@@ -36,6 +41,7 @@ import {
36
41
  } from "@tellescope/types-models"
37
42
 
38
43
  import {
44
+ APIError,
39
45
  LoadedData,
40
46
  SessionType,
41
47
  } from "@tellescope/types-utilities"
@@ -222,6 +228,7 @@ export const MessageAttachments = ({ message, chatUserId, imageDimensions } : {
222
228
  marginRight: message.senderId === chatUserId ? 0 : 10,
223
229
  marginLeft: message.senderId === chatUserId ? 10 : 0,
224
230
  justifyContent: message.senderId === chatUserId ? "flex-end" : "flex-start",
231
+ ...imageDimensions,
225
232
  }}>
226
233
  <SecureImage secureName={a.secureName} alt="image attachment" {...imageDimensions} />
227
234
  </Flex>
@@ -229,6 +236,7 @@ export const MessageAttachments = ({ message, chatUserId, imageDimensions } : {
229
236
  {message.attachments.filter(a => a.type === 'video').map(a=> (
230
237
  <Flex key={a.secureName} style={{
231
238
  justifyContent: message.senderId === chatUserId ? "flex-end" : "flex-start",
239
+ ...imageDimensions,
232
240
  }}>
233
241
  <SecureVideo secureName={a.secureName} {...imageDimensions} />
234
242
  </Flex>
@@ -502,4 +510,49 @@ const [, { findById: findRoom } ] = useChatRooms({ dontFetch: true })
502
510
  ))}
503
511
  </Flex>
504
512
  )
513
+ }
514
+
515
+ export interface CreateChatRoomProps extends Omit<UserAndEnduserSelectorProps, 'onSelect'> {
516
+ onSuccess?: (c: ChatRoom) => void,
517
+ onError?: (e: APIError) => void,
518
+ roomTitle?: string,
519
+ }
520
+ export const CreateChatRoom = ({
521
+ roomTitle: defaultRoomTitle = "Group Chat",
522
+ onSuccess, onError,
523
+ ...props
524
+ } : CreateChatRoomProps) => {
525
+ const [, { createElement: createRoom }] = useChatRooms({ dontFetch: true })
526
+ const [roomTitle, setRoomTitle] = useState(defaultRoomTitle ?? '')
527
+
528
+ const handleCreateRoom = useCallback(({ users, endusers }: { users: User[], endusers: Enduser[] }) => {
529
+ const userIds = users.map(u => u.id)
530
+ const enduserIds = endusers.map(e => e.id)
531
+
532
+ createRoom({
533
+ enduserIds,
534
+ userIds,
535
+ title: roomTitle,
536
+ })
537
+ .then(r => {
538
+ onSuccess?.(r)
539
+ })
540
+ .catch(onError)
541
+ }, [createRoom, roomTitle, onSuccess, onError])
542
+
543
+ return (
544
+ <UserAndEnduserSelector {...props} onSelect={handleCreateRoom}
545
+ titleInput={
546
+ <TextField autoFocus value={roomTitle} onChange={t => setRoomTitle(t)}
547
+ style={{
548
+ width: props.searchBarPlacement !== 'top'
549
+ ? '100%'
550
+ : undefined
551
+ }}
552
+ label="Title" placeholder="Enter conversation title..."
553
+ size="small"
554
+ />
555
+ }
556
+ />
557
+ )
505
558
  }
@@ -1,6 +1,6 @@
1
1
  import { launchImageLibrary, launchCamera } from 'react-native-image-picker';
2
- import { Modal, View, TextInput } from "react-native"
3
- import { Avatar, RadioButton } from 'react-native-paper'
2
+ import { Modal, TextInput } from "react-native"
3
+ import { Avatar } from 'react-native-paper'
4
4
  import {
5
5
  useFileUpload,
6
6
  Flex,
@@ -11,20 +11,9 @@ import {
11
11
  Button,
12
12
  Paper,
13
13
  useChats,
14
- useUsers,
15
- useEndusers,
16
- List,
17
- LoadingData,
18
- useChatRooms,
19
- APIError,
20
- LoadingButton,
21
14
  useResolvedSession,
22
15
  } from "@tellescope/react-components"
23
16
  import { useState } from 'react';
24
- import { ChatRoom } from '@tellescope/types-client';
25
- import { render } from 'react-dom';
26
- import { user_display_name } from '@tellescope/utilities';
27
- import { CreateChatRoomProps } from './components';
28
17
 
29
18
  const CHAT_ICON_SIZE = 35
30
19
  const SendImageOrVideo = ({
@@ -181,97 +170,3 @@ export const SendMessage = ({
181
170
  </Flex>
182
171
  )
183
172
  }
184
-
185
- export const CreateChatRoom = ({
186
- excludeEndusers,
187
- excludeUsers,
188
- onGoBack,
189
- onSuccess,
190
- onError=console.error,
191
- roomTitle="Group Chat",
192
- radio,
193
- }: CreateChatRoomProps) => {
194
- const session = useResolvedSession()
195
- const [, { createElement: createRoom }] = useChatRooms()
196
- const [endusersLoading] = useEndusers()
197
- const [usersLoading] = useUsers()
198
-
199
- const [selected, setSelected] = useState<string[]>([])
200
-
201
- return (
202
- <LoadingData data={{ endusers: endusersLoading, users: usersLoading }} render={({ users, endusers }) => (
203
- <Flex flex={1} column>
204
- <Flex alignItems="center" justifyContent={"space-between"} style={{
205
- marginBottom: 10,
206
- }}>
207
- {onGoBack &&
208
- <Button onClick={onGoBack}>
209
- Back
210
- </Button>
211
- }
212
- <Typography style={{ fontSize: 20, textAlign: 'center' }}>
213
- Select Members
214
- </Typography>
215
-
216
- <LoadingButton submitText='Create' submittingText='Create'
217
- disabled={selected.length === 0}
218
- style={{ display: 'flex', marginRight: 5 }}
219
- onClick={() => {
220
- const userIds = selected.filter(s => users.find(u => u.id === s))
221
- const enduserIds = selected.filter(s => endusers.find(u => u.id === s))
222
-
223
- if (session.type === 'enduser') {
224
- enduserIds.push(session.userInfo.id)
225
- } else {
226
- userIds.push(session.userInfo.id)
227
- }
228
-
229
- createRoom({
230
- enduserIds,
231
- userIds,
232
- title: roomTitle,
233
- })
234
- .then(r => {
235
- setSelected([])
236
- onSuccess?.(r)
237
- })
238
- .catch(onError)
239
- }}
240
- />
241
- </Flex>
242
-
243
- <List items={[...excludeUsers ? [] : users, ... excludeEndusers ? [] : endusers].filter(u => u.id !== session.userInfo.id)}
244
- render={user => (
245
- <Paper flex elevation={5} style={{
246
- marginBottom: 2,
247
- }}>
248
- <Flex flex={1} alignItems="center" justifyContent="space-between"
249
- onClick={() => setSelected(ss => (
250
- ss.includes(user.id)
251
- ? radio
252
- ? []
253
- : ss.filter(s => s !== user.id)
254
- :radio
255
- ? [user.id]
256
- : [user.id, ...ss]
257
- ))}
258
- style={{
259
- paddingLeft: 5, paddingRight: 5,
260
- }}
261
- >
262
- <RadioButton value={user.id}
263
- status={selected.includes(user.id) ? 'checked' : 'unchecked'}
264
- />
265
-
266
- <Typography style={{
267
- fontWeight: selected.includes(user.id) ? 'bold' : undefined,
268
- }}>
269
- {user_display_name(user)}
270
- </Typography>
271
- </Flex>
272
- </Paper>
273
- )} />
274
- </Flex>
275
- )} />
276
- )
277
- }
@@ -1,27 +1,11 @@
1
- import React, { CSSProperties, useCallback, useEffect, useMemo, useState } from "react"
2
- import { ChatMessage, ChatRoom, Enduser, User } from "@tellescope/types-client";
1
+ import React, { CSSProperties, useEffect, useState } from "react"
2
+ import { ChatMessage } from "@tellescope/types-client";
3
3
 
4
4
  import {
5
- APIError,
6
5
  AsyncIconButton,
7
- Button,
8
- EnduserOrUserSearch,
9
- EnduserSearch,
10
6
  Flex,
11
- HoverPaper,
12
- List,
13
- LoadingButton,
14
- LoadingData,
15
- Paper,
16
- ScrollingList,
17
7
  SendIcon,
18
- Typography,
19
- useChatRooms,
20
8
  useChats,
21
- useEndusers,
22
- useFilters,
23
- UserSearch,
24
- useUsers,
25
9
  } from "@tellescope/react-components"
26
10
  import { user_display_name } from "@tellescope/utilities";
27
11
  import { Checkbox, TextField, FormControlLabel, Grid } from "@mui/material";
@@ -103,164 +87,4 @@ export const SendMessage = ({
103
87
  </Flex>
104
88
  </Flex>
105
89
  )
106
- }
107
-
108
- export interface CreateChatRoomProps {
109
- excludeEndusers?: boolean,
110
- excludeUsers?: boolean,
111
- onGoBack?: () => void,
112
- onSuccess?: (c: ChatRoom) => void,
113
- onError?: (e: APIError) => void,
114
- title?: string,
115
- roomTitle?: string,
116
- radio?: boolean,
117
- minHeight?: React.CSSProperties['maxHeight']
118
- maxHeight?: React.CSSProperties['maxHeight']
119
- showTitleInput?: boolean,
120
- searchBarPlacement?: "top" | "bottom",
121
- }
122
- export const CreateChatRoom: React.JSXElementConstructor<CreateChatRoomProps> = ({
123
- excludeEndusers,
124
- excludeUsers,
125
- onGoBack,
126
- onSuccess,
127
- onError=console.error,
128
- showTitleInput,
129
- title="Select Members",
130
- roomTitle: defaultRoomTitle = "Group Chat",
131
- minHeight,
132
- maxHeight='50vh',
133
- searchBarPlacement="top"
134
- }) => {
135
- const [, { createElement: createRoom }] = useChatRooms()
136
- const [endusersLoading, { loadMore: loadMoreEndusers, doneLoading: doneLoadingEndusers }] = useEndusers()
137
- const [usersLoading, { loadMore: loadMoreUsers, doneLoading: doneLoadingUsers }] = useUsers()
138
- const [roomTitle, setRoomTitle] = useState(defaultRoomTitle)
139
-
140
- const doneLoading = useCallback(() =>
141
- (excludeUsers || doneLoadingUsers()) && (excludeEndusers || doneLoadingEndusers()),
142
- [doneLoadingEndusers, excludeUsers, excludeEndusers, doneLoadingUsers]
143
- )
144
-
145
- const loadMore = useCallback(async () => {
146
- if (!excludeEndusers && !doneLoadingEndusers()) { loadMoreEndusers().catch(console.error) };
147
- if (!excludeUsers && !doneLoadingUsers()) { loadMoreUsers().catch(console.error) };
148
- }, [doneLoadingEndusers, doneLoadingUsers, loadMoreEndusers, loadMoreUsers])
149
-
150
- const [selected, setSelected] = useState<string[]>([])
151
-
152
- const { applyFilters, ...filterProps } = useFilters<any>()
153
-
154
- const searchbarFullWidth = searchBarPlacement === "bottom"
155
-
156
- const searchbar = useMemo(() => (
157
- excludeUsers
158
- ? <EnduserSearch {...filterProps} fullWidth={searchbarFullWidth} />
159
- : excludeEndusers
160
- ? <UserSearch {...filterProps} fullWidth={searchbarFullWidth} />
161
- : <EnduserOrUserSearch {...filterProps} fullWidth={searchbarFullWidth} />
162
- ), [excludeUsers, excludeEndusers, filterProps, searchbarFullWidth])
163
-
164
- const handleCreateRoom = useCallback((users: User[], endusers: Enduser[]) => {
165
- const userIds = selected.filter(s => users.find(u => u.id === s))
166
- const enduserIds = selected.filter(s => endusers.find(u => u.id === s))
167
-
168
- createRoom({
169
- enduserIds,
170
- userIds,
171
- title: roomTitle,
172
- })
173
- .then(r => {
174
- setSelected([])
175
- onSuccess?.(r)
176
- })
177
- .catch(onError)
178
- }, [createRoom, roomTitle, onSuccess, onError])
179
-
180
- return (
181
- <LoadingData data={{ endusers: endusersLoading, users: usersLoading }} render={({ users, endusers }) => {
182
- const itemsUnfiltered = [...excludeUsers ? [] : users, ... excludeEndusers ? [] : endusers]
183
- const items = applyFilters(itemsUnfiltered)
184
- return (
185
- <Flex flex={1} column>
186
- <Grid container alignItems="center" justifyContent={"space-between"} wrap="nowrap"
187
- style={{
188
- marginBottom: 10,
189
- }}
190
- >
191
- {onGoBack &&
192
- <Button onClick={onGoBack}>
193
- Back
194
- </Button>
195
- }
196
- <Typography style={{ fontSize: 16, textAlign: 'center' }}>
197
- {title}
198
- </Typography>
199
-
200
- <LoadingButton submitText='Create' submittingText='Create'
201
- disabled={selected.length === 0}
202
- style={{ display: 'flex' }}
203
- onClick={() => handleCreateRoom(users, endusers)}
204
- />
205
- </Grid>
206
-
207
- <ScrollingList items={items}
208
- emptyText={
209
- itemsUnfiltered.length === 0
210
- ? "No contacts found"
211
- : "No one found for search"
212
- }
213
- minHeight={minHeight} maxHeight={maxHeight}
214
- doneLoading={doneLoading}
215
- loadMore={loadMore}
216
- title={
217
- showTitleInput &&
218
- <TextField autoFocus value={roomTitle} onChange={e => setRoomTitle(e.target.value)}
219
- fullWidth={searchBarPlacement !== 'top'}
220
- label="Title" placeholder="Enter conversation title..."
221
- size="small"
222
- />
223
- }
224
- titleStyle={searchBarPlacement !== "top" ?
225
- {
226
- width: '100%',
227
- }
228
- : {}
229
- }
230
- titleActionsComponent={searchBarPlacement === 'top' ? searchbar : undefined}
231
- Item={({ item: user }) => (
232
- <HoverPaper style={{
233
- marginBottom: 4,
234
- }}>
235
- <Flex flex={1} alignItems="center" justifyContent="space-between"
236
- onClick={() => setSelected(ss => (
237
- ss.includes(user.id)
238
- ? ss.filter(s => s !== user.id)
239
- : [user.id, ...ss]
240
- ))}
241
- style={{
242
- paddingLeft: 5, paddingRight: 5,
243
- }}
244
- >
245
- <FormControlLabel control={<Checkbox checked={selected.includes(user.id)} />} label="" />
246
-
247
- <Typography style={{
248
- fontWeight: selected.includes(user.id) ? 'bold' : undefined,
249
- }}>
250
- {user_display_name(user)}
251
- </Typography>
252
- </Flex>
253
- </HoverPaper>
254
- )}
255
- />
256
-
257
- {searchBarPlacement === 'bottom' &&
258
- <Grid item alignSelf="flex-end" sx={{ mt: 1, width: '100%' }}>
259
- {searchbar}
260
- </Grid>
261
- }
262
- </Flex>
263
- )}
264
- } />
265
- )
266
90
  }