@planningcenter/chat-react-native 4.3.0-rc.5 → 4.3.0-rc.7
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/components/display/conversation_avatar.d.ts +1 -2
- package/build/components/display/conversation_avatar.d.ts.map +1 -1
- package/build/components/display/conversation_avatar.js.map +1 -1
- package/build/navigation/index.d.ts +5 -0
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +5 -0
- package/build/navigation/index.js.map +1 -1
- package/build/screens/conversations/components/search_list_header_component.d.ts.map +1 -1
- package/build/screens/conversations/components/search_list_header_component.js +159 -133
- package/build/screens/conversations/components/search_list_header_component.js.map +1 -1
- package/build/screens/search/search_screen.d.ts +5 -0
- package/build/screens/search/search_screen.d.ts.map +1 -0
- package/build/screens/search/search_screen.js +33 -0
- package/build/screens/search/search_screen.js.map +1 -0
- package/build/types/resources/conversation.d.ts +1 -0
- package/build/types/resources/conversation.d.ts.map +1 -1
- package/build/types/resources/conversation.js.map +1 -1
- package/build/types/resources/index.d.ts +1 -0
- package/build/types/resources/index.d.ts.map +1 -1
- package/build/types/resources/index.js +1 -0
- package/build/types/resources/index.js.map +1 -1
- package/build/types/resources/search_result.d.ts +29 -0
- package/build/types/resources/search_result.d.ts.map +1 -0
- package/build/types/resources/search_result.js +2 -0
- package/build/types/resources/search_result.js.map +1 -0
- package/build/utils/request/get_search_results.d.ts +13 -0
- package/build/utils/request/get_search_results.d.ts.map +1 -0
- package/build/utils/request/get_search_results.js +20 -0
- package/build/utils/request/get_search_results.js.map +1 -0
- package/package.json +2 -2
- package/src/components/display/conversation_avatar.tsx +1 -10
- package/src/navigation/index.tsx +5 -0
- package/src/screens/conversations/components/__tests__/search_list_header_component.test.tsx +62 -0
- package/src/screens/conversations/components/search_list_header_component.tsx +11 -0
- package/src/screens/search/search_screen.tsx +29 -0
- package/src/types/resources/conversation.ts +9 -0
- package/src/types/resources/index.ts +1 -0
- package/src/types/resources/search_result.ts +32 -0
- package/src/utils/__tests__/get_search_results.test.ts +24 -0
- package/src/utils/request/get_search_results.ts +23 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/resources/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,2BAA2B,CAAA;AACzC,cAAc,yBAAyB,CAAA;AACvC,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/resources/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,2BAA2B,CAAA;AACzC,cAAc,yBAAyB,CAAA;AACvC,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/types/resources/index.ts"],"sourcesContent":["export * from './analytics_metadata'\nexport * from './direct_message_affiliations'\nexport * from './direct_message_candidate'\nexport * from './conversation'\nexport * from './conversation_membership'\nexport * from './conversation_validate'\nexport * from './member'\nexport * from './message'\nexport * from './oauth_token'\nexport * from './person'\nexport * from './groups'\nexport * from './app_grant'\nexport * from './services'\nexport * from './organization'\nexport * from './group_membership'\nexport * from './group_resource'\n"],"mappings":"AAAA,cAAc,sBAAsB;AACpC,cAAc,+BAA+B;AAC7C,cAAc,4BAA4B;AAC1C,cAAc,gBAAgB;AAC9B,cAAc,2BAA2B;AACzC,cAAc,yBAAyB;AACvC,cAAc,UAAU;AACxB,cAAc,WAAW;AACzB,cAAc,eAAe;AAC7B,cAAc,UAAU;AACxB,cAAc,UAAU;AACxB,cAAc,aAAa;AAC3B,cAAc,YAAY;AAC1B,cAAc,gBAAgB;AAC9B,cAAc,oBAAoB;AAClC,cAAc,kBAAkB","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/types/resources/index.ts"],"sourcesContent":["export * from './analytics_metadata'\nexport * from './direct_message_affiliations'\nexport * from './direct_message_candidate'\nexport * from './conversation'\nexport * from './conversation_membership'\nexport * from './conversation_validate'\nexport * from './member'\nexport * from './message'\nexport * from './oauth_token'\nexport * from './person'\nexport * from './groups'\nexport * from './app_grant'\nexport * from './services'\nexport * from './organization'\nexport * from './group_membership'\nexport * from './group_resource'\nexport * from './search_result'\n"],"mappings":"AAAA,cAAc,sBAAsB;AACpC,cAAc,+BAA+B;AAC7C,cAAc,4BAA4B;AAC1C,cAAc,gBAAgB;AAC9B,cAAc,2BAA2B;AACzC,cAAc,yBAAyB;AACvC,cAAc,UAAU;AACxB,cAAc,WAAW;AACzB,cAAc,eAAe;AAC7B,cAAc,UAAU;AACxB,cAAc,UAAU;AACxB,cAAc,aAAa;AAC3B,cAAc,YAAY;AAC1B,cAAc,gBAAgB;AAC9B,cAAc,oBAAoB;AAClC,cAAc,kBAAkB;AAChC,cAAc,iBAAiB","ignoreList":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ConversationAvatarData } from './conversation';
|
|
2
|
+
interface BaseSearchResultResource {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
url: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SnippetSegment {
|
|
8
|
+
text: string;
|
|
9
|
+
match: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ConversationSearchResultResource extends BaseSearchResultResource {
|
|
12
|
+
type: 'conversation';
|
|
13
|
+
extra: {
|
|
14
|
+
avatar: ConversationAvatarData;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface MessageSearchResultResource extends BaseSearchResultResource {
|
|
18
|
+
type: 'message';
|
|
19
|
+
extra: {
|
|
20
|
+
conversationId: number;
|
|
21
|
+
authorName: string;
|
|
22
|
+
snippet: SnippetSegment[];
|
|
23
|
+
createdAt: string;
|
|
24
|
+
avatar: ConversationAvatarData;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export type SearchResultResource = ConversationSearchResultResource | MessageSearchResultResource;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=search_result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search_result.d.ts","sourceRoot":"","sources":["../../../src/types/resources/search_result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAE5D,UAAU,wBAAwB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,gCAAiC,SAAQ,wBAAwB;IAChF,IAAI,EAAE,cAAc,CAAA;IACpB,KAAK,EAAE;QACL,MAAM,EAAE,sBAAsB,CAAA;KAC/B,CAAA;CACF;AAED,MAAM,WAAW,2BAA4B,SAAQ,wBAAwB;IAC3E,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE;QACL,cAAc,EAAE,MAAM,CAAA;QACtB,UAAU,EAAE,MAAM,CAAA;QAClB,OAAO,EAAE,cAAc,EAAE,CAAA;QACzB,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,EAAE,sBAAsB,CAAA;KAC/B,CAAA;CACF;AAED,MAAM,MAAM,oBAAoB,GAAG,gCAAgC,GAAG,2BAA2B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search_result.js","names":[],"sources":["../../../src/types/resources/search_result.ts"],"sourcesContent":["import type { ConversationAvatarData } from './conversation'\n\ninterface BaseSearchResultResource {\n id: string\n title: string\n url: string\n}\n\nexport interface SnippetSegment {\n text: string\n match: boolean\n}\n\nexport interface ConversationSearchResultResource extends BaseSearchResultResource {\n type: 'conversation'\n extra: {\n avatar: ConversationAvatarData\n }\n}\n\nexport interface MessageSearchResultResource extends BaseSearchResultResource {\n type: 'message'\n extra: {\n conversationId: number\n authorName: string\n snippet: SnippetSegment[]\n createdAt: string\n avatar: ConversationAvatarData\n }\n}\n\nexport type SearchResultResource = ConversationSearchResultResource | MessageSearchResultResource\n"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const SEARCH_RESULTS_PAGE_SIZE = 20;
|
|
2
|
+
export declare const getSearchResultsRequestArgs: (term: string) => {
|
|
3
|
+
url: string;
|
|
4
|
+
data: {
|
|
5
|
+
q: string;
|
|
6
|
+
perPage: number;
|
|
7
|
+
fields: {
|
|
8
|
+
SearchResult: string[];
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare const getSearchResultsQueryKey: (term: string) => import("../..").RequestQueryKey;
|
|
13
|
+
//# sourceMappingURL=get_search_results.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get_search_results.d.ts","sourceRoot":"","sources":["../../../src/utils/request/get_search_results.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAE1C,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM;;;;;;;;;CAavD,CAAA;AAED,eAAO,MAAM,wBAAwB,GAAI,MAAM,MAAM,oCAGpD,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getRequestQueryKey } from '../../hooks/use_suspense_api';
|
|
2
|
+
export const SEARCH_RESULTS_PAGE_SIZE = 20;
|
|
3
|
+
export const getSearchResultsRequestArgs = term => {
|
|
4
|
+
const url = '/search_results';
|
|
5
|
+
return {
|
|
6
|
+
url,
|
|
7
|
+
data: {
|
|
8
|
+
q: term,
|
|
9
|
+
perPage: SEARCH_RESULTS_PAGE_SIZE,
|
|
10
|
+
fields: {
|
|
11
|
+
SearchResult: ['type', 'title', 'url', 'extra']
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export const getSearchResultsQueryKey = term => {
|
|
17
|
+
const requestArgs = getSearchResultsRequestArgs(term);
|
|
18
|
+
return getRequestQueryKey(requestArgs);
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=get_search_results.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get_search_results.js","names":["getRequestQueryKey","SEARCH_RESULTS_PAGE_SIZE","getSearchResultsRequestArgs","term","url","data","q","perPage","fields","SearchResult","getSearchResultsQueryKey","requestArgs"],"sources":["../../../src/utils/request/get_search_results.ts"],"sourcesContent":["import { getRequestQueryKey } from '../../hooks/use_suspense_api'\n\nexport const SEARCH_RESULTS_PAGE_SIZE = 20\n\nexport const getSearchResultsRequestArgs = (term: string) => {\n const url = '/search_results'\n\n return {\n url,\n data: {\n q: term,\n perPage: SEARCH_RESULTS_PAGE_SIZE,\n fields: {\n SearchResult: ['type', 'title', 'url', 'extra'],\n },\n },\n }\n}\n\nexport const getSearchResultsQueryKey = (term: string) => {\n const requestArgs = getSearchResultsRequestArgs(term)\n return getRequestQueryKey(requestArgs)\n}\n"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,8BAA8B;AAEjE,OAAO,MAAMC,wBAAwB,GAAG,EAAE;AAE1C,OAAO,MAAMC,2BAA2B,GAAIC,IAAY,IAAK;EAC3D,MAAMC,GAAG,GAAG,iBAAiB;EAE7B,OAAO;IACLA,GAAG;IACHC,IAAI,EAAE;MACJC,CAAC,EAAEH,IAAI;MACPI,OAAO,EAAEN,wBAAwB;MACjCO,MAAM,EAAE;QACNC,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO;MAChD;IACF;EACF,CAAC;AACH,CAAC;AAED,OAAO,MAAMC,wBAAwB,GAAIP,IAAY,IAAK;EACxD,MAAMQ,WAAW,GAAGT,2BAA2B,CAACC,IAAI,CAAC;EACrD,OAAOH,kBAAkB,CAACW,WAAW,CAAC;AACxC,CAAC","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "4.3.0-rc.
|
|
3
|
+
"version": "4.3.0-rc.7",
|
|
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": "0709c9d5b579af4f138ca1fd067b4e7f180768a6"
|
|
88
88
|
}
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type { ViewStyle } from 'react-native'
|
|
3
|
-
import type {
|
|
3
|
+
import type { ConversationAvatarData } from '../../types'
|
|
4
4
|
import AvatarPrimitive, { type AvatarRootProps } from '../primitive/avatar_primitive'
|
|
5
5
|
import { AvatarGroup } from './avatar_group'
|
|
6
6
|
import { EmojiAvatar } from './emoji_avatar'
|
|
7
7
|
import { type IconString } from './icon'
|
|
8
8
|
import { IconAvatar } from './icon_avatar'
|
|
9
9
|
|
|
10
|
-
type ConversationAvatarData = Pick<
|
|
11
|
-
ConversationResource,
|
|
12
|
-
| 'customAvatarType'
|
|
13
|
-
| 'customAvatarKey'
|
|
14
|
-
| 'customAvatarColor'
|
|
15
|
-
| 'customAvatarImageUrl'
|
|
16
|
-
| 'previewAvatarUrls'
|
|
17
|
-
>
|
|
18
|
-
|
|
19
10
|
type ResolvedAvatar =
|
|
20
11
|
| { kind: 'image'; url: string }
|
|
21
12
|
| { kind: 'icon'; iconKey: string; color: string | null }
|
package/src/navigation/index.tsx
CHANGED
|
@@ -72,6 +72,7 @@ import { NotFound } from '../screens/not_found'
|
|
|
72
72
|
import { NotificationSettingsScreen } from '../screens/notification_settings_screen'
|
|
73
73
|
import { PreferredAppSelectionScreen } from '../screens/preferred_app_selection_screen'
|
|
74
74
|
import { ReactionsScreen, ReactionsScreenOptions } from '../screens/reactions_screen'
|
|
75
|
+
import { SearchScreen, SearchScreenOptions } from '../screens/search/search_screen'
|
|
75
76
|
import { SendGiphyScreen, SendGiphyScreenOptions } from '../screens/send_giphy_screen'
|
|
76
77
|
import { SettingsScreen } from '../screens/settings_screen'
|
|
77
78
|
import {
|
|
@@ -186,6 +187,10 @@ export const ChatStack = createNativeStackNavigator({
|
|
|
186
187
|
screen: ConversationFiltersScreen,
|
|
187
188
|
options: ConversationFiltersScreenOptions,
|
|
188
189
|
},
|
|
190
|
+
Search: {
|
|
191
|
+
screen: SearchScreen,
|
|
192
|
+
options: SearchScreenOptions,
|
|
193
|
+
},
|
|
189
194
|
Conversation: {
|
|
190
195
|
screen: ConversationScreen,
|
|
191
196
|
options: ({ route, navigation }) =>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/react-native'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { SearchListHeaderComponent } from '../search_list_header_component'
|
|
4
|
+
|
|
5
|
+
const mockNavigate = jest.fn()
|
|
6
|
+
|
|
7
|
+
jest.mock('@react-navigation/native', () => ({
|
|
8
|
+
...jest.requireActual('@react-navigation/native'),
|
|
9
|
+
useNavigation: () => ({ setParams: jest.fn(), navigate: mockNavigate }),
|
|
10
|
+
useRoute: () => ({ params: {} }),
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
jest.mock('../../../../hooks/use_groups', () => ({
|
|
14
|
+
useCanDisplayGroups: () => true,
|
|
15
|
+
}))
|
|
16
|
+
jest.mock('../../../../hooks/use_direct_messages_eligible', () => ({
|
|
17
|
+
useDirectMessagesEligible: () => true,
|
|
18
|
+
}))
|
|
19
|
+
jest.mock('../../../../hooks/use_has_direct_messages', () => ({
|
|
20
|
+
useHasDirectMessages: () => true,
|
|
21
|
+
}))
|
|
22
|
+
jest.mock('../../../../hooks/use_chat_permissions', () => ({
|
|
23
|
+
useCanCreateConversations: () => true,
|
|
24
|
+
}))
|
|
25
|
+
jest.mock('../../../../hooks/use_new_conversation_entry', () => ({
|
|
26
|
+
useNewConversationEntry: () => 'select_type',
|
|
27
|
+
}))
|
|
28
|
+
jest.mock('../../../../hooks/use_current_person', () => ({
|
|
29
|
+
useCurrentPersonCache: () => ({ update: jest.fn() }),
|
|
30
|
+
}))
|
|
31
|
+
jest.mock('../../../../hooks/use_conversations_actions', () => ({
|
|
32
|
+
useMarkAllRead: () => ({ markAllRead: jest.fn(), isPending: false }),
|
|
33
|
+
}))
|
|
34
|
+
jest.mock('../../../../hooks/use_new_conversation_from_filter', () => ({
|
|
35
|
+
useNewConversationFromFilter: () => undefined,
|
|
36
|
+
}))
|
|
37
|
+
jest.mock('../../../../hooks/use_api', () => ({
|
|
38
|
+
useApiGet: () => ({ data: undefined }),
|
|
39
|
+
}))
|
|
40
|
+
|
|
41
|
+
describe('SearchListHeaderComponent search button', () => {
|
|
42
|
+
beforeEach(() => mockNavigate.mockClear())
|
|
43
|
+
|
|
44
|
+
it('navigates to Search on press', () => {
|
|
45
|
+
const view = render(<SearchListHeaderComponent />)
|
|
46
|
+
|
|
47
|
+
fireEvent.press(view.getByTestId('conversations-search-button'))
|
|
48
|
+
|
|
49
|
+
expect(mockNavigate).toHaveBeenCalledTimes(1)
|
|
50
|
+
expect(mockNavigate).toHaveBeenCalledWith('Search')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('renders the search button after the other actions', () => {
|
|
54
|
+
const view = render(<SearchListHeaderComponent />)
|
|
55
|
+
|
|
56
|
+
const buttons = view.getAllByRole('button').map(button => button.props.accessibilityLabel)
|
|
57
|
+
|
|
58
|
+
expect(buttons.indexOf('Search')).toBeGreaterThanOrEqual(0)
|
|
59
|
+
expect(buttons.indexOf('Search')).toBeGreaterThan(buttons.indexOf('Chat settings'))
|
|
60
|
+
expect(buttons.indexOf('Search')).toBeGreaterThan(buttons.indexOf('New conversation'))
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -110,6 +110,10 @@ export const SearchListHeaderComponent = () => {
|
|
|
110
110
|
[navigation]
|
|
111
111
|
)
|
|
112
112
|
|
|
113
|
+
const handleSearch = useCallback(() => {
|
|
114
|
+
navigation.navigate('Search')
|
|
115
|
+
}, [navigation])
|
|
116
|
+
|
|
113
117
|
const handleChatSettings = useCallback(() => {
|
|
114
118
|
navigation.navigate('ChatSettings', {})
|
|
115
119
|
}, [navigation])
|
|
@@ -188,6 +192,13 @@ export const SearchListHeaderComponent = () => {
|
|
|
188
192
|
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
189
193
|
/>
|
|
190
194
|
)}
|
|
195
|
+
<ListHeaderActionButton
|
|
196
|
+
name="general.search"
|
|
197
|
+
onPress={handleSearch}
|
|
198
|
+
accessibilityLabel="Search"
|
|
199
|
+
testID="conversations-search-button"
|
|
200
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
|
|
201
|
+
/>
|
|
191
202
|
{shouldShowHeaderFiltersButton && <ToggleButton {...moreFiltersButtonProps} />}
|
|
192
203
|
</View>
|
|
193
204
|
</View>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ParamListBase } from '@react-navigation/native'
|
|
2
|
+
import {
|
|
3
|
+
NativeStackNavigationOptions,
|
|
4
|
+
NativeStackScreenProps,
|
|
5
|
+
} from '@react-navigation/native-stack'
|
|
6
|
+
import React from 'react'
|
|
7
|
+
import { StyleSheet, View } from 'react-native'
|
|
8
|
+
import {
|
|
9
|
+
composeOptions,
|
|
10
|
+
withMinimalBack,
|
|
11
|
+
} from '../../components/display/platform_modal_header_buttons'
|
|
12
|
+
|
|
13
|
+
export const SearchScreenOptions = ({
|
|
14
|
+
navigation,
|
|
15
|
+
}: NativeStackScreenProps<ParamListBase>): NativeStackNavigationOptions =>
|
|
16
|
+
composeOptions(
|
|
17
|
+
{ title: 'Search', animation: 'fade' },
|
|
18
|
+
withMinimalBack({ onPress: () => navigation.goBack() })
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
export function SearchScreen() {
|
|
22
|
+
return <View style={styles.container} testID="search-screen" />
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const styles = StyleSheet.create({
|
|
26
|
+
container: {
|
|
27
|
+
flex: 1,
|
|
28
|
+
},
|
|
29
|
+
})
|
|
@@ -46,3 +46,12 @@ export interface ConversationResource {
|
|
|
46
46
|
unreadReaction: boolean
|
|
47
47
|
updatedAt: string
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
export type ConversationAvatarData = Pick<
|
|
51
|
+
ConversationResource,
|
|
52
|
+
| 'customAvatarType'
|
|
53
|
+
| 'customAvatarKey'
|
|
54
|
+
| 'customAvatarColor'
|
|
55
|
+
| 'customAvatarImageUrl'
|
|
56
|
+
| 'previewAvatarUrls'
|
|
57
|
+
>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ConversationAvatarData } from './conversation'
|
|
2
|
+
|
|
3
|
+
interface BaseSearchResultResource {
|
|
4
|
+
id: string
|
|
5
|
+
title: string
|
|
6
|
+
url: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SnippetSegment {
|
|
10
|
+
text: string
|
|
11
|
+
match: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ConversationSearchResultResource extends BaseSearchResultResource {
|
|
15
|
+
type: 'conversation'
|
|
16
|
+
extra: {
|
|
17
|
+
avatar: ConversationAvatarData
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MessageSearchResultResource extends BaseSearchResultResource {
|
|
22
|
+
type: 'message'
|
|
23
|
+
extra: {
|
|
24
|
+
conversationId: number
|
|
25
|
+
authorName: string
|
|
26
|
+
snippet: SnippetSegment[]
|
|
27
|
+
createdAt: string
|
|
28
|
+
avatar: ConversationAvatarData
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type SearchResultResource = ConversationSearchResultResource | MessageSearchResultResource
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSearchResultsQueryKey,
|
|
3
|
+
getSearchResultsRequestArgs,
|
|
4
|
+
} from '../request/get_search_results'
|
|
5
|
+
|
|
6
|
+
describe('getSearchResultsRequestArgs', () => {
|
|
7
|
+
it('requests the first page of search results for the term', () => {
|
|
8
|
+
expect(getSearchResultsRequestArgs('potluck')).toEqual({
|
|
9
|
+
url: '/search_results',
|
|
10
|
+
data: {
|
|
11
|
+
q: 'potluck',
|
|
12
|
+
perPage: 20,
|
|
13
|
+
fields: { SearchResult: ['type', 'title', 'url', 'extra'] },
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('getSearchResultsQueryKey', () => {
|
|
20
|
+
it('changes with the term', () => {
|
|
21
|
+
expect(getSearchResultsQueryKey('potluck')).not.toEqual(getSearchResultsQueryKey('picnic'))
|
|
22
|
+
expect(getSearchResultsQueryKey('potluck')).toEqual(getSearchResultsQueryKey('potluck'))
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getRequestQueryKey } from '../../hooks/use_suspense_api'
|
|
2
|
+
|
|
3
|
+
export const SEARCH_RESULTS_PAGE_SIZE = 20
|
|
4
|
+
|
|
5
|
+
export const getSearchResultsRequestArgs = (term: string) => {
|
|
6
|
+
const url = '/search_results'
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
url,
|
|
10
|
+
data: {
|
|
11
|
+
q: term,
|
|
12
|
+
perPage: SEARCH_RESULTS_PAGE_SIZE,
|
|
13
|
+
fields: {
|
|
14
|
+
SearchResult: ['type', 'title', 'url', 'extra'],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const getSearchResultsQueryKey = (term: string) => {
|
|
21
|
+
const requestArgs = getSearchResultsRequestArgs(term)
|
|
22
|
+
return getRequestQueryKey(requestArgs)
|
|
23
|
+
}
|