@planningcenter/chat-react-native 1.7.0-rc.1 → 2.0.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.
Files changed (32) hide show
  1. package/build/index.d.ts +4 -6
  2. package/build/index.d.ts.map +1 -1
  3. package/build/index.js +4 -6
  4. package/build/index.js.map +1 -1
  5. package/build/navigation/index.d.ts.map +1 -1
  6. package/build/screens/message_actions_screen.d.ts.map +1 -1
  7. package/build/screens/message_actions_screen.js +41 -21
  8. package/build/screens/message_actions_screen.js.map +1 -1
  9. package/build/utils/index.d.ts +1 -0
  10. package/build/utils/index.d.ts.map +1 -1
  11. package/build/utils/index.js +1 -0
  12. package/build/utils/index.js.map +1 -1
  13. package/build/utils/native_adapters/clipboard.d.ts +6 -0
  14. package/build/utils/native_adapters/clipboard.d.ts.map +1 -0
  15. package/build/utils/native_adapters/clipboard.js +9 -0
  16. package/build/utils/native_adapters/clipboard.js.map +1 -0
  17. package/build/utils/native_adapters/configuration.d.ts +10 -0
  18. package/build/utils/native_adapters/configuration.d.ts.map +1 -0
  19. package/build/utils/native_adapters/configuration.js +18 -0
  20. package/build/utils/native_adapters/configuration.js.map +1 -0
  21. package/build/utils/native_adapters/index.d.ts +3 -0
  22. package/build/utils/native_adapters/index.d.ts.map +1 -0
  23. package/build/utils/native_adapters/index.js +3 -0
  24. package/build/utils/native_adapters/index.js.map +1 -0
  25. package/package.json +2 -2
  26. package/src/__tests__/utils/native_adapters/configuration.ts +21 -0
  27. package/src/index.tsx +10 -6
  28. package/src/screens/message_actions_screen.tsx +70 -41
  29. package/src/utils/index.ts +1 -0
  30. package/src/utils/native_adapters/clipboard.ts +9 -0
  31. package/src/utils/native_adapters/configuration.ts +25 -0
  32. package/src/utils/native_adapters/index.ts +2 -0
package/build/index.d.ts CHANGED
@@ -1,7 +1,5 @@
1
- export * from './components';
2
- export * from './contexts';
3
- export * from './hooks';
4
- export * from './screens';
5
- export * from './utils';
6
- export * from './navigation';
1
+ export { ChatProvider, ChatContext, CreateChatThemeProps } from './contexts';
2
+ export { DesignSystemScreen } from './screens';
3
+ export { ChatStack } from './navigation';
4
+ export { TemporaryDefaultColorsType, ChatAdapters, ClipboardAdapter, Session, Client, } from './utils';
7
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EACL,0BAA0B,EAC1B,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,MAAM,GACP,MAAM,SAAS,CAAA"}
package/build/index.js CHANGED
@@ -1,7 +1,5 @@
1
- export * from './components';
2
- export * from './contexts';
3
- export * from './hooks';
4
- export * from './screens';
5
- export * from './utils';
6
- export * from './navigation';
1
+ export { ChatProvider, ChatContext } from './contexts';
2
+ export { DesignSystemScreen } from './screens';
3
+ export { ChatStack } from './navigation';
4
+ export { ChatAdapters, ClipboardAdapter, Session, Client, } from './utils';
7
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA","sourcesContent":["export * from './components'\nexport * from './contexts'\nexport * from './hooks'\nexport * from './screens'\nexport * from './utils'\nexport * from './navigation'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAwB,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAEL,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,MAAM,GACP,MAAM,SAAS,CAAA","sourcesContent":["export { ChatProvider, ChatContext, CreateChatThemeProps } from './contexts'\nexport { DesignSystemScreen } from './screens'\nexport { ChatStack } from './navigation'\nexport {\n TemporaryDefaultColorsType,\n ChatAdapters,\n ClipboardAdapter,\n Session,\n Client,\n} from './utils'\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigation/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAGnE,OAAO,EAAE,oBAAoB,EAAsB,MAAM,mCAAmC,CAAA;AAE5F,eAAO,MAAM,SAAS;;;;;;;;;uOAFb,mBAAkB;;;;;;;;;uBAiDq3gB,gBAAiB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;EATp6gB,CAAA;AAEF,KAAK,kBAAkB,GAAG,eAAe,CAAC,OAAO,SAAS,CAAC,CAAA;AAE3D,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,eAAe,CAAC;QACxB,UAAU,aAAc,SAAQ,kBAAkB;SAAG;KACtD;CACF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigation/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAGnE,OAAO,EAAE,oBAAoB,EAAsB,MAAM,mCAAmC,CAAA;AAE5F,eAAO,MAAM,SAAS;;;;;;;;;uOAFb,mBAAkB;;;;;;;;;uBAiDyqhB,gBAAiB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;EATxthB,CAAA;AAEF,KAAK,kBAAkB,GAAG,eAAe,CAAC,OAAO,SAAS,CAAC,CAAA;AAE3D,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,eAAe,CAAC;QACxB,UAAU,aAAc,SAAQ,kBAAkB;SAAG;KACtD;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"message_actions_screen.d.ts","sourceRoot":"","sources":["../../src/screens/message_actions_screen.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAiB,MAAM,0BAA0B,CAAA;AAC3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAA;AAE7E,OAAO,KAAqB,MAAM,OAAO,CAAA;AAYzC,eAAO,MAAM,kBAAkB,EAAE,4BAKhC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;CACxB,CAAC,CAAA;AAEF,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,EAAE,mBAAmB,qBAqFlE"}
1
+ {"version":3,"file":"message_actions_screen.d.ts","sourceRoot":"","sources":["../../src/screens/message_actions_screen.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAiB,MAAM,0BAA0B,CAAA;AAC3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAA;AAE7E,OAAO,KAAkC,MAAM,OAAO,CAAA;AAatD,eAAO,MAAM,kBAAkB,EAAE,4BAKhC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;CACxB,CAAC,CAAA;AAEF,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,EAAE,mBAAmB,qBAiHlE"}
@@ -1,8 +1,8 @@
1
1
  import { PlatformPressable } from '@react-navigation/elements';
2
2
  import { useNavigation } from '@react-navigation/native';
3
3
  import { useMutation, useQueryClient } from '@tanstack/react-query';
4
- import React, { useContext } from 'react';
5
- import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
4
+ import React, { useCallback, useContext } from 'react';
5
+ import { Alert, Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
6
6
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
7
  import { Text, TextButton } from '../components';
8
8
  import { REACTION_EMOJIS, useReactionStyles } from '../components/conversation/message_reaction';
@@ -10,6 +10,7 @@ import { ChatContext } from '../contexts';
10
10
  import { useTheme } from '../hooks';
11
11
  import { getMessagesRequestArgs, useConversationMessages } from '../hooks/use_conversation_messages';
12
12
  import { updateRecordInPagesData } from '../utils';
13
+ import { Clipboard } from '../utils/native_adapters';
13
14
  export const ReactScreenOptions = {
14
15
  presentation: 'formSheet',
15
16
  headerShown: false,
@@ -22,14 +23,26 @@ export function MessageActionsScreen({ route }) {
22
23
  const { client } = useContext(ChatContext);
23
24
  const queryClient = useQueryClient();
24
25
  const styles = useStyles();
25
- const { messages, queryKey } = useConversationMessages({ conversation_id }, { refetchOnMount: false });
26
+ const { messages, queryKey, refetch } = useConversationMessages({ conversation_id }, { refetchOnMount: false });
26
27
  const message = messages.find(m => m.id === message_id);
27
- const reactionValues = message?.reactionCounts.filter(reaction => reaction.mine).map(reaction => reaction.value) || [];
28
- const handleReactionPress = (value) => {
29
- const present = reactionValues.includes(value);
28
+ const myReactions = message?.reactionCounts
29
+ .filter(reaction => reaction.mine)
30
+ .map(reaction => reaction.value);
31
+ const availableReactions = Object.entries(REACTION_EMOJIS).map(([value, emoji]) => {
32
+ return {
33
+ value: value,
34
+ emoji,
35
+ mine: myReactions?.includes(value),
36
+ };
37
+ });
38
+ const handleCopyPress = (text) => {
39
+ Clipboard.setStringAsync(text || '');
40
+ navigation.goBack();
41
+ };
42
+ const handleReactionPress = useCallback(({ value, mine }) => {
30
43
  const requestParams = getMessagesRequestArgs({ conversation_id });
31
44
  // Value has already been updated
32
- const endpoint = !present ? 'react' : 'unreact';
45
+ const endpoint = !mine ? 'react' : 'unreact';
33
46
  const url = `/me/conversations/${conversation_id}/messages/${message_id}/${endpoint}`;
34
47
  const fieldsWithValueJoined = Object.fromEntries(Object.entries(requestParams.data.fields).map(([k, v]) => [k, v.join(',')]));
35
48
  return client.post({
@@ -43,8 +56,12 @@ export function MessageActionsScreen({ route }) {
43
56
  fields: fieldsWithValueJoined,
44
57
  },
45
58
  });
46
- };
47
- const { mutate, isPending } = useMutation({
59
+ }, [client, conversation_id, message_id]);
60
+ const deleteMessage = useCallback(() => {
61
+ const url = `/me/conversations/${conversation_id}/messages/${message_id}/`;
62
+ return client.delete({ url });
63
+ }, [client, conversation_id, message_id]);
64
+ const { mutate: handleReaction, isPending } = useMutation({
48
65
  mutationFn: handleReactionPress,
49
66
  onSuccess: (result) => {
50
67
  const updatedMessage = result.data;
@@ -54,25 +71,28 @@ export function MessageActionsScreen({ route }) {
54
71
  }));
55
72
  navigation.goBack();
56
73
  },
74
+ onError: () => {
75
+ Alert.alert('Oops', 'We were unable to react to this message. Please try again.');
76
+ },
57
77
  });
58
- const myReactions = message?.reactionCounts
59
- .filter(reaction => reaction.mine)
60
- .map(reaction => reaction.value);
61
- const availableReactions = Object.entries(REACTION_EMOJIS).map(([value, emoji]) => {
62
- return {
63
- value: value,
64
- emoji,
65
- mine: myReactions?.includes(value),
66
- };
78
+ const { mutate: handleDeleteMessage } = useMutation({
79
+ mutationFn: deleteMessage,
80
+ onSuccess: () => {
81
+ refetch();
82
+ navigation.goBack();
83
+ },
84
+ onError: () => {
85
+ Alert.alert('Oops', 'We were unable to delete this message. Please try again.');
86
+ },
67
87
  });
68
88
  return (<View style={styles.container}>
69
89
  <View style={styles.reactionList}>
70
- {availableReactions.map((reaction, index) => (<Reaction key={index} reaction={reaction} onPress={() => mutate(reaction.value)}/>))}
90
+ {availableReactions.map((reaction, index) => (<Reaction key={index} reaction={reaction} onPress={() => handleReaction(reaction)}/>))}
71
91
  </View>
72
92
  <View style={styles.actions}>
73
93
  <View style={styles.actionButton}>
74
- <TextButton onPress={() => navigation.goBack()}>Copy</TextButton>
75
- <TextButton appearance="danger" onPress={() => navigation.goBack()} disabled={isPending}>
94
+ <TextButton onPress={() => handleCopyPress(message?.text)}>Copy</TextButton>
95
+ <TextButton appearance="danger" onPress={() => handleDeleteMessage()} disabled={isPending}>
76
96
  Delete
77
97
  </TextButton>
78
98
  </View>
@@ -1 +1 @@
1
- {"version":3,"file":"message_actions_screen.js","sourceRoot":"","sources":["../../src/screens/message_actions_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport { StaticScreenProps, useNavigation } from '@react-navigation/native'\nimport { NativeStackNavigationOptions } from '@react-navigation/native-stack'\nimport { InfiniteData, useMutation, useQueryClient } from '@tanstack/react-query'\nimport React, { useContext } from 'react'\nimport { Platform, StyleSheet, useWindowDimensions, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Text, TextButton } from '../components'\nimport { REACTION_EMOJIS, useReactionStyles } from '../components/conversation/message_reaction'\nimport { ChatContext } from '../contexts'\nimport { useTheme } from '../hooks'\nimport { getMessagesRequestArgs, useConversationMessages } from '../hooks/use_conversation_messages'\nimport { ApiCollection, ApiResource, MessageResource } from '../types'\nimport { ReactionCountResource } from '../types/resources/reaction'\nimport { updateRecordInPagesData } from '../utils'\n\nexport const ReactScreenOptions: NativeStackNavigationOptions = {\n presentation: 'formSheet',\n headerShown: false,\n sheetAllowedDetents: [0.25],\n sheetGrabberVisible: true,\n}\n\nexport type ReactionScreenProps = StaticScreenProps<{\n message_id: string\n conversation_id: string\n}>\n\nexport function MessageActionsScreen({ route }: ReactionScreenProps) {\n const navigation = useNavigation()\n const { conversation_id, message_id } = route.params\n\n const { client } = useContext(ChatContext)\n const queryClient = useQueryClient()\n const styles = useStyles()\n\n const { messages, queryKey } = useConversationMessages(\n { conversation_id },\n { refetchOnMount: false }\n )\n const message = messages.find(m => m.id === message_id)\n const reactionValues =\n message?.reactionCounts.filter(reaction => reaction.mine).map(reaction => reaction.value) || []\n\n const handleReactionPress = (value: keyof typeof REACTION_EMOJIS) => {\n const present = reactionValues.includes(value)\n const requestParams = getMessagesRequestArgs({ conversation_id })\n\n // Value has already been updated\n const endpoint = !present ? 'react' : 'unreact'\n const url = `/me/conversations/${conversation_id}/messages/${message_id}/${endpoint}`\n const fieldsWithValueJoined = Object.fromEntries(\n Object.entries(requestParams.data.fields).map(([k, v]) => [k, v.join(',')])\n )\n\n return client.post({\n url,\n data: {\n ...requestParams.data,\n data: {\n type: 'Message',\n attributes: { value: value },\n },\n fields: fieldsWithValueJoined,\n },\n })\n }\n\n const { mutate, isPending } = useMutation({\n mutationFn: handleReactionPress,\n onSuccess: (result: ApiResource<MessageResource>) => {\n const updatedMessage = result.data\n type QueryData = InfiniteData<ApiCollection<MessageResource>>\n\n queryClient.setQueryData<QueryData>(queryKey, data =>\n updateRecordInPagesData({\n data,\n record: updatedMessage,\n })\n )\n navigation.goBack()\n },\n })\n\n const myReactions = message?.reactionCounts\n .filter(reaction => reaction.mine)\n .map(reaction => reaction.value)\n\n const availableReactions = Object.entries(REACTION_EMOJIS).map(([value, emoji]) => {\n return {\n value: value as ReactionCountResource['value'],\n emoji,\n mine: myReactions?.includes(value as ReactionCountResource['value']),\n }\n })\n\n return (\n <View style={styles.container}>\n <View style={styles.reactionList}>\n {availableReactions.map((reaction, index) => (\n <Reaction key={index} reaction={reaction} onPress={() => mutate(reaction.value)} />\n ))}\n </View>\n <View style={styles.actions}>\n <View style={styles.actionButton}>\n <TextButton onPress={() => navigation.goBack()}>Copy</TextButton>\n <TextButton appearance=\"danger\" onPress={() => navigation.goBack()} disabled={isPending}>\n Delete\n </TextButton>\n </View>\n </View>\n </View>\n )\n}\n\nconst Reaction = ({\n reaction,\n onPress,\n}: {\n reaction: { value: ReactionCountResource['value']; emoji: string; mine: boolean | undefined }\n onPress: () => void\n}) => {\n const styles = useStyles()\n const reactionStyles = useReactionStyles({ mine: reaction.mine ? 1 : 0 })\n\n return (\n <PlatformPressable\n key={reaction.value}\n style={[reactionStyles.reaction, styles.reaction]}\n onPress={onPress}\n >\n <Text style={reactionStyles.reactionEmoji}>{REACTION_EMOJIS[reaction.value]}</Text>\n </PlatformPressable>\n )\n}\n\nconst useStyles = () => {\n const theme = useTheme()\n const { height } = useWindowDimensions()\n const { bottom } = useSafeAreaInsets()\n const btnBorderWidth = 1\n const baseSize = 44\n const reactionBtnSize = Platform.select({\n ios: baseSize,\n android: baseSize + btnBorderWidth * 2,\n })\n\n return StyleSheet.create({\n container: {\n justifyContent: 'flex-start',\n paddingTop: 12,\n paddingBottom: bottom,\n width: '100%',\n backgroundColor: theme.colors.fillColorNeutral100Inverted,\n height,\n },\n reaction: {\n height: reactionBtnSize,\n width: reactionBtnSize,\n borderWidth: btnBorderWidth,\n borderRadius: 32,\n justifyContent: 'center',\n },\n reactionList: {\n justifyContent: 'center',\n gap: 24,\n paddingVertical: 12,\n flexDirection: 'row',\n borderBottomColor: theme.colors.fillColorNeutral040,\n borderBottomWidth: 1,\n },\n actions: { flex: 1 },\n actionButton: { padding: 12, paddingBottom: 100, gap: 12 },\n })\n}\n"]}
1
+ {"version":3,"file":"message_actions_screen.js","sourceRoot":"","sources":["../../src/screens/message_actions_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport { StaticScreenProps, useNavigation } from '@react-navigation/native'\nimport { NativeStackNavigationOptions } from '@react-navigation/native-stack'\nimport { InfiniteData, useMutation, useQueryClient } from '@tanstack/react-query'\nimport React, { useCallback, useContext } from 'react'\nimport { Alert, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Text, TextButton } from '../components'\nimport { REACTION_EMOJIS, useReactionStyles } from '../components/conversation/message_reaction'\nimport { ChatContext } from '../contexts'\nimport { useTheme } from '../hooks'\nimport { getMessagesRequestArgs, useConversationMessages } from '../hooks/use_conversation_messages'\nimport { ApiCollection, ApiResource, MessageResource } from '../types'\nimport { ReactionCountResource } from '../types/resources/reaction'\nimport { updateRecordInPagesData } from '../utils'\nimport { Clipboard } from '../utils/native_adapters'\n\nexport const ReactScreenOptions: NativeStackNavigationOptions = {\n presentation: 'formSheet',\n headerShown: false,\n sheetAllowedDetents: [0.25],\n sheetGrabberVisible: true,\n}\n\nexport type ReactionScreenProps = StaticScreenProps<{\n message_id: string\n conversation_id: string\n}>\n\nexport function MessageActionsScreen({ route }: ReactionScreenProps) {\n const navigation = useNavigation()\n const { conversation_id, message_id } = route.params\n\n const { client } = useContext(ChatContext)\n const queryClient = useQueryClient()\n const styles = useStyles()\n\n const { messages, queryKey, refetch } = useConversationMessages(\n { conversation_id },\n { refetchOnMount: false }\n )\n const message = messages.find(m => m.id === message_id)\n const myReactions = message?.reactionCounts\n .filter(reaction => reaction.mine)\n .map(reaction => reaction.value)\n\n const availableReactions = Object.entries(REACTION_EMOJIS).map(([value, emoji]) => {\n return {\n value: value as ReactionCountResource['value'],\n emoji,\n mine: myReactions?.includes(value as ReactionCountResource['value']),\n }\n })\n\n const handleCopyPress = (text?: string) => {\n Clipboard.setStringAsync(text || '')\n navigation.goBack()\n }\n\n const handleReactionPress = useCallback(\n ({ value, mine }: { value: keyof typeof REACTION_EMOJIS; mine?: boolean }) => {\n const requestParams = getMessagesRequestArgs({ conversation_id })\n\n // Value has already been updated\n const endpoint = !mine ? 'react' : 'unreact'\n const url = `/me/conversations/${conversation_id}/messages/${message_id}/${endpoint}`\n const fieldsWithValueJoined = Object.fromEntries(\n Object.entries(requestParams.data.fields).map(([k, v]) => [k, v.join(',')])\n )\n\n return client.post({\n url,\n data: {\n ...requestParams.data,\n data: {\n type: 'Message',\n attributes: { value: value },\n },\n fields: fieldsWithValueJoined,\n },\n })\n },\n [client, conversation_id, message_id]\n )\n\n const deleteMessage = useCallback(() => {\n const url = `/me/conversations/${conversation_id}/messages/${message_id}/`\n\n return client.delete({ url })\n }, [client, conversation_id, message_id])\n\n const { mutate: handleReaction, isPending } = useMutation({\n mutationFn: handleReactionPress,\n onSuccess: (result: ApiResource<MessageResource>) => {\n const updatedMessage = result.data\n type QueryData = InfiniteData<ApiCollection<MessageResource>>\n\n queryClient.setQueryData<QueryData>(queryKey, data =>\n updateRecordInPagesData({\n data,\n record: updatedMessage,\n })\n )\n navigation.goBack()\n },\n onError: () => {\n Alert.alert('Oops', 'We were unable to react to this message. Please try again.')\n },\n })\n\n const { mutate: handleDeleteMessage } = useMutation({\n mutationFn: deleteMessage,\n onSuccess: () => {\n refetch()\n navigation.goBack()\n },\n onError: () => {\n Alert.alert('Oops', 'We were unable to delete this message. Please try again.')\n },\n })\n\n return (\n <View style={styles.container}>\n <View style={styles.reactionList}>\n {availableReactions.map((reaction, index) => (\n <Reaction key={index} reaction={reaction} onPress={() => handleReaction(reaction)} />\n ))}\n </View>\n <View style={styles.actions}>\n <View style={styles.actionButton}>\n <TextButton onPress={() => handleCopyPress(message?.text)}>Copy</TextButton>\n <TextButton\n appearance=\"danger\"\n onPress={() => handleDeleteMessage()}\n disabled={isPending}\n >\n Delete\n </TextButton>\n </View>\n </View>\n </View>\n )\n}\n\nconst Reaction = ({\n reaction,\n onPress,\n}: {\n reaction: { value: ReactionCountResource['value']; emoji: string; mine: boolean | undefined }\n onPress: () => void\n}) => {\n const styles = useStyles()\n const reactionStyles = useReactionStyles({ mine: reaction.mine ? 1 : 0 })\n\n return (\n <PlatformPressable\n key={reaction.value}\n style={[reactionStyles.reaction, styles.reaction]}\n onPress={onPress}\n >\n <Text style={reactionStyles.reactionEmoji}>{REACTION_EMOJIS[reaction.value]}</Text>\n </PlatformPressable>\n )\n}\n\nconst useStyles = () => {\n const theme = useTheme()\n const { height } = useWindowDimensions()\n const { bottom } = useSafeAreaInsets()\n const btnBorderWidth = 1\n const baseSize = 44\n const reactionBtnSize = Platform.select({\n ios: baseSize,\n android: baseSize + btnBorderWidth * 2,\n })\n\n return StyleSheet.create({\n container: {\n justifyContent: 'flex-start',\n paddingTop: 12,\n paddingBottom: bottom,\n width: '100%',\n backgroundColor: theme.colors.fillColorNeutral100Inverted,\n height,\n },\n reaction: {\n height: reactionBtnSize,\n width: reactionBtnSize,\n borderWidth: btnBorderWidth,\n borderRadius: 32,\n justifyContent: 'center',\n },\n reactionList: {\n justifyContent: 'center',\n gap: 24,\n paddingVertical: 12,\n flexDirection: 'row',\n borderBottomColor: theme.colors.fillColorNeutral040,\n borderBottomWidth: 1,\n },\n actions: { flex: 1 },\n actionButton: { padding: 12, paddingBottom: 100, gap: 12 },\n })\n}\n"]}
@@ -5,4 +5,5 @@ export * from './space';
5
5
  export * from './client';
6
6
  export * from './uri';
7
7
  export * from './cache';
8
+ export * from './native_adapters';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,mBAAmB,CAAA"}
@@ -5,4 +5,5 @@ export * from './space';
5
5
  export * from './client';
6
6
  export * from './uri';
7
7
  export * from './cache';
8
+ export * from './native_adapters';
8
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA","sourcesContent":["export * from './session'\nexport * from './theme'\nexport * from './styles'\nexport * from './space'\nexport * from './client'\nexport * from './uri'\nexport * from './cache'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,mBAAmB,CAAA","sourcesContent":["export * from './session'\nexport * from './theme'\nexport * from './styles'\nexport * from './space'\nexport * from './client'\nexport * from './uri'\nexport * from './cache'\nexport * from './native_adapters'\n"]}
@@ -0,0 +1,6 @@
1
+ export declare class ClipboardAdapter {
2
+ getStringAsync: () => Promise<string>;
3
+ setStringAsync: (_: string) => Promise<void>;
4
+ constructor(methods: ClipboardAdapter);
5
+ }
6
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/clipboard.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAgB;IAC3B,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACrC,cAAc,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;gBAEhC,OAAO,EAAE,gBAAgB;CAItC"}
@@ -0,0 +1,9 @@
1
+ export class ClipboardAdapter {
2
+ getStringAsync;
3
+ setStringAsync;
4
+ constructor(methods) {
5
+ this.getStringAsync = methods.getStringAsync;
6
+ this.setStringAsync = methods.setStringAsync;
7
+ }
8
+ }
9
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/clipboard.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,gBAAgB;IAC3B,cAAc,CAAuB;IACrC,cAAc,CAA8B;IAE5C,YAAY,OAAyB;QACnC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAA;QAC5C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAA;IAC9C,CAAC;CACF","sourcesContent":["export class ClipboardAdapter {\n getStringAsync: () => Promise<string>\n setStringAsync: (_: string) => Promise<void>\n\n constructor(methods: ClipboardAdapter) {\n this.getStringAsync = methods.getStringAsync\n this.setStringAsync = methods.setStringAsync\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { ClipboardAdapter } from './clipboard';
2
+ type ChatConfigurations = {
3
+ clipboard: ClipboardAdapter;
4
+ };
5
+ export declare class ChatAdapters {
6
+ static configure(configurations: ChatConfigurations): void;
7
+ }
8
+ declare let Clipboard: ClipboardAdapter;
9
+ export { Clipboard };
10
+ //# sourceMappingURL=configuration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9C,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,gBAAgB,CAAA;CAC5B,CAAA;AAED,qBAAa,YAAY;IACvB,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB;CAGpD;AAMD,QAAA,IAAI,SAAS,EAAE,gBAMb,CAAA;AAEF,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -0,0 +1,18 @@
1
+ import { ClipboardAdapter } from './clipboard';
2
+ export class ChatAdapters {
3
+ static configure(configurations) {
4
+ Clipboard = configurations.clipboard;
5
+ }
6
+ }
7
+ const methodMissing = () => {
8
+ console.warn('ChatAdapters.configure() must be called before using any adapters');
9
+ };
10
+ let Clipboard = new ClipboardAdapter({
11
+ getStringAsync: async () => {
12
+ methodMissing();
13
+ return '';
14
+ },
15
+ setStringAsync: async (_) => methodMissing(),
16
+ });
17
+ export { Clipboard };
18
+ //# sourceMappingURL=configuration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAM9C,MAAM,OAAO,YAAY;IACvB,MAAM,CAAC,SAAS,CAAC,cAAkC;QACjD,SAAS,GAAG,cAAc,CAAC,SAAS,CAAA;IACtC,CAAC;CACF;AAED,MAAM,aAAa,GAAG,GAAG,EAAE;IACzB,OAAO,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAA;AACnF,CAAC,CAAA;AAED,IAAI,SAAS,GAAqB,IAAI,gBAAgB,CAAC;IACrD,cAAc,EAAE,KAAK,IAAI,EAAE;QACzB,aAAa,EAAE,CAAA;QACf,OAAO,EAAE,CAAA;IACX,CAAC;IACD,cAAc,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,CAAC,aAAa,EAAE;CACrD,CAAC,CAAA;AAEF,OAAO,EAAE,SAAS,EAAE,CAAA","sourcesContent":["import { ClipboardAdapter } from './clipboard'\n\ntype ChatConfigurations = {\n clipboard: ClipboardAdapter\n}\n\nexport class ChatAdapters {\n static configure(configurations: ChatConfigurations) {\n Clipboard = configurations.clipboard\n }\n}\n\nconst methodMissing = () => {\n console.warn('ChatAdapters.configure() must be called before using any adapters')\n}\n\nlet Clipboard: ClipboardAdapter = new ClipboardAdapter({\n getStringAsync: async () => {\n methodMissing()\n return ''\n },\n setStringAsync: async (_: string) => methodMissing(),\n})\n\nexport { Clipboard }\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './clipboard';
2
+ export * from './configuration';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './clipboard';
2
+ export * from './configuration';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA","sourcesContent":["export * from './clipboard'\nexport * from './configuration'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "1.7.0-rc.1",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -47,5 +47,5 @@
47
47
  "prettier": "^3.4.2",
48
48
  "typescript": "<5.6.0"
49
49
  },
50
- "gitHead": "dbf0a4193a5961fba55629f2f90c475e1310a68b"
50
+ "gitHead": "cdb7edc65761b084f41cee5fa84d10f8f341b5d6"
51
51
  }
@@ -0,0 +1,21 @@
1
+ import { ChatAdapters } from '../../../utils/native_adapters/configuration'
2
+ import { Clipboard, ClipboardAdapter } from '../../../utils/native_adapters'
3
+
4
+ describe('ChatAdapters', () => {
5
+ const getStringAsync = jest.fn()
6
+ const setStringAsync = jest.fn()
7
+ const clipboard = new ClipboardAdapter({
8
+ getStringAsync,
9
+ setStringAsync,
10
+ })
11
+
12
+ it('should be defined', () => {
13
+ expect(ChatAdapters).toBeDefined()
14
+ })
15
+
16
+ it('should configure the clipboard', () => {
17
+ ChatAdapters.configure({ clipboard })
18
+
19
+ expect(Clipboard).toEqual(clipboard)
20
+ })
21
+ })
package/src/index.tsx CHANGED
@@ -1,6 +1,10 @@
1
- export * from './components'
2
- export * from './contexts'
3
- export * from './hooks'
4
- export * from './screens'
5
- export * from './utils'
6
- export * from './navigation'
1
+ export { ChatProvider, ChatContext, CreateChatThemeProps } from './contexts'
2
+ export { DesignSystemScreen } from './screens'
3
+ export { ChatStack } from './navigation'
4
+ export {
5
+ TemporaryDefaultColorsType,
6
+ ChatAdapters,
7
+ ClipboardAdapter,
8
+ Session,
9
+ Client,
10
+ } from './utils'
@@ -2,8 +2,8 @@ import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
3
3
  import { NativeStackNavigationOptions } from '@react-navigation/native-stack'
4
4
  import { InfiniteData, useMutation, useQueryClient } from '@tanstack/react-query'
5
- import React, { useContext } from 'react'
6
- import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native'
5
+ import React, { useCallback, useContext } from 'react'
6
+ import { Alert, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'
7
7
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
8
8
  import { Text, TextButton } from '../components'
9
9
  import { REACTION_EMOJIS, useReactionStyles } from '../components/conversation/message_reaction'
@@ -13,6 +13,7 @@ import { getMessagesRequestArgs, useConversationMessages } from '../hooks/use_co
13
13
  import { ApiCollection, ApiResource, MessageResource } from '../types'
14
14
  import { ReactionCountResource } from '../types/resources/reaction'
15
15
  import { updateRecordInPagesData } from '../utils'
16
+ import { Clipboard } from '../utils/native_adapters'
16
17
 
17
18
  export const ReactScreenOptions: NativeStackNavigationOptions = {
18
19
  presentation: 'formSheet',
@@ -34,39 +35,61 @@ export function MessageActionsScreen({ route }: ReactionScreenProps) {
34
35
  const queryClient = useQueryClient()
35
36
  const styles = useStyles()
36
37
 
37
- const { messages, queryKey } = useConversationMessages(
38
+ const { messages, queryKey, refetch } = useConversationMessages(
38
39
  { conversation_id },
39
40
  { refetchOnMount: false }
40
41
  )
41
42
  const message = messages.find(m => m.id === message_id)
42
- const reactionValues =
43
- message?.reactionCounts.filter(reaction => reaction.mine).map(reaction => reaction.value) || []
44
-
45
- const handleReactionPress = (value: keyof typeof REACTION_EMOJIS) => {
46
- const present = reactionValues.includes(value)
47
- const requestParams = getMessagesRequestArgs({ conversation_id })
48
-
49
- // Value has already been updated
50
- const endpoint = !present ? 'react' : 'unreact'
51
- const url = `/me/conversations/${conversation_id}/messages/${message_id}/${endpoint}`
52
- const fieldsWithValueJoined = Object.fromEntries(
53
- Object.entries(requestParams.data.fields).map(([k, v]) => [k, v.join(',')])
54
- )
55
-
56
- return client.post({
57
- url,
58
- data: {
59
- ...requestParams.data,
43
+ const myReactions = message?.reactionCounts
44
+ .filter(reaction => reaction.mine)
45
+ .map(reaction => reaction.value)
46
+
47
+ const availableReactions = Object.entries(REACTION_EMOJIS).map(([value, emoji]) => {
48
+ return {
49
+ value: value as ReactionCountResource['value'],
50
+ emoji,
51
+ mine: myReactions?.includes(value as ReactionCountResource['value']),
52
+ }
53
+ })
54
+
55
+ const handleCopyPress = (text?: string) => {
56
+ Clipboard.setStringAsync(text || '')
57
+ navigation.goBack()
58
+ }
59
+
60
+ const handleReactionPress = useCallback(
61
+ ({ value, mine }: { value: keyof typeof REACTION_EMOJIS; mine?: boolean }) => {
62
+ const requestParams = getMessagesRequestArgs({ conversation_id })
63
+
64
+ // Value has already been updated
65
+ const endpoint = !mine ? 'react' : 'unreact'
66
+ const url = `/me/conversations/${conversation_id}/messages/${message_id}/${endpoint}`
67
+ const fieldsWithValueJoined = Object.fromEntries(
68
+ Object.entries(requestParams.data.fields).map(([k, v]) => [k, v.join(',')])
69
+ )
70
+
71
+ return client.post({
72
+ url,
60
73
  data: {
61
- type: 'Message',
62
- attributes: { value: value },
74
+ ...requestParams.data,
75
+ data: {
76
+ type: 'Message',
77
+ attributes: { value: value },
78
+ },
79
+ fields: fieldsWithValueJoined,
63
80
  },
64
- fields: fieldsWithValueJoined,
65
- },
66
- })
67
- }
81
+ })
82
+ },
83
+ [client, conversation_id, message_id]
84
+ )
85
+
86
+ const deleteMessage = useCallback(() => {
87
+ const url = `/me/conversations/${conversation_id}/messages/${message_id}/`
68
88
 
69
- const { mutate, isPending } = useMutation({
89
+ return client.delete({ url })
90
+ }, [client, conversation_id, message_id])
91
+
92
+ const { mutate: handleReaction, isPending } = useMutation({
70
93
  mutationFn: handleReactionPress,
71
94
  onSuccess: (result: ApiResource<MessageResource>) => {
72
95
  const updatedMessage = result.data
@@ -80,31 +103,37 @@ export function MessageActionsScreen({ route }: ReactionScreenProps) {
80
103
  )
81
104
  navigation.goBack()
82
105
  },
106
+ onError: () => {
107
+ Alert.alert('Oops', 'We were unable to react to this message. Please try again.')
108
+ },
83
109
  })
84
110
 
85
- const myReactions = message?.reactionCounts
86
- .filter(reaction => reaction.mine)
87
- .map(reaction => reaction.value)
88
-
89
- const availableReactions = Object.entries(REACTION_EMOJIS).map(([value, emoji]) => {
90
- return {
91
- value: value as ReactionCountResource['value'],
92
- emoji,
93
- mine: myReactions?.includes(value as ReactionCountResource['value']),
94
- }
111
+ const { mutate: handleDeleteMessage } = useMutation({
112
+ mutationFn: deleteMessage,
113
+ onSuccess: () => {
114
+ refetch()
115
+ navigation.goBack()
116
+ },
117
+ onError: () => {
118
+ Alert.alert('Oops', 'We were unable to delete this message. Please try again.')
119
+ },
95
120
  })
96
121
 
97
122
  return (
98
123
  <View style={styles.container}>
99
124
  <View style={styles.reactionList}>
100
125
  {availableReactions.map((reaction, index) => (
101
- <Reaction key={index} reaction={reaction} onPress={() => mutate(reaction.value)} />
126
+ <Reaction key={index} reaction={reaction} onPress={() => handleReaction(reaction)} />
102
127
  ))}
103
128
  </View>
104
129
  <View style={styles.actions}>
105
130
  <View style={styles.actionButton}>
106
- <TextButton onPress={() => navigation.goBack()}>Copy</TextButton>
107
- <TextButton appearance="danger" onPress={() => navigation.goBack()} disabled={isPending}>
131
+ <TextButton onPress={() => handleCopyPress(message?.text)}>Copy</TextButton>
132
+ <TextButton
133
+ appearance="danger"
134
+ onPress={() => handleDeleteMessage()}
135
+ disabled={isPending}
136
+ >
108
137
  Delete
109
138
  </TextButton>
110
139
  </View>
@@ -5,3 +5,4 @@ export * from './space'
5
5
  export * from './client'
6
6
  export * from './uri'
7
7
  export * from './cache'
8
+ export * from './native_adapters'
@@ -0,0 +1,9 @@
1
+ export class ClipboardAdapter {
2
+ getStringAsync: () => Promise<string>
3
+ setStringAsync: (_: string) => Promise<void>
4
+
5
+ constructor(methods: ClipboardAdapter) {
6
+ this.getStringAsync = methods.getStringAsync
7
+ this.setStringAsync = methods.setStringAsync
8
+ }
9
+ }
@@ -0,0 +1,25 @@
1
+ import { ClipboardAdapter } from './clipboard'
2
+
3
+ type ChatConfigurations = {
4
+ clipboard: ClipboardAdapter
5
+ }
6
+
7
+ export class ChatAdapters {
8
+ static configure(configurations: ChatConfigurations) {
9
+ Clipboard = configurations.clipboard
10
+ }
11
+ }
12
+
13
+ const methodMissing = () => {
14
+ console.warn('ChatAdapters.configure() must be called before using any adapters')
15
+ }
16
+
17
+ let Clipboard: ClipboardAdapter = new ClipboardAdapter({
18
+ getStringAsync: async () => {
19
+ methodMissing()
20
+ return ''
21
+ },
22
+ setStringAsync: async (_: string) => methodMissing(),
23
+ })
24
+
25
+ export { Clipboard }
@@ -0,0 +1,2 @@
1
+ export * from './clipboard'
2
+ export * from './configuration'