@planningcenter/chat-react-native 4.3.1-rc.0 → 4.4.0-rc.0
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/build/screens/search/search_screen.d.ts.map +1 -1
- package/build/screens/search/search_screen.js +48 -23
- package/build/screens/search/search_screen.js.map +1 -1
- package/package.json +2 -2
- package/src/screens/search/__tests__/search_screen.test.tsx +76 -0
- package/src/screens/search/search_screen.tsx +22 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search_screen.d.ts","sourceRoot":"","sources":["../../../src/screens/search/search_screen.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"search_screen.d.ts","sourceRoot":"","sources":["../../../src/screens/search/search_screen.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAiB,MAAM,0BAA0B,CAAA;AACvE,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACvB,MAAM,gCAAgC,CAAA;AAcvC,eAAO,MAAM,mBAAmB,GAAI,iBAEjC,sBAAsB,CAAC,aAAa,CAAC,KAAG,4BAIxC,CAAA;AAEH,wBAAgB,YAAY,4CAkC3B"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { c as _c } from "react/compiler-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useNavigation } from '@react-navigation/native';
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
|
-
import { StyleSheet, View } from 'react-native';
|
|
4
|
+
import { Keyboard, StyleSheet, View } from 'react-native';
|
|
5
5
|
import { KeyboardView } from '../../components';
|
|
6
6
|
import { composeOptions, withMinimalBack } from '../../components/display/platform_modal_header_buttons';
|
|
7
7
|
import { useDebouncedSearchTerm } from '../../hooks/use_debounced_search_term';
|
|
@@ -18,47 +18,72 @@ export const SearchScreenOptions = ({
|
|
|
18
18
|
onPress: () => navigation.goBack()
|
|
19
19
|
}));
|
|
20
20
|
export function SearchScreen() {
|
|
21
|
-
const $ = _c(
|
|
21
|
+
const $ = _c(10);
|
|
22
|
+
const navigation = useNavigation();
|
|
22
23
|
const [term, setTerm] = useState("");
|
|
23
24
|
const debouncedTerm = useDebouncedSearchTerm(term);
|
|
24
25
|
let t0;
|
|
25
|
-
if ($[0] !==
|
|
26
|
-
t0 =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
if ($[0] !== navigation) {
|
|
27
|
+
t0 = result => {
|
|
28
|
+
Keyboard.dismiss();
|
|
29
|
+
if (result.type === "conversation") {
|
|
30
|
+
navigation.navigate("Conversation", {
|
|
31
|
+
conversation_id: Number(result.id),
|
|
32
|
+
title: result.title
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
navigation.navigate("Conversation", {
|
|
36
|
+
conversation_id: result.extra.conversationId,
|
|
37
|
+
initial_message_id: result.id,
|
|
38
|
+
title: result.title
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
$[0] = navigation;
|
|
31
43
|
$[1] = t0;
|
|
32
44
|
} else {
|
|
33
45
|
t0 = $[1];
|
|
34
46
|
}
|
|
47
|
+
const openResult = t0;
|
|
35
48
|
let t1;
|
|
36
|
-
if ($[2] !==
|
|
37
|
-
t1 =
|
|
38
|
-
term:
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
$[2] =
|
|
49
|
+
if ($[2] !== term) {
|
|
50
|
+
t1 = /*#__PURE__*/_jsx(SearchField, {
|
|
51
|
+
term: term,
|
|
52
|
+
onChange: setTerm
|
|
53
|
+
});
|
|
54
|
+
$[2] = term;
|
|
42
55
|
$[3] = t1;
|
|
43
56
|
} else {
|
|
44
57
|
t1 = $[3];
|
|
45
58
|
}
|
|
46
59
|
let t2;
|
|
47
|
-
if ($[4] !==
|
|
48
|
-
t2 = /*#__PURE__*/_jsx(
|
|
60
|
+
if ($[4] !== debouncedTerm || $[5] !== openResult) {
|
|
61
|
+
t2 = debouncedTerm.length > 0 ? /*#__PURE__*/_jsx(SearchResultsList, {
|
|
62
|
+
term: debouncedTerm,
|
|
63
|
+
onSelect: openResult
|
|
64
|
+
}, debouncedTerm) : /*#__PURE__*/_jsx(SearchPromptBlankState, {});
|
|
65
|
+
$[4] = debouncedTerm;
|
|
66
|
+
$[5] = openResult;
|
|
67
|
+
$[6] = t2;
|
|
68
|
+
} else {
|
|
69
|
+
t2 = $[6];
|
|
70
|
+
}
|
|
71
|
+
let t3;
|
|
72
|
+
if ($[7] !== t1 || $[8] !== t2) {
|
|
73
|
+
t3 = /*#__PURE__*/_jsx(KeyboardView, {
|
|
49
74
|
children: /*#__PURE__*/_jsxs(View, {
|
|
50
75
|
style: styles.container,
|
|
51
76
|
testID: "search-screen",
|
|
52
|
-
children: [
|
|
77
|
+
children: [t1, t2]
|
|
53
78
|
})
|
|
54
79
|
});
|
|
55
|
-
$[
|
|
56
|
-
$[
|
|
57
|
-
$[
|
|
80
|
+
$[7] = t1;
|
|
81
|
+
$[8] = t2;
|
|
82
|
+
$[9] = t3;
|
|
58
83
|
} else {
|
|
59
|
-
|
|
84
|
+
t3 = $[9];
|
|
60
85
|
}
|
|
61
|
-
return
|
|
86
|
+
return t3;
|
|
62
87
|
}
|
|
63
88
|
const styles = StyleSheet.create({
|
|
64
89
|
container: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search_screen.js","names":["
|
|
1
|
+
{"version":3,"file":"search_screen.js","names":["useNavigation","React","useState","Keyboard","StyleSheet","View","KeyboardView","composeOptions","withMinimalBack","useDebouncedSearchTerm","SearchPromptBlankState","SearchField","SearchResultsList","jsx","_jsx","jsxs","_jsxs","SearchScreenOptions","navigation","title","animation","onPress","goBack","SearchScreen","$","_c","term","setTerm","debouncedTerm","t0","result","dismiss","type","navigate","conversation_id","Number","id","extra","conversationId","initial_message_id","openResult","t1","t2","length","t3","children","style","styles","container","testID","create","flex"],"sources":["../../../src/screens/search/search_screen.tsx"],"sourcesContent":["import { ParamListBase, useNavigation } from '@react-navigation/native'\nimport {\n NativeStackNavigationOptions,\n NativeStackScreenProps,\n} from '@react-navigation/native-stack'\nimport React, { useState } from 'react'\nimport { Keyboard, StyleSheet, View } from 'react-native'\nimport { KeyboardView } from '../../components'\nimport {\n composeOptions,\n withMinimalBack,\n} from '../../components/display/platform_modal_header_buttons'\nimport { useDebouncedSearchTerm } from '../../hooks/use_debounced_search_term'\nimport type { SearchResultResource } from '../../types/resources/search_result'\nimport { SearchPromptBlankState } from './components/search_blank_states'\nimport { SearchField } from './components/search_field'\nimport { SearchResultsList } from './components/search_results_list'\n\nexport const SearchScreenOptions = ({\n navigation,\n}: NativeStackScreenProps<ParamListBase>): NativeStackNavigationOptions =>\n composeOptions(\n { title: 'Search', animation: 'fade' },\n withMinimalBack({ onPress: () => navigation.goBack() })\n )\n\nexport function SearchScreen() {\n const navigation = useNavigation()\n const [term, setTerm] = useState('')\n const debouncedTerm = useDebouncedSearchTerm(term)\n\n const openResult = (result: SearchResultResource) => {\n Keyboard.dismiss()\n\n if (result.type === 'conversation') {\n navigation.navigate('Conversation', {\n conversation_id: Number(result.id),\n title: result.title,\n })\n } else {\n navigation.navigate('Conversation', {\n conversation_id: result.extra.conversationId,\n initial_message_id: result.id,\n title: result.title,\n })\n }\n }\n\n return (\n <KeyboardView>\n <View style={styles.container} testID=\"search-screen\">\n <SearchField term={term} onChange={setTerm} />\n {debouncedTerm.length > 0 ? (\n <SearchResultsList key={debouncedTerm} term={debouncedTerm} onSelect={openResult} />\n ) : (\n <SearchPromptBlankState />\n )}\n </View>\n </KeyboardView>\n )\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n },\n})\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0-rc.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"sf-symbols-typescript": "^2.2.0",
|
|
85
85
|
"typescript": "~6.0.3"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "0539b5f633f3d60712eb79c502ca354180a9f0ac"
|
|
88
88
|
}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { act, fireEvent, render } from '@testing-library/react-native'
|
|
2
2
|
import React from 'react'
|
|
3
|
+
import { Keyboard } from 'react-native'
|
|
3
4
|
import type { useSearchResults } from '../../../hooks/use_search_results'
|
|
5
|
+
import type {
|
|
6
|
+
ConversationSearchResultResource,
|
|
7
|
+
MessageSearchResultResource,
|
|
8
|
+
} from '../../../types/resources/search_result'
|
|
4
9
|
import { SearchScreen } from '../search_screen'
|
|
5
10
|
|
|
11
|
+
const mockNavigate = jest.fn()
|
|
12
|
+
jest.mock('@react-navigation/native', () => ({
|
|
13
|
+
...jest.requireActual('@react-navigation/native'),
|
|
14
|
+
useNavigation: () => ({ navigate: mockNavigate }),
|
|
15
|
+
}))
|
|
16
|
+
|
|
6
17
|
const mockUseSearchResults = jest.fn<ReturnType<typeof useSearchResults>, [string]>()
|
|
7
18
|
jest.mock('../../../hooks/use_search_results', () => ({
|
|
8
19
|
useSearchResults: (term: string) => mockUseSearchResults(term),
|
|
@@ -14,8 +25,48 @@ jest.mock('../../../components/display/keyboard_view', () => ({
|
|
|
14
25
|
|
|
15
26
|
const inputLabel = 'Search titles and messages'
|
|
16
27
|
|
|
28
|
+
const conversationResult: ConversationSearchResultResource = {
|
|
29
|
+
type: 'conversation',
|
|
30
|
+
id: '42',
|
|
31
|
+
title: 'Budget Team',
|
|
32
|
+
url: '/conversations/42',
|
|
33
|
+
extra: { avatar: { previewAvatarUrls: [] } },
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const messageResult: MessageSearchResultResource = {
|
|
37
|
+
type: 'message',
|
|
38
|
+
id: '01JMESSAGEULID',
|
|
39
|
+
title: 'Finance',
|
|
40
|
+
url: '/conversations/7/messages/01JMESSAGEULID',
|
|
41
|
+
extra: {
|
|
42
|
+
conversationId: 7,
|
|
43
|
+
authorName: 'Ann',
|
|
44
|
+
snippet: [{ text: 'budget', match: true }],
|
|
45
|
+
createdAt: '2026-01-01T00:00:00Z',
|
|
46
|
+
avatar: { previewAvatarUrls: [] },
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const renderWithResults = () => {
|
|
51
|
+
mockUseSearchResults.mockReturnValue({
|
|
52
|
+
results: [conversationResult, messageResult],
|
|
53
|
+
failedTypes: [],
|
|
54
|
+
isPending: false,
|
|
55
|
+
isError: false,
|
|
56
|
+
isFetchNextPageError: false,
|
|
57
|
+
loadMore: jest.fn(),
|
|
58
|
+
hasNextPage: false,
|
|
59
|
+
isFetchingNextPage: false,
|
|
60
|
+
})
|
|
61
|
+
const view = render(<SearchScreen />)
|
|
62
|
+
fireEvent.changeText(view.getByLabelText(inputLabel), 'budget')
|
|
63
|
+
act(() => jest.advanceTimersByTime(200))
|
|
64
|
+
return view
|
|
65
|
+
}
|
|
66
|
+
|
|
17
67
|
beforeEach(() => {
|
|
18
68
|
jest.useFakeTimers()
|
|
69
|
+
mockNavigate.mockReset()
|
|
19
70
|
mockUseSearchResults.mockReset().mockReturnValue({
|
|
20
71
|
results: [],
|
|
21
72
|
failedTypes: [],
|
|
@@ -61,4 +112,29 @@ describe('SearchScreen', () => {
|
|
|
61
112
|
|
|
62
113
|
expect(view.getByText('Search chat')).toBeTruthy()
|
|
63
114
|
})
|
|
115
|
+
|
|
116
|
+
it('opens a conversation result at the latest message', () => {
|
|
117
|
+
const dismiss = jest.spyOn(Keyboard, 'dismiss')
|
|
118
|
+
const view = renderWithResults()
|
|
119
|
+
|
|
120
|
+
fireEvent.press(view.getByLabelText('Budget Team'))
|
|
121
|
+
|
|
122
|
+
expect(dismiss).toHaveBeenCalled()
|
|
123
|
+
expect(mockNavigate).toHaveBeenCalledWith('Conversation', {
|
|
124
|
+
conversation_id: 42,
|
|
125
|
+
title: 'Budget Team',
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('opens a message result anchored at that message', () => {
|
|
130
|
+
const view = renderWithResults()
|
|
131
|
+
|
|
132
|
+
fireEvent.press(view.getByLabelText(/^Finance, Ann: budget/))
|
|
133
|
+
|
|
134
|
+
expect(mockNavigate).toHaveBeenCalledWith('Conversation', {
|
|
135
|
+
conversation_id: 7,
|
|
136
|
+
initial_message_id: '01JMESSAGEULID',
|
|
137
|
+
title: 'Finance',
|
|
138
|
+
})
|
|
139
|
+
})
|
|
64
140
|
})
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { ParamListBase } from '@react-navigation/native'
|
|
1
|
+
import { ParamListBase, useNavigation } from '@react-navigation/native'
|
|
2
2
|
import {
|
|
3
3
|
NativeStackNavigationOptions,
|
|
4
4
|
NativeStackScreenProps,
|
|
5
5
|
} from '@react-navigation/native-stack'
|
|
6
|
-
import { noop } from 'lodash'
|
|
7
6
|
import React, { useState } from 'react'
|
|
8
|
-
import { StyleSheet, View } from 'react-native'
|
|
7
|
+
import { Keyboard, StyleSheet, View } from 'react-native'
|
|
9
8
|
import { KeyboardView } from '../../components'
|
|
10
9
|
import {
|
|
11
10
|
composeOptions,
|
|
12
11
|
withMinimalBack,
|
|
13
12
|
} from '../../components/display/platform_modal_header_buttons'
|
|
14
13
|
import { useDebouncedSearchTerm } from '../../hooks/use_debounced_search_term'
|
|
14
|
+
import type { SearchResultResource } from '../../types/resources/search_result'
|
|
15
15
|
import { SearchPromptBlankState } from './components/search_blank_states'
|
|
16
16
|
import { SearchField } from './components/search_field'
|
|
17
17
|
import { SearchResultsList } from './components/search_results_list'
|
|
@@ -25,15 +25,33 @@ export const SearchScreenOptions = ({
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
export function SearchScreen() {
|
|
28
|
+
const navigation = useNavigation()
|
|
28
29
|
const [term, setTerm] = useState('')
|
|
29
30
|
const debouncedTerm = useDebouncedSearchTerm(term)
|
|
30
31
|
|
|
32
|
+
const openResult = (result: SearchResultResource) => {
|
|
33
|
+
Keyboard.dismiss()
|
|
34
|
+
|
|
35
|
+
if (result.type === 'conversation') {
|
|
36
|
+
navigation.navigate('Conversation', {
|
|
37
|
+
conversation_id: Number(result.id),
|
|
38
|
+
title: result.title,
|
|
39
|
+
})
|
|
40
|
+
} else {
|
|
41
|
+
navigation.navigate('Conversation', {
|
|
42
|
+
conversation_id: result.extra.conversationId,
|
|
43
|
+
initial_message_id: result.id,
|
|
44
|
+
title: result.title,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
return (
|
|
32
50
|
<KeyboardView>
|
|
33
51
|
<View style={styles.container} testID="search-screen">
|
|
34
52
|
<SearchField term={term} onChange={setTerm} />
|
|
35
53
|
{debouncedTerm.length > 0 ? (
|
|
36
|
-
<SearchResultsList key={debouncedTerm} term={debouncedTerm} onSelect={
|
|
54
|
+
<SearchResultsList key={debouncedTerm} term={debouncedTerm} onSelect={openResult} />
|
|
37
55
|
) : (
|
|
38
56
|
<SearchPromptBlankState />
|
|
39
57
|
)}
|