@planningcenter/chat-react-native 3.2.0-rc.23 → 3.2.0-rc.25
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/hooks/groups/use_group_members_for_new_conversation.d.ts +10 -1
- package/build/hooks/groups/use_group_members_for_new_conversation.d.ts.map +1 -1
- package/build/hooks/groups/use_group_members_for_new_conversation.js +1 -0
- package/build/hooks/groups/use_group_members_for_new_conversation.js.map +1 -1
- package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
- package/build/screens/conversation_new/components/groups_form.js +21 -10
- package/build/screens/conversation_new/components/groups_form.js.map +1 -1
- package/build/screens/conversation_screen.d.ts +7 -0
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +76 -2
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/types/resources/message.d.ts +2 -0
- package/build/types/resources/message.d.ts.map +1 -1
- package/build/types/resources/message.js.map +1 -1
- package/build/utils/date.d.ts +5 -0
- package/build/utils/date.d.ts.map +1 -1
- package/build/utils/date.js +8 -1
- package/build/utils/date.js.map +1 -1
- package/package.json +2 -2
- package/src/hooks/groups/use_group_members_for_new_conversation.ts +13 -1
- package/src/screens/conversation_new/components/groups_form.tsx +33 -15
- package/src/screens/conversation_screen.tsx +92 -3
- package/src/types/resources/message.ts +2 -0
- package/src/utils/date.ts +11 -1
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { MemberResource } from '../../types';
|
|
1
|
+
import { MemberResource, ResourceObject } from '../../types';
|
|
2
2
|
import { GroupsGroupMemberResource } from '../../types/resources/groups/groups_member_resource_with_person';
|
|
3
|
+
import { useApiPaginator } from '../use_api';
|
|
4
|
+
type UseApiPaginatorResult<T extends ResourceObject> = ReturnType<typeof useApiPaginator<T>>;
|
|
5
|
+
export type GroupMembersForNewConversationResult = Omit<UseApiPaginatorResult<GroupsGroupMemberResource>, // We are passing our own "members" data shape
|
|
6
|
+
'data'> & {
|
|
7
|
+
data: MemberResource[];
|
|
8
|
+
adultMembers: MemberResource[];
|
|
9
|
+
childMembers: MemberResource[];
|
|
10
|
+
};
|
|
3
11
|
/**
|
|
4
12
|
* This is specifically for the new conversation screen because we assign
|
|
5
13
|
* the "Conversation owner" badge to the current person.
|
|
@@ -223,4 +231,5 @@ export declare function useGroupMembersForNewConversation({ id }: {
|
|
|
223
231
|
fetchStatus: import("@tanstack/query-core").FetchStatus;
|
|
224
232
|
promise: Promise<import("@tanstack/query-core").InfiniteData<import("../../types").ApiCollection<GroupsGroupMemberResource>, unknown>>;
|
|
225
233
|
};
|
|
234
|
+
export {};
|
|
226
235
|
//# sourceMappingURL=use_group_members_for_new_conversation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_group_members_for_new_conversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/groups/use_group_members_for_new_conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"use_group_members_for_new_conversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/groups/use_group_members_for_new_conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,iEAAiE,CAAA;AAC3G,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAG5C,KAAK,qBAAqB,CAAC,CAAC,SAAS,cAAc,IAAI,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAA;AAE5F,MAAM,MAAM,oCAAoC,GAAG,IAAI,CACrD,qBAAqB,CAAC,yBAAyB,CAAC,EAAE,8CAA8C;AAChG,MAAM,CACP,GAAG;IACF,IAAI,EAAE,cAAc,EAAE,CAAA;IACtB,YAAY,EAAE,cAAc,EAAE,CAAA;IAC9B,YAAY,EAAE,cAAc,EAAE,CAAA;CAC/B,CAAA;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_group_members_for_new_conversation.js","sourceRoot":"","sources":["../../../src/hooks/groups/use_group_members_for_new_conversation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"use_group_members_for_new_conversation.js","sourceRoot":"","sources":["../../../src/hooks/groups/use_group_members_for_new_conversation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAaxD;;;GAGG;AACH,MAAM,UAAU,iCAAiC,CAAC,EAAE,EAAE,EAAkB;IACtE,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAA;IACxC,MAAM,QAAQ,GAAG,eAAe,CAA4B;QAC1D,GAAG,EAAE,cAAc,EAAE,cAAc;QACnC,IAAI,EAAE;YACJ,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,CAAC,QAAQ,CAAC;YACnB,MAAM,EAAE;gBACN,UAAU,EAAE,CAAC,cAAc,CAAC;gBAC5B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,CAAC;aACnE;SACF;QACD,GAAG,EAAE,QAAQ;KACd,CAAC,CAAA;IAEF,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,EAAE,EAAE,GAAG,QAAQ,CAAA;IAC3C,MAAM,OAAO,GAAqB,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;QAC7D,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAA;QAC7B,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,MAAM,CAAC,SAAS;YACxB,MAAM,EAAE,WAAW,CAAC,UAAU,EAAE,aAAa,CAAC,EAAE,CAAC;YACjD,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE;YACd,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,QAAQ,EAAE;YAC9C,MAAM,EAAE,EAAE;YACV,IAAI,EAAE,UAAU,CAAC,IAAI;SACtB,CAAA;IACH,CAAC,CAAC,CAAA;IACF,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC5D,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAE3D,OAAO;QACL,GAAG,QAAQ;QACX,IAAI,EAAE,OAAO;QACb,YAAY;QACZ,YAAY;KACb,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAClB,UAAqC,EACrC,eAAuB;IAEvB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAA;IAC7B,MAAM,MAAM,GAAG,EAAE,CAAA;IACjB,IAAI,UAAU,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IAClC,CAAC;IACD,IAAI,MAAM,CAAC,EAAE,KAAK,eAAe,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { MemberBadge, MemberResource, ResourceObject } from '../../types'\nimport { GroupsGroupMemberResource } from '../../types/resources/groups/groups_member_resource_with_person'\nimport { useApiPaginator } from '../use_api'\nimport { useCurrentPerson } from '../use_current_person'\n\ntype UseApiPaginatorResult<T extends ResourceObject> = ReturnType<typeof useApiPaginator<T>>\n\nexport type GroupMembersForNewConversationResult = Omit<\n UseApiPaginatorResult<GroupsGroupMemberResource>, // We are passing our own \"members\" data shape\n 'data'\n> & {\n data: MemberResource[]\n adultMembers: MemberResource[]\n childMembers: MemberResource[]\n}\n\n/**\n * This is specifically for the new conversation screen because we assign\n * the \"Conversation owner\" badge to the current person.\n */\nexport function useGroupMembersForNewConversation({ id }: { id: number }) {\n const currentPerson = useCurrentPerson()\n const response = useApiPaginator<GroupsGroupMemberResource>({\n url: `/me/groups/${id}/memberships`,\n data: {\n perPage: 100,\n include: ['person'],\n fields: {\n Membership: ['person, role'],\n Person: ['avatar_url', 'name', 'first_name', 'last_name', 'child'],\n },\n },\n app: 'groups',\n })\n\n const { data: memberships = [] } = response\n const members: MemberResource[] = memberships.map(membership => {\n const { person } = membership\n return {\n type: 'Member',\n avatar: person.avatarUrl,\n badges: buildBadges(membership, currentPerson.id),\n child: person.child,\n id: +person.id,\n name: `${person.firstName} ${person.lastName}`,\n source: [],\n role: membership.role,\n }\n })\n const adultMembers = members.filter(member => !member.child)\n const childMembers = members.filter(member => member.child)\n\n return {\n ...response,\n data: members,\n adultMembers,\n childMembers,\n }\n}\n\nfunction buildBadges(\n membership: GroupsGroupMemberResource,\n currentPersonId: number\n): MemberBadge[] {\n const { person } = membership\n const badges = []\n if (membership.role === 'leader') {\n badges.push({ title: 'Leader' })\n }\n if (person.id === currentPersonId) {\n badges.push({ title: 'Conversation owner' })\n }\n return badges\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groups_form.d.ts","sourceRoot":"","sources":["../../../../src/screens/conversation_new/components/groups_form.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"groups_form.d.ts","sourceRoot":"","sources":["../../../../src/screens/conversation_new/components/groups_form.tsx"],"names":[],"mappings":"AACA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAgB7D,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,eAAO,MAAM,UAAU,gBAAiB,eAAe,sBAwDtD,CAAA"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { StackActions, useNavigation } from '@react-navigation/native';
|
|
2
|
-
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
3
3
|
import { Platform, StyleSheet, TextInput, View } from 'react-native';
|
|
4
4
|
import { Banner, ChildNotice, Heading, Text } from '../../../components';
|
|
5
5
|
import { ActionButton } from '../../../components/display/action_button';
|
|
6
|
-
import { useSuspenseGet } from '../../../hooks';
|
|
6
|
+
import { useCurrentPerson, useSuspenseGet } from '../../../hooks';
|
|
7
7
|
import { Divider, FormList } from './form_list';
|
|
8
8
|
import { pluralize } from '../../../utils';
|
|
9
9
|
import { KeyboardView } from '../../../components/display/keyboard_view';
|
|
10
|
-
import { useGroupMembersForNewConversation } from '../../../hooks/groups/use_group_members_for_new_conversation';
|
|
10
|
+
import { useGroupMembersForNewConversation, } from '../../../hooks/groups/use_group_members_for_new_conversation';
|
|
11
11
|
import { useGroupsConversationCreate } from '../../../hooks/groups/use_groups_conversation_create';
|
|
12
|
+
import { useTheme } from '../../../hooks';
|
|
12
13
|
export const GroupsForm = ({ groupId }) => {
|
|
13
14
|
const navigation = useNavigation();
|
|
14
15
|
const [title, setTitle] = useState();
|
|
@@ -21,6 +22,7 @@ export const GroupsForm = ({ groupId }) => {
|
|
|
21
22
|
},
|
|
22
23
|
app: 'groups',
|
|
23
24
|
});
|
|
25
|
+
const groupMemberships = useGroupMembersForNewConversation({ id: groupId });
|
|
24
26
|
const redirectToConversation = useCallback((conversationId) => {
|
|
25
27
|
// exit from the create stack
|
|
26
28
|
navigation.getParent()?.goBack();
|
|
@@ -29,22 +31,29 @@ export const GroupsForm = ({ groupId }) => {
|
|
|
29
31
|
conversation_id: conversationId,
|
|
30
32
|
}));
|
|
31
33
|
}, [navigation]);
|
|
32
|
-
const groupMemberships = useGroupMembersForNewConversation({ id: groupId });
|
|
33
34
|
const { mutate: handleSave } = useGroupsConversationCreate({
|
|
34
35
|
groupId,
|
|
35
36
|
title,
|
|
36
37
|
onSuccess: redirectToConversation,
|
|
37
38
|
});
|
|
38
39
|
return (<KeyboardView>
|
|
39
|
-
<FormList memberData={groupMemberships.adultMembers} FormContent={<FormContent group={group} title={title} setTitle={setTitle}
|
|
40
|
+
<FormList memberData={groupMemberships.adultMembers} FormContent={<FormContent group={group} title={title} setTitle={setTitle} groupMemberships={groupMemberships}/>}/>
|
|
40
41
|
<ActionButton disabled={!title} title="Start Conversation" onPress={() => handleSave()} infoText="Conversation will be automatically updated if any members are added or removed from this group."/>
|
|
41
42
|
</KeyboardView>);
|
|
42
43
|
};
|
|
43
|
-
function FormContent({ group, title, setTitle,
|
|
44
|
+
function FormContent({ group, title, setTitle, groupMemberships }) {
|
|
44
45
|
const styles = useStyles();
|
|
46
|
+
const currentPerson = useCurrentPerson();
|
|
45
47
|
const { name, membershipsCount } = group;
|
|
46
|
-
const
|
|
48
|
+
const myGroupsMembership = useMemo(() => groupMemberships.data.find(m => m.id === currentPerson.id), [groupMemberships.data, currentPerson.id]);
|
|
49
|
+
const isLeader = myGroupsMembership?.role === 'leader';
|
|
50
|
+
const childMembers = groupMemberships.childMembers;
|
|
47
51
|
const hasChildren = childMembers.length > 0;
|
|
52
|
+
const adultCount = groupMemberships.adultMembers.length;
|
|
53
|
+
const memberCount = isLeader ? membershipsCount : adultCount;
|
|
54
|
+
const memberHeaderLabel = `${pluralize(memberCount, 'member')} selected`;
|
|
55
|
+
const showMemberError = groupMemberships.isError;
|
|
56
|
+
const showChildNotice = hasChildren && isLeader;
|
|
48
57
|
return (<View style={styles.formContent}>
|
|
49
58
|
<View style={styles.toSection}>
|
|
50
59
|
<Heading variant="h3">To:</Heading>
|
|
@@ -57,13 +66,14 @@ function FormContent({ group, title, setTitle, isMemberError, childMembers }) {
|
|
|
57
66
|
</View>
|
|
58
67
|
<Divider />
|
|
59
68
|
<View style={styles.memberSection}>
|
|
60
|
-
<Heading variant="h3">{
|
|
61
|
-
{
|
|
62
|
-
{
|
|
69
|
+
<Heading variant="h3">{memberHeaderLabel}</Heading>
|
|
70
|
+
{showChildNotice && <ChildNotice childMembers={childMembers} style={styles.banner}/>}
|
|
71
|
+
{showMemberError && (<Banner appearance="error" description="There was an issue loading group members, please refresh and try again." style={styles.banner}/>)}
|
|
63
72
|
</View>
|
|
64
73
|
</View>);
|
|
65
74
|
}
|
|
66
75
|
const useStyles = () => {
|
|
76
|
+
const { colors } = useTheme();
|
|
67
77
|
const sectionPadding = 16;
|
|
68
78
|
const inputPadding = 8;
|
|
69
79
|
return StyleSheet.create({
|
|
@@ -92,6 +102,7 @@ const useStyles = () => {
|
|
|
92
102
|
},
|
|
93
103
|
titleInput: {
|
|
94
104
|
fontSize: 18,
|
|
105
|
+
color: colors.textColorDefaultPrimary,
|
|
95
106
|
},
|
|
96
107
|
memberSection: {
|
|
97
108
|
padding: sectionPadding,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groups_form.js","sourceRoot":"","sources":["../../../../src/screens/conversation_new/components/groups_form.tsx"],"names":[],"mappings":"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","sourcesContent":["import { StackActions, useNavigation } from '@react-navigation/native'\nimport React, { useCallback, useState } from 'react'\nimport { Platform, StyleSheet, TextInput, View } from 'react-native'\nimport { Banner, ChildNotice, Heading, Text } from '../../../components'\nimport { ActionButton } from '../../../components/display/action_button'\nimport { useSuspenseGet } from '../../../hooks'\nimport { GroupsGroupResource, MemberResource } from '../../../types'\nimport { Divider, FormList } from './form_list'\nimport { pluralize } from '../../../utils'\nimport { KeyboardView } from '../../../components/display/keyboard_view'\nimport { useGroupMembersForNewConversation } from '../../../hooks/groups/use_group_members_for_new_conversation'\nimport { useGroupsConversationCreate } from '../../../hooks/groups/use_groups_conversation_create'\n\ntype GroupsFormProps = {\n groupId: number\n}\n\nexport const GroupsForm = ({ groupId }: GroupsFormProps) => {\n const navigation = useNavigation()\n const [title, setTitle] = useState<string>()\n const { data: group } = useSuspenseGet<GroupsGroupResource>({\n url: `/me/groups/${groupId}`,\n data: {\n fields: {\n Group: [],\n },\n },\n app: 'groups',\n })\n\n const redirectToConversation = useCallback(\n (conversationId: number) => {\n // exit from the create stack\n navigation.getParent()?.goBack()\n // navigate to the conversation screen\n navigation.dispatch(\n StackActions.push('Conversation', {\n conversation_id: conversationId,\n })\n )\n },\n [navigation]\n )\n\n const groupMemberships = useGroupMembersForNewConversation({ id: groupId })\n\n const { mutate: handleSave } = useGroupsConversationCreate({\n groupId,\n title,\n onSuccess: redirectToConversation,\n })\n\n return (\n <KeyboardView>\n <FormList\n memberData={groupMemberships.adultMembers}\n FormContent={\n <FormContent\n group={group}\n title={title}\n setTitle={setTitle}\n isMemberError={groupMemberships.isError}\n childMembers={groupMemberships.childMembers}\n />\n }\n />\n <ActionButton\n disabled={!title}\n title=\"Start Conversation\"\n onPress={() => handleSave()}\n infoText=\"Conversation will be automatically updated if any members are added or removed from this group.\"\n />\n </KeyboardView>\n )\n}\n\ninterface FormContentProps {\n group: GroupsGroupResource\n title?: string\n setTitle: (title: string) => void\n isMemberError: boolean\n childMembers: MemberResource[]\n}\n\nfunction FormContent({ group, title, setTitle, isMemberError, childMembers }: FormContentProps) {\n const styles = useStyles()\n const { name, membershipsCount } = group\n const memberHeader = pluralize(membershipsCount, 'member')\n const hasChildren = childMembers.length > 0\n\n return (\n <View style={styles.formContent}>\n <View style={styles.toSection}>\n <Heading variant=\"h3\">To:</Heading>\n <Text style={styles.groupName}>{name}</Text>\n </View>\n <Divider />\n <View style={styles.titleSection}>\n <Heading variant=\"h3\">Title</Heading>\n <TextInput\n placeholder=\"Topic of conversation (required)\"\n value={title}\n onChangeText={setTitle}\n style={styles.titleInput}\n autoFocus={true}\n />\n </View>\n <Divider />\n <View style={styles.memberSection}>\n <Heading variant=\"h3\">{memberHeader} selected</Heading>\n {hasChildren && <ChildNotice childMembers={childMembers} style={styles.banner} />}\n {isMemberError && (\n <Banner\n appearance=\"error\"\n description=\"There was an issue loading group members, please refresh and try again.\"\n style={styles.banner}\n />\n )}\n </View>\n </View>\n )\n}\n\nconst useStyles = () => {\n const sectionPadding = 16\n const inputPadding = 8\n\n return StyleSheet.create({\n formContent: {\n paddingVertical: sectionPadding,\n flex: 1,\n },\n toSection: {\n padding: sectionPadding,\n flexDirection: 'row',\n gap: 8,\n },\n groupName: {\n fontSize: 18,\n },\n titleSection: {\n padding: sectionPadding,\n paddingBottom: Platform.select({\n ios: sectionPadding,\n android: sectionPadding - inputPadding,\n }),\n gap: Platform.select({\n ios: 8,\n android: 0,\n }),\n },\n titleInput: {\n fontSize: 18,\n },\n memberSection: {\n padding: sectionPadding,\n paddingBottom: 0,\n },\n banner: {\n marginTop: 16,\n },\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"groups_form.js","sourceRoot":"","sources":["../../../../src/screens/conversation_new/components/groups_form.tsx"],"names":[],"mappings":"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","sourcesContent":["import { StackActions, useNavigation } from '@react-navigation/native'\nimport React, { useCallback, useMemo, useState } from 'react'\nimport { Platform, StyleSheet, TextInput, View } from 'react-native'\nimport { Banner, ChildNotice, Heading, Text } from '../../../components'\nimport { ActionButton } from '../../../components/display/action_button'\nimport { useCurrentPerson, useSuspenseGet } from '../../../hooks'\nimport { GroupsGroupResource } from '../../../types'\nimport { Divider, FormList } from './form_list'\nimport { pluralize } from '../../../utils'\nimport { KeyboardView } from '../../../components/display/keyboard_view'\nimport {\n useGroupMembersForNewConversation,\n GroupMembersForNewConversationResult,\n} from '../../../hooks/groups/use_group_members_for_new_conversation'\nimport { useGroupsConversationCreate } from '../../../hooks/groups/use_groups_conversation_create'\nimport { useTheme } from '../../../hooks'\n\ntype GroupsFormProps = {\n groupId: number\n}\n\nexport const GroupsForm = ({ groupId }: GroupsFormProps) => {\n const navigation = useNavigation()\n const [title, setTitle] = useState<string>()\n const { data: group } = useSuspenseGet<GroupsGroupResource>({\n url: `/me/groups/${groupId}`,\n data: {\n fields: {\n Group: [],\n },\n },\n app: 'groups',\n })\n\n const groupMemberships = useGroupMembersForNewConversation({ id: groupId })\n\n const redirectToConversation = useCallback(\n (conversationId: number) => {\n // exit from the create stack\n navigation.getParent()?.goBack()\n // navigate to the conversation screen\n navigation.dispatch(\n StackActions.push('Conversation', {\n conversation_id: conversationId,\n })\n )\n },\n [navigation]\n )\n\n const { mutate: handleSave } = useGroupsConversationCreate({\n groupId,\n title,\n onSuccess: redirectToConversation,\n })\n\n return (\n <KeyboardView>\n <FormList\n memberData={groupMemberships.adultMembers}\n FormContent={\n <FormContent\n group={group}\n title={title}\n setTitle={setTitle}\n groupMemberships={groupMemberships}\n />\n }\n />\n <ActionButton\n disabled={!title}\n title=\"Start Conversation\"\n onPress={() => handleSave()}\n infoText=\"Conversation will be automatically updated if any members are added or removed from this group.\"\n />\n </KeyboardView>\n )\n}\n\ninterface FormContentProps {\n group: GroupsGroupResource\n title?: string\n setTitle: (title: string) => void\n groupMemberships: GroupMembersForNewConversationResult\n}\n\nfunction FormContent({ group, title, setTitle, groupMemberships }: FormContentProps) {\n const styles = useStyles()\n const currentPerson = useCurrentPerson()\n const { name, membershipsCount } = group\n\n const myGroupsMembership = useMemo(\n () => groupMemberships.data.find(m => m.id === currentPerson.id),\n [groupMemberships.data, currentPerson.id]\n )\n const isLeader = myGroupsMembership?.role === 'leader'\n\n const childMembers = groupMemberships.childMembers\n const hasChildren = childMembers.length > 0\n const adultCount = groupMemberships.adultMembers.length\n const memberCount = isLeader ? membershipsCount : adultCount\n const memberHeaderLabel = `${pluralize(memberCount, 'member')} selected`\n\n const showMemberError = groupMemberships.isError\n const showChildNotice = hasChildren && isLeader\n\n return (\n <View style={styles.formContent}>\n <View style={styles.toSection}>\n <Heading variant=\"h3\">To:</Heading>\n <Text style={styles.groupName}>{name}</Text>\n </View>\n <Divider />\n <View style={styles.titleSection}>\n <Heading variant=\"h3\">Title</Heading>\n <TextInput\n placeholder=\"Topic of conversation (required)\"\n value={title}\n onChangeText={setTitle}\n style={styles.titleInput}\n autoFocus={true}\n />\n </View>\n <Divider />\n <View style={styles.memberSection}>\n <Heading variant=\"h3\">{memberHeaderLabel}</Heading>\n {showChildNotice && <ChildNotice childMembers={childMembers} style={styles.banner} />}\n {showMemberError && (\n <Banner\n appearance=\"error\"\n description=\"There was an issue loading group members, please refresh and try again.\"\n style={styles.banner}\n />\n )}\n </View>\n </View>\n )\n}\n\nconst useStyles = () => {\n const { colors } = useTheme()\n const sectionPadding = 16\n const inputPadding = 8\n\n return StyleSheet.create({\n formContent: {\n paddingVertical: sectionPadding,\n flex: 1,\n },\n toSection: {\n padding: sectionPadding,\n flexDirection: 'row',\n gap: 8,\n },\n groupName: {\n fontSize: 18,\n },\n titleSection: {\n padding: sectionPadding,\n paddingBottom: Platform.select({\n ios: sectionPadding,\n android: sectionPadding - inputPadding,\n }),\n gap: Platform.select({\n ios: 8,\n android: 0,\n }),\n },\n titleInput: {\n fontSize: 18,\n color: colors.textColorDefaultPrimary,\n },\n memberSection: {\n padding: sectionPadding,\n paddingBottom: 0,\n },\n banner: {\n marginTop: 16,\n },\n })\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StaticScreenProps } from '@react-navigation/native';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { MessageResource } from '../types';
|
|
3
4
|
type ConversationRouteProps = {
|
|
4
5
|
conversation_id: number;
|
|
5
6
|
chat_group_graph_id?: string;
|
|
@@ -7,5 +8,11 @@ type ConversationRouteProps = {
|
|
|
7
8
|
};
|
|
8
9
|
export type ConversationScreenProps = StaticScreenProps<ConversationRouteProps>;
|
|
9
10
|
export declare function ConversationScreen({ route }: ConversationScreenProps): React.JSX.Element;
|
|
11
|
+
export type DateSeparator = {
|
|
12
|
+
type: 'DateSeparator';
|
|
13
|
+
id: string;
|
|
14
|
+
date: string;
|
|
15
|
+
};
|
|
16
|
+
export declare const groupMessages: (ms: MessageResource[]) => (MessageResource | DateSeparator)[];
|
|
10
17
|
export {};
|
|
11
18
|
//# sourceMappingURL=conversation_screen.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation_screen.d.ts","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"conversation_screen.d.ts","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,iBAAiB,EAIlB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,KAAiC,MAAM,OAAO,CAAA;AAUrD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAG1C,KAAK,sBAAsB,GAAG;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,iBAAiB,CAAC,sBAAsB,CAAC,CAAA;AAE/E,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE,uBAAuB,qBAiDpE;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAwC/E,eAAO,MAAM,aAAa,OAAQ,eAAe,EAAE,wCAkClD,CAAA"}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
// @ts-expect-error
|
|
2
|
+
import { date as formatDate } from '@planningcenter/datetime-fmt';
|
|
1
3
|
import { HeaderTitle, PlatformPressable } from '@react-navigation/elements';
|
|
2
4
|
import { useNavigation, useTheme as useNavigationTheme, useRoute, } from '@react-navigation/native';
|
|
5
|
+
import moment from 'moment';
|
|
3
6
|
import React, { useCallback, useEffect } from 'react';
|
|
4
7
|
import { FlatList, Platform, StyleSheet, View } from 'react-native';
|
|
5
8
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
|
-
import { Badge, Icon } from '../components';
|
|
9
|
+
import { Badge, Icon, Text } from '../components';
|
|
7
10
|
import { Message } from '../components/conversation/message';
|
|
8
11
|
import { MessageForm } from '../components/conversation/message_form';
|
|
9
12
|
import { KeyboardView } from '../components/display/keyboard_view';
|
|
13
|
+
import { useTheme } from '../hooks';
|
|
10
14
|
import { useConversation } from '../hooks/use_conversation';
|
|
11
15
|
import { useConversationMessages } from '../hooks/use_conversation_messages';
|
|
16
|
+
import { getRelativeDateStatus } from '../utils/date';
|
|
12
17
|
export function ConversationScreen({ route }) {
|
|
13
18
|
const styles = useStyles();
|
|
14
19
|
const navigation = useNavigation();
|
|
@@ -17,6 +22,7 @@ export function ConversationScreen({ route }) {
|
|
|
17
22
|
const { messages, refetch, isRefetching, fetchNextPage } = useConversationMessages({
|
|
18
23
|
conversation_id,
|
|
19
24
|
});
|
|
25
|
+
const messagesWithSeparators = groupMessages(messages);
|
|
20
26
|
// Seems to be necessary to define this way so we get the route picked up
|
|
21
27
|
const headerTitle = useCallback((props) => <PressableHeaderTitle {...props}/>, []);
|
|
22
28
|
useEffect(() => {
|
|
@@ -24,7 +30,12 @@ export function ConversationScreen({ route }) {
|
|
|
24
30
|
}, [conversation, conversation_id, navigation, headerTitle, conversation?.title]);
|
|
25
31
|
return (<View style={styles.container}>
|
|
26
32
|
<KeyboardView style={styles.keyboardView}>
|
|
27
|
-
<FlatList inverted contentContainerStyle={styles.listContainer} refreshing={isRefetching} onRefresh={refetch} data={
|
|
33
|
+
<FlatList inverted contentContainerStyle={styles.listContainer} refreshing={isRefetching} onRefresh={refetch} data={messagesWithSeparators} keyExtractor={item => item.id} renderItem={({ item }) => {
|
|
34
|
+
if (item.type === 'DateSeparator') {
|
|
35
|
+
return <InlineDateSeparator {...item}/>;
|
|
36
|
+
}
|
|
37
|
+
return <Message {...item} conversation_id={conversation_id}/>;
|
|
38
|
+
}} onEndReached={() => fetchNextPage()}/>
|
|
28
39
|
<MessageForm.Root conversation={conversation}>
|
|
29
40
|
{/* <MessageForm.AttachmentPicker /> */}
|
|
30
41
|
<MessageForm.Commands />
|
|
@@ -34,6 +45,69 @@ export function ConversationScreen({ route }) {
|
|
|
34
45
|
</KeyboardView>
|
|
35
46
|
</View>);
|
|
36
47
|
}
|
|
48
|
+
function InlineDateSeparator({ date }) {
|
|
49
|
+
const styles = useDateSeparatorStyles();
|
|
50
|
+
const { isThisYear } = getRelativeDateStatus(date);
|
|
51
|
+
const showYear = !isThisYear;
|
|
52
|
+
const dateStamp = formatDate(date, { style: 'long', year: showYear });
|
|
53
|
+
return (<View style={styles.container}>
|
|
54
|
+
<View style={styles.separator}/>
|
|
55
|
+
<Text variant="footnote" style={styles.dateText}>
|
|
56
|
+
{dateStamp}
|
|
57
|
+
</Text>
|
|
58
|
+
<View style={styles.separator}/>
|
|
59
|
+
</View>);
|
|
60
|
+
}
|
|
61
|
+
const useDateSeparatorStyles = () => {
|
|
62
|
+
const theme = useTheme();
|
|
63
|
+
return StyleSheet.create({
|
|
64
|
+
container: {
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
flexDirection: 'row',
|
|
67
|
+
paddingHorizontal: 16,
|
|
68
|
+
paddingVertical: 16,
|
|
69
|
+
},
|
|
70
|
+
separator: {
|
|
71
|
+
flex: 1,
|
|
72
|
+
height: 1,
|
|
73
|
+
borderTopWidth: 1,
|
|
74
|
+
borderTopColor: theme.colors.borderColorDefaultBase,
|
|
75
|
+
},
|
|
76
|
+
dateText: {
|
|
77
|
+
paddingHorizontal: 8,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
export const groupMessages = (ms) => {
|
|
82
|
+
let enrichedMessages = [];
|
|
83
|
+
let encounteredOneOfMyMessages = false;
|
|
84
|
+
ms.forEach((message, i) => {
|
|
85
|
+
const prevMessage = ms[i + 1];
|
|
86
|
+
const nextMessage = ms[i - 1];
|
|
87
|
+
const date = moment(message.createdAt).format('YYYY-MM-DD');
|
|
88
|
+
const prevMessageDifferentAuthor = message.author?.id !== prevMessage?.author?.id;
|
|
89
|
+
const nextMessageDifferentAuthor = message.author?.id !== nextMessage?.author?.id;
|
|
90
|
+
const prevMessageMoreThan5Minutes = prevMessage &&
|
|
91
|
+
new Date(message.createdAt).getTime() - new Date(prevMessage.createdAt).getTime() > 60000 * 5;
|
|
92
|
+
const nextMessageMoreThan5Minutes = nextMessage &&
|
|
93
|
+
new Date(nextMessage.createdAt).getTime() - new Date(message.createdAt).getTime() > 60000 * 5;
|
|
94
|
+
if (message.mine && !encounteredOneOfMyMessages) {
|
|
95
|
+
encounteredOneOfMyMessages = true;
|
|
96
|
+
message.myLatestInConversation = true;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
message.myLatestInConversation = false;
|
|
100
|
+
}
|
|
101
|
+
message.lastInGroup = !nextMessage || nextMessageDifferentAuthor || nextMessageMoreThan5Minutes;
|
|
102
|
+
message.renderAuthor =
|
|
103
|
+
!message.mine && (!prevMessage || prevMessageDifferentAuthor || prevMessageMoreThan5Minutes);
|
|
104
|
+
enrichedMessages.push(message);
|
|
105
|
+
if (!prevMessage || date !== moment(prevMessage.createdAt).format('YYYY-MM-DD')) {
|
|
106
|
+
enrichedMessages.push({ type: 'DateSeparator', id: `day-divider-${message.id}`, date });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
return enrichedMessages;
|
|
110
|
+
};
|
|
37
111
|
const PressableHeaderTitle = ({ style, children }) => {
|
|
38
112
|
const styles = usePressableHeaderStyle();
|
|
39
113
|
const navigation = useNavigation();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation_screen.js","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { HeaderTitle, HeaderTitleProps, PlatformPressable } from '@react-navigation/elements'\nimport {\n StaticScreenProps,\n useNavigation,\n useTheme as useNavigationTheme,\n useRoute,\n} from '@react-navigation/native'\nimport React, { useCallback, useEffect } from 'react'\nimport { FlatList, Platform, StyleSheet, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Badge, Icon } from '../components'\nimport { Message } from '../components/conversation/message'\nimport { MessageForm } from '../components/conversation/message_form'\nimport { KeyboardView } from '../components/display/keyboard_view'\nimport { useConversation } from '../hooks/use_conversation'\nimport { useConversationMessages } from '../hooks/use_conversation_messages'\n\ntype ConversationRouteProps = {\n conversation_id: number\n chat_group_graph_id?: string\n clear_input?: boolean\n}\n\nexport type ConversationScreenProps = StaticScreenProps<ConversationRouteProps>\n\nexport function ConversationScreen({ route }: ConversationScreenProps) {\n const styles = useStyles()\n const navigation = useNavigation()\n\n const { conversation_id } = route.params\n const { data: conversation } = useConversation(route.params)\n const { messages, refetch, isRefetching, fetchNextPage } = useConversationMessages({\n conversation_id,\n })\n\n // Seems to be necessary to define this way so we get the route picked up\n const headerTitle = useCallback(\n (props: HeaderTitleProps) => <PressableHeaderTitle {...props} />,\n []\n )\n\n useEffect(() => {\n navigation.setOptions({ headerTitle, title: conversation?.title })\n }, [conversation, conversation_id, navigation, headerTitle, conversation?.title])\n\n return (\n <View style={styles.container}>\n <KeyboardView style={styles.keyboardView}>\n <FlatList\n inverted\n contentContainerStyle={styles.listContainer}\n refreshing={isRefetching}\n onRefresh={refetch}\n data={messages}\n keyExtractor={item => item.id}\n renderItem={({ item }) => <Message {...item} conversation_id={conversation_id} />}\n onEndReached={() => fetchNextPage()}\n />\n <MessageForm.Root conversation={conversation}>\n {/* <MessageForm.AttachmentPicker /> */}\n <MessageForm.Commands />\n <MessageForm.TextInput />\n <MessageForm.SubmitButton />\n </MessageForm.Root>\n </KeyboardView>\n </View>\n )\n}\n\nconst PressableHeaderTitle = ({ style, children }: HeaderTitleProps) => {\n const styles = usePressableHeaderStyle()\n const navigation = useNavigation()\n\n const route = useRoute() as ConversationScreenProps['route']\n\n const { data: conversation } = useConversation(route.params)\n const badge = conversation.badges?.[0]\n const resourceType = badge?.pcoResourceType || ''\n const productName = badge?.appName\n const name = badge?.text || undefined\n\n return (\n <PlatformPressable\n style={styles.container}\n onPress={() =>\n navigation.navigate('ConversationDetails', { conversation_id: conversation?.id })\n }\n >\n <View style={styles.titleWrapper}>\n <HeaderTitle style={[styles.title, style]}>{children}</HeaderTitle>\n <Icon name=\"general.downChevron\" size={12} />\n </View>\n <Badge\n variant=\"metaSubtle\"\n productLogoName={productName}\n label={resourceType}\n metaLabel={name}\n style={styles.badge}\n />\n </PlatformPressable>\n )\n}\n\nconst usePressableHeaderStyle = () => {\n return StyleSheet.create({\n container: {\n alignItems: Platform.select({ android: 'flex-start', default: 'center' }),\n },\n titleWrapper: {\n alignItems: 'center',\n columnGap: 4,\n flexDirection: 'row',\n },\n title: {\n padding: 0,\n margin: 0,\n },\n badge: {\n alignSelf: Platform.select({ android: 'flex-start', default: 'center' }),\n marginTop: 2,\n },\n })\n}\n\nconst useStyles = () => {\n const navigationTheme = useNavigationTheme()\n const { bottom } = useSafeAreaInsets()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'center',\n backgroundColor: navigationTheme.colors.card,\n paddingBottom: bottom,\n },\n keyboardView: {\n flex: 1,\n },\n listContainer: {\n gap: 12,\n paddingHorizontal: 16,\n paddingVertical: 12,\n },\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"conversation_screen.js","sourceRoot":"","sources":["../../src/screens/conversation_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["// @ts-expect-error\nimport { date as formatDate } from '@planningcenter/datetime-fmt'\nimport { HeaderTitle, HeaderTitleProps, PlatformPressable } from '@react-navigation/elements'\nimport {\n StaticScreenProps,\n useNavigation,\n useTheme as useNavigationTheme,\n useRoute,\n} from '@react-navigation/native'\nimport moment from 'moment'\nimport React, { useCallback, useEffect } from 'react'\nimport { FlatList, Platform, StyleSheet, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Badge, Icon, Text } from '../components'\nimport { Message } from '../components/conversation/message'\nimport { MessageForm } from '../components/conversation/message_form'\nimport { KeyboardView } from '../components/display/keyboard_view'\nimport { useTheme } from '../hooks'\nimport { useConversation } from '../hooks/use_conversation'\nimport { useConversationMessages } from '../hooks/use_conversation_messages'\nimport { MessageResource } from '../types'\nimport { getRelativeDateStatus } from '../utils/date'\n\ntype ConversationRouteProps = {\n conversation_id: number\n chat_group_graph_id?: string\n clear_input?: boolean\n}\n\nexport type ConversationScreenProps = StaticScreenProps<ConversationRouteProps>\n\nexport function ConversationScreen({ route }: ConversationScreenProps) {\n const styles = useStyles()\n const navigation = useNavigation()\n\n const { conversation_id } = route.params\n const { data: conversation } = useConversation(route.params)\n const { messages, refetch, isRefetching, fetchNextPage } = useConversationMessages({\n conversation_id,\n })\n const messagesWithSeparators = groupMessages(messages)\n\n // Seems to be necessary to define this way so we get the route picked up\n const headerTitle = useCallback(\n (props: HeaderTitleProps) => <PressableHeaderTitle {...props} />,\n []\n )\n\n useEffect(() => {\n navigation.setOptions({ headerTitle, title: conversation?.title })\n }, [conversation, conversation_id, navigation, headerTitle, conversation?.title])\n\n return (\n <View style={styles.container}>\n <KeyboardView style={styles.keyboardView}>\n <FlatList\n inverted\n contentContainerStyle={styles.listContainer}\n refreshing={isRefetching}\n onRefresh={refetch}\n data={messagesWithSeparators}\n keyExtractor={item => item.id}\n renderItem={({ item }) => {\n if (item.type === 'DateSeparator') {\n return <InlineDateSeparator {...item} />\n }\n\n return <Message {...item} conversation_id={conversation_id} />\n }}\n onEndReached={() => fetchNextPage()}\n />\n <MessageForm.Root conversation={conversation}>\n {/* <MessageForm.AttachmentPicker /> */}\n <MessageForm.Commands />\n <MessageForm.TextInput />\n <MessageForm.SubmitButton />\n </MessageForm.Root>\n </KeyboardView>\n </View>\n )\n}\n\nexport type DateSeparator = { type: 'DateSeparator'; id: string; date: string }\n\nfunction InlineDateSeparator({ date }: DateSeparator) {\n const styles = useDateSeparatorStyles()\n const { isThisYear } = getRelativeDateStatus(date)\n const showYear = !isThisYear\n const dateStamp = formatDate(date, { style: 'long', year: showYear })\n\n return (\n <View style={styles.container}>\n <View style={styles.separator} />\n <Text variant=\"footnote\" style={styles.dateText}>\n {dateStamp}\n </Text>\n <View style={styles.separator} />\n </View>\n )\n}\n\nconst useDateSeparatorStyles = () => {\n const theme = useTheme()\n return StyleSheet.create({\n container: {\n alignItems: 'center',\n flexDirection: 'row',\n paddingHorizontal: 16,\n paddingVertical: 16,\n },\n separator: {\n flex: 1,\n height: 1,\n borderTopWidth: 1,\n borderTopColor: theme.colors.borderColorDefaultBase,\n },\n dateText: {\n paddingHorizontal: 8,\n },\n })\n}\n\nexport const groupMessages = (ms: MessageResource[]) => {\n let enrichedMessages: (MessageResource | DateSeparator)[] = []\n let encounteredOneOfMyMessages = false\n\n ms.forEach((message, i) => {\n const prevMessage = ms[i + 1]\n const nextMessage = ms[i - 1]\n const date = moment(message.createdAt).format('YYYY-MM-DD')\n const prevMessageDifferentAuthor = message.author?.id !== prevMessage?.author?.id\n const nextMessageDifferentAuthor = message.author?.id !== nextMessage?.author?.id\n const prevMessageMoreThan5Minutes =\n prevMessage &&\n new Date(message.createdAt).getTime() - new Date(prevMessage.createdAt).getTime() > 60000 * 5\n const nextMessageMoreThan5Minutes =\n nextMessage &&\n new Date(nextMessage.createdAt).getTime() - new Date(message.createdAt).getTime() > 60000 * 5\n\n if (message.mine && !encounteredOneOfMyMessages) {\n encounteredOneOfMyMessages = true\n message.myLatestInConversation = true\n } else {\n message.myLatestInConversation = false\n }\n message.lastInGroup = !nextMessage || nextMessageDifferentAuthor || nextMessageMoreThan5Minutes\n message.renderAuthor =\n !message.mine && (!prevMessage || prevMessageDifferentAuthor || prevMessageMoreThan5Minutes)\n\n enrichedMessages.push(message)\n if (!prevMessage || date !== moment(prevMessage.createdAt).format('YYYY-MM-DD')) {\n enrichedMessages.push({ type: 'DateSeparator', id: `day-divider-${message.id}`, date })\n }\n })\n\n return enrichedMessages\n}\n\nconst PressableHeaderTitle = ({ style, children }: HeaderTitleProps) => {\n const styles = usePressableHeaderStyle()\n const navigation = useNavigation()\n\n const route = useRoute() as ConversationScreenProps['route']\n\n const { data: conversation } = useConversation(route.params)\n const badge = conversation.badges?.[0]\n const resourceType = badge?.pcoResourceType || ''\n const productName = badge?.appName\n const name = badge?.text || undefined\n\n return (\n <PlatformPressable\n style={styles.container}\n onPress={() =>\n navigation.navigate('ConversationDetails', { conversation_id: conversation?.id })\n }\n >\n <View style={styles.titleWrapper}>\n <HeaderTitle style={[styles.title, style]}>{children}</HeaderTitle>\n <Icon name=\"general.downChevron\" size={12} />\n </View>\n <Badge\n variant=\"metaSubtle\"\n productLogoName={productName}\n label={resourceType}\n metaLabel={name}\n style={styles.badge}\n />\n </PlatformPressable>\n )\n}\n\nconst usePressableHeaderStyle = () => {\n return StyleSheet.create({\n container: {\n alignItems: Platform.select({ android: 'flex-start', default: 'center' }),\n },\n titleWrapper: {\n alignItems: 'center',\n columnGap: 4,\n flexDirection: 'row',\n },\n title: {\n padding: 0,\n margin: 0,\n },\n badge: {\n alignSelf: Platform.select({ android: 'flex-start', default: 'center' }),\n marginTop: 2,\n },\n })\n}\n\nconst useStyles = () => {\n const navigationTheme = useNavigationTheme()\n const { bottom } = useSafeAreaInsets()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'center',\n backgroundColor: navigationTheme.colors.card,\n paddingBottom: bottom,\n },\n keyboardView: {\n flex: 1,\n },\n listContainer: {\n gap: 12,\n paddingHorizontal: 16,\n paddingVertical: 12,\n },\n })\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/types/resources/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAA;AACnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,8BAA8B,EAAE,CAAA;IAC7C,MAAM,EAAE,cAAc,CAAA;IACtB,cAAc,EAAE,qBAAqB,EAAE,CAAA;IAGvC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/types/resources/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAA;AACnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,8BAA8B,EAAE,CAAA;IAC7C,MAAM,EAAE,cAAc,CAAA;IACtB,cAAc,EAAE,qBAAqB,EAAE,CAAA;IAGvC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/types/resources/message.ts"],"names":[],"mappings":"","sourcesContent":["import { DenormalizedAttachmentResource } from './denormalized_attachment_resource'\nimport type { PersonResource } from './person'\nimport type { ReactionCountResource } from './reaction'\n\nexport interface MessageResource {\n type: 'Message'\n id: string\n text: string\n html: string\n createdAt: string\n deletedAt: string | null\n textEditedAt: string | null\n mine: boolean\n attachments: DenormalizedAttachmentResource[]\n author: PersonResource\n reactionCounts: ReactionCountResource[]\n\n // Custom Local Properties we set for rendering\n renderAuthor?: boolean\n renderTime?: boolean\n}\n"]}
|
|
1
|
+
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/types/resources/message.ts"],"names":[],"mappings":"","sourcesContent":["import { DenormalizedAttachmentResource } from './denormalized_attachment_resource'\nimport type { PersonResource } from './person'\nimport type { ReactionCountResource } from './reaction'\n\nexport interface MessageResource {\n type: 'Message'\n id: string\n text: string\n html: string\n createdAt: string\n deletedAt: string | null\n textEditedAt: string | null\n mine: boolean\n attachments: DenormalizedAttachmentResource[]\n author: PersonResource\n reactionCounts: ReactionCountResource[]\n\n // Custom Local Properties we set for rendering\n renderAuthor?: boolean\n renderTime?: boolean\n myLatestInConversation?: boolean\n lastInGroup?: boolean\n}\n"]}
|
package/build/utils/date.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
type DateProps = string | number | Date;
|
|
2
2
|
export declare function formatDatePreview(date?: DateProps): any;
|
|
3
|
+
export declare function getRelativeDateStatus(date: DateProps): {
|
|
4
|
+
isToday: boolean;
|
|
5
|
+
isThisWeek: boolean;
|
|
6
|
+
isThisYear: boolean;
|
|
7
|
+
};
|
|
3
8
|
export {};
|
|
4
9
|
//# sourceMappingURL=date.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAIA,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;AAEvC,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,SAAS,OAcjD"}
|
|
1
|
+
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAIA,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;AAEvC,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,SAAS,OAcjD;AAOD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS;;;;EAQpD"}
|
package/build/utils/date.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ts-
|
|
1
|
+
// @ts-expect-error
|
|
2
2
|
import { date as formatDate } from '@planningcenter/datetime-fmt';
|
|
3
3
|
import moment from 'moment-timezone';
|
|
4
4
|
export function formatDatePreview(date) {
|
|
@@ -21,4 +21,11 @@ const formatShorterDate = (date) => {
|
|
|
21
21
|
// Drop the century from the year
|
|
22
22
|
return formatDate(date, { style: 'short', year: true }).replace(/20(\d{2})/, '$1');
|
|
23
23
|
};
|
|
24
|
+
export function getRelativeDateStatus(date) {
|
|
25
|
+
const now = moment();
|
|
26
|
+
const isToday = now.isSame(date, 'day');
|
|
27
|
+
const isThisWeek = now.isSame(date, 'week');
|
|
28
|
+
const isThisYear = now.isSame(date, 'year');
|
|
29
|
+
return { isToday, isThisWeek, isThisYear };
|
|
30
|
+
}
|
|
24
31
|
//# sourceMappingURL=date.js.map
|
package/build/utils/date.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,MAAM,MAAM,iBAAiB,CAAA;AAIpC,MAAM,UAAU,iBAAiB,CAAC,IAAgB;IAChD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAEpB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IACpB,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACvC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAE3C,IAAI,OAAO;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACjD,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAA;IACpE,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAA;IAEjE,wBAAwB;IACxB,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAA;AAChC,CAAC;AAED,MAAM,iBAAiB,GAAG,CAAC,IAAe,EAAE,EAAE;IAC5C,iCAAiC;IACjC,OAAO,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;AACpF,CAAC,CAAA;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAe;IACnD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IAEpB,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACvC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAE3C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAA;AAC5C,CAAC","sourcesContent":["// @ts-expect-error\nimport { date as formatDate } from '@planningcenter/datetime-fmt'\nimport moment from 'moment-timezone'\n\ntype DateProps = string | number | Date\n\nexport function formatDatePreview(date?: DateProps) {\n if (!date) return ''\n\n const now = moment()\n const isToday = now.isSame(date, 'day')\n const isThisWeek = now.isSame(date, 'week')\n const isThisYear = now.isSame(date, 'year')\n\n if (isToday) return moment(date).format('h:mm a')\n if (isThisWeek) return formatDate(date, { style: 'relative-short' })\n if (isThisYear) return formatDate(date, { style: 'abbreviated' })\n\n // TODO: Org date format\n return formatShorterDate(date)\n}\n\nconst formatShorterDate = (date: DateProps) => {\n // Drop the century from the year\n return formatDate(date, { style: 'short', year: true }).replace(/20(\\d{2})/, '$1')\n}\n\nexport function getRelativeDateStatus(date: DateProps) {\n const now = moment()\n\n const isToday = now.isSame(date, 'day')\n const isThisWeek = now.isSame(date, 'week')\n const isThisYear = now.isSame(date, 'year')\n\n return { isToday, isThisWeek, isThisYear }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "3.2.0-rc.
|
|
3
|
+
"version": "3.2.0-rc.25",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"prettier": "^3.4.2",
|
|
56
56
|
"typescript": "<5.6.0"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "5341fc7d2489707a08f20ce39b3fef50cbe9d5c5"
|
|
59
59
|
}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import { MemberBadge, MemberResource } from '../../types'
|
|
1
|
+
import { MemberBadge, MemberResource, ResourceObject } from '../../types'
|
|
2
2
|
import { GroupsGroupMemberResource } from '../../types/resources/groups/groups_member_resource_with_person'
|
|
3
3
|
import { useApiPaginator } from '../use_api'
|
|
4
4
|
import { useCurrentPerson } from '../use_current_person'
|
|
5
5
|
|
|
6
|
+
type UseApiPaginatorResult<T extends ResourceObject> = ReturnType<typeof useApiPaginator<T>>
|
|
7
|
+
|
|
8
|
+
export type GroupMembersForNewConversationResult = Omit<
|
|
9
|
+
UseApiPaginatorResult<GroupsGroupMemberResource>, // We are passing our own "members" data shape
|
|
10
|
+
'data'
|
|
11
|
+
> & {
|
|
12
|
+
data: MemberResource[]
|
|
13
|
+
adultMembers: MemberResource[]
|
|
14
|
+
childMembers: MemberResource[]
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
/**
|
|
7
18
|
* This is specifically for the new conversation screen because we assign
|
|
8
19
|
* the "Conversation owner" badge to the current person.
|
|
@@ -33,6 +44,7 @@ export function useGroupMembersForNewConversation({ id }: { id: number }) {
|
|
|
33
44
|
id: +person.id,
|
|
34
45
|
name: `${person.firstName} ${person.lastName}`,
|
|
35
46
|
source: [],
|
|
47
|
+
role: membership.role,
|
|
36
48
|
}
|
|
37
49
|
})
|
|
38
50
|
const adultMembers = members.filter(member => !member.child)
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { StackActions, useNavigation } from '@react-navigation/native'
|
|
2
|
-
import React, { useCallback, useState } from 'react'
|
|
2
|
+
import React, { useCallback, useMemo, useState } from 'react'
|
|
3
3
|
import { Platform, StyleSheet, TextInput, View } from 'react-native'
|
|
4
4
|
import { Banner, ChildNotice, Heading, Text } from '../../../components'
|
|
5
5
|
import { ActionButton } from '../../../components/display/action_button'
|
|
6
|
-
import { useSuspenseGet } from '../../../hooks'
|
|
7
|
-
import { GroupsGroupResource
|
|
6
|
+
import { useCurrentPerson, useSuspenseGet } from '../../../hooks'
|
|
7
|
+
import { GroupsGroupResource } from '../../../types'
|
|
8
8
|
import { Divider, FormList } from './form_list'
|
|
9
9
|
import { pluralize } from '../../../utils'
|
|
10
10
|
import { KeyboardView } from '../../../components/display/keyboard_view'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
useGroupMembersForNewConversation,
|
|
13
|
+
GroupMembersForNewConversationResult,
|
|
14
|
+
} from '../../../hooks/groups/use_group_members_for_new_conversation'
|
|
12
15
|
import { useGroupsConversationCreate } from '../../../hooks/groups/use_groups_conversation_create'
|
|
16
|
+
import { useTheme } from '../../../hooks'
|
|
13
17
|
|
|
14
18
|
type GroupsFormProps = {
|
|
15
19
|
groupId: number
|
|
@@ -28,6 +32,8 @@ export const GroupsForm = ({ groupId }: GroupsFormProps) => {
|
|
|
28
32
|
app: 'groups',
|
|
29
33
|
})
|
|
30
34
|
|
|
35
|
+
const groupMemberships = useGroupMembersForNewConversation({ id: groupId })
|
|
36
|
+
|
|
31
37
|
const redirectToConversation = useCallback(
|
|
32
38
|
(conversationId: number) => {
|
|
33
39
|
// exit from the create stack
|
|
@@ -42,8 +48,6 @@ export const GroupsForm = ({ groupId }: GroupsFormProps) => {
|
|
|
42
48
|
[navigation]
|
|
43
49
|
)
|
|
44
50
|
|
|
45
|
-
const groupMemberships = useGroupMembersForNewConversation({ id: groupId })
|
|
46
|
-
|
|
47
51
|
const { mutate: handleSave } = useGroupsConversationCreate({
|
|
48
52
|
groupId,
|
|
49
53
|
title,
|
|
@@ -59,8 +63,7 @@ export const GroupsForm = ({ groupId }: GroupsFormProps) => {
|
|
|
59
63
|
group={group}
|
|
60
64
|
title={title}
|
|
61
65
|
setTitle={setTitle}
|
|
62
|
-
|
|
63
|
-
childMembers={groupMemberships.childMembers}
|
|
66
|
+
groupMemberships={groupMemberships}
|
|
64
67
|
/>
|
|
65
68
|
}
|
|
66
69
|
/>
|
|
@@ -78,15 +81,28 @@ interface FormContentProps {
|
|
|
78
81
|
group: GroupsGroupResource
|
|
79
82
|
title?: string
|
|
80
83
|
setTitle: (title: string) => void
|
|
81
|
-
|
|
82
|
-
childMembers: MemberResource[]
|
|
84
|
+
groupMemberships: GroupMembersForNewConversationResult
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
function FormContent({ group, title, setTitle,
|
|
87
|
+
function FormContent({ group, title, setTitle, groupMemberships }: FormContentProps) {
|
|
86
88
|
const styles = useStyles()
|
|
89
|
+
const currentPerson = useCurrentPerson()
|
|
87
90
|
const { name, membershipsCount } = group
|
|
88
|
-
|
|
91
|
+
|
|
92
|
+
const myGroupsMembership = useMemo(
|
|
93
|
+
() => groupMemberships.data.find(m => m.id === currentPerson.id),
|
|
94
|
+
[groupMemberships.data, currentPerson.id]
|
|
95
|
+
)
|
|
96
|
+
const isLeader = myGroupsMembership?.role === 'leader'
|
|
97
|
+
|
|
98
|
+
const childMembers = groupMemberships.childMembers
|
|
89
99
|
const hasChildren = childMembers.length > 0
|
|
100
|
+
const adultCount = groupMemberships.adultMembers.length
|
|
101
|
+
const memberCount = isLeader ? membershipsCount : adultCount
|
|
102
|
+
const memberHeaderLabel = `${pluralize(memberCount, 'member')} selected`
|
|
103
|
+
|
|
104
|
+
const showMemberError = groupMemberships.isError
|
|
105
|
+
const showChildNotice = hasChildren && isLeader
|
|
90
106
|
|
|
91
107
|
return (
|
|
92
108
|
<View style={styles.formContent}>
|
|
@@ -107,9 +123,9 @@ function FormContent({ group, title, setTitle, isMemberError, childMembers }: Fo
|
|
|
107
123
|
</View>
|
|
108
124
|
<Divider />
|
|
109
125
|
<View style={styles.memberSection}>
|
|
110
|
-
<Heading variant="h3">{
|
|
111
|
-
{
|
|
112
|
-
{
|
|
126
|
+
<Heading variant="h3">{memberHeaderLabel}</Heading>
|
|
127
|
+
{showChildNotice && <ChildNotice childMembers={childMembers} style={styles.banner} />}
|
|
128
|
+
{showMemberError && (
|
|
113
129
|
<Banner
|
|
114
130
|
appearance="error"
|
|
115
131
|
description="There was an issue loading group members, please refresh and try again."
|
|
@@ -122,6 +138,7 @@ function FormContent({ group, title, setTitle, isMemberError, childMembers }: Fo
|
|
|
122
138
|
}
|
|
123
139
|
|
|
124
140
|
const useStyles = () => {
|
|
141
|
+
const { colors } = useTheme()
|
|
125
142
|
const sectionPadding = 16
|
|
126
143
|
const inputPadding = 8
|
|
127
144
|
|
|
@@ -151,6 +168,7 @@ const useStyles = () => {
|
|
|
151
168
|
},
|
|
152
169
|
titleInput: {
|
|
153
170
|
fontSize: 18,
|
|
171
|
+
color: colors.textColorDefaultPrimary,
|
|
154
172
|
},
|
|
155
173
|
memberSection: {
|
|
156
174
|
padding: sectionPadding,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-expect-error
|
|
2
|
+
import { date as formatDate } from '@planningcenter/datetime-fmt'
|
|
1
3
|
import { HeaderTitle, HeaderTitleProps, PlatformPressable } from '@react-navigation/elements'
|
|
2
4
|
import {
|
|
3
5
|
StaticScreenProps,
|
|
@@ -5,15 +7,19 @@ import {
|
|
|
5
7
|
useTheme as useNavigationTheme,
|
|
6
8
|
useRoute,
|
|
7
9
|
} from '@react-navigation/native'
|
|
10
|
+
import moment from 'moment'
|
|
8
11
|
import React, { useCallback, useEffect } from 'react'
|
|
9
12
|
import { FlatList, Platform, StyleSheet, View } from 'react-native'
|
|
10
13
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
11
|
-
import { Badge, Icon } from '../components'
|
|
14
|
+
import { Badge, Icon, Text } from '../components'
|
|
12
15
|
import { Message } from '../components/conversation/message'
|
|
13
16
|
import { MessageForm } from '../components/conversation/message_form'
|
|
14
17
|
import { KeyboardView } from '../components/display/keyboard_view'
|
|
18
|
+
import { useTheme } from '../hooks'
|
|
15
19
|
import { useConversation } from '../hooks/use_conversation'
|
|
16
20
|
import { useConversationMessages } from '../hooks/use_conversation_messages'
|
|
21
|
+
import { MessageResource } from '../types'
|
|
22
|
+
import { getRelativeDateStatus } from '../utils/date'
|
|
17
23
|
|
|
18
24
|
type ConversationRouteProps = {
|
|
19
25
|
conversation_id: number
|
|
@@ -32,6 +38,7 @@ export function ConversationScreen({ route }: ConversationScreenProps) {
|
|
|
32
38
|
const { messages, refetch, isRefetching, fetchNextPage } = useConversationMessages({
|
|
33
39
|
conversation_id,
|
|
34
40
|
})
|
|
41
|
+
const messagesWithSeparators = groupMessages(messages)
|
|
35
42
|
|
|
36
43
|
// Seems to be necessary to define this way so we get the route picked up
|
|
37
44
|
const headerTitle = useCallback(
|
|
@@ -51,9 +58,15 @@ export function ConversationScreen({ route }: ConversationScreenProps) {
|
|
|
51
58
|
contentContainerStyle={styles.listContainer}
|
|
52
59
|
refreshing={isRefetching}
|
|
53
60
|
onRefresh={refetch}
|
|
54
|
-
data={
|
|
61
|
+
data={messagesWithSeparators}
|
|
55
62
|
keyExtractor={item => item.id}
|
|
56
|
-
renderItem={({ item }) =>
|
|
63
|
+
renderItem={({ item }) => {
|
|
64
|
+
if (item.type === 'DateSeparator') {
|
|
65
|
+
return <InlineDateSeparator {...item} />
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return <Message {...item} conversation_id={conversation_id} />
|
|
69
|
+
}}
|
|
57
70
|
onEndReached={() => fetchNextPage()}
|
|
58
71
|
/>
|
|
59
72
|
<MessageForm.Root conversation={conversation}>
|
|
@@ -67,6 +80,82 @@ export function ConversationScreen({ route }: ConversationScreenProps) {
|
|
|
67
80
|
)
|
|
68
81
|
}
|
|
69
82
|
|
|
83
|
+
export type DateSeparator = { type: 'DateSeparator'; id: string; date: string }
|
|
84
|
+
|
|
85
|
+
function InlineDateSeparator({ date }: DateSeparator) {
|
|
86
|
+
const styles = useDateSeparatorStyles()
|
|
87
|
+
const { isThisYear } = getRelativeDateStatus(date)
|
|
88
|
+
const showYear = !isThisYear
|
|
89
|
+
const dateStamp = formatDate(date, { style: 'long', year: showYear })
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<View style={styles.container}>
|
|
93
|
+
<View style={styles.separator} />
|
|
94
|
+
<Text variant="footnote" style={styles.dateText}>
|
|
95
|
+
{dateStamp}
|
|
96
|
+
</Text>
|
|
97
|
+
<View style={styles.separator} />
|
|
98
|
+
</View>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const useDateSeparatorStyles = () => {
|
|
103
|
+
const theme = useTheme()
|
|
104
|
+
return StyleSheet.create({
|
|
105
|
+
container: {
|
|
106
|
+
alignItems: 'center',
|
|
107
|
+
flexDirection: 'row',
|
|
108
|
+
paddingHorizontal: 16,
|
|
109
|
+
paddingVertical: 16,
|
|
110
|
+
},
|
|
111
|
+
separator: {
|
|
112
|
+
flex: 1,
|
|
113
|
+
height: 1,
|
|
114
|
+
borderTopWidth: 1,
|
|
115
|
+
borderTopColor: theme.colors.borderColorDefaultBase,
|
|
116
|
+
},
|
|
117
|
+
dateText: {
|
|
118
|
+
paddingHorizontal: 8,
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const groupMessages = (ms: MessageResource[]) => {
|
|
124
|
+
let enrichedMessages: (MessageResource | DateSeparator)[] = []
|
|
125
|
+
let encounteredOneOfMyMessages = false
|
|
126
|
+
|
|
127
|
+
ms.forEach((message, i) => {
|
|
128
|
+
const prevMessage = ms[i + 1]
|
|
129
|
+
const nextMessage = ms[i - 1]
|
|
130
|
+
const date = moment(message.createdAt).format('YYYY-MM-DD')
|
|
131
|
+
const prevMessageDifferentAuthor = message.author?.id !== prevMessage?.author?.id
|
|
132
|
+
const nextMessageDifferentAuthor = message.author?.id !== nextMessage?.author?.id
|
|
133
|
+
const prevMessageMoreThan5Minutes =
|
|
134
|
+
prevMessage &&
|
|
135
|
+
new Date(message.createdAt).getTime() - new Date(prevMessage.createdAt).getTime() > 60000 * 5
|
|
136
|
+
const nextMessageMoreThan5Minutes =
|
|
137
|
+
nextMessage &&
|
|
138
|
+
new Date(nextMessage.createdAt).getTime() - new Date(message.createdAt).getTime() > 60000 * 5
|
|
139
|
+
|
|
140
|
+
if (message.mine && !encounteredOneOfMyMessages) {
|
|
141
|
+
encounteredOneOfMyMessages = true
|
|
142
|
+
message.myLatestInConversation = true
|
|
143
|
+
} else {
|
|
144
|
+
message.myLatestInConversation = false
|
|
145
|
+
}
|
|
146
|
+
message.lastInGroup = !nextMessage || nextMessageDifferentAuthor || nextMessageMoreThan5Minutes
|
|
147
|
+
message.renderAuthor =
|
|
148
|
+
!message.mine && (!prevMessage || prevMessageDifferentAuthor || prevMessageMoreThan5Minutes)
|
|
149
|
+
|
|
150
|
+
enrichedMessages.push(message)
|
|
151
|
+
if (!prevMessage || date !== moment(prevMessage.createdAt).format('YYYY-MM-DD')) {
|
|
152
|
+
enrichedMessages.push({ type: 'DateSeparator', id: `day-divider-${message.id}`, date })
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
return enrichedMessages
|
|
157
|
+
}
|
|
158
|
+
|
|
70
159
|
const PressableHeaderTitle = ({ style, children }: HeaderTitleProps) => {
|
|
71
160
|
const styles = usePressableHeaderStyle()
|
|
72
161
|
const navigation = useNavigation()
|
package/src/utils/date.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ts-
|
|
1
|
+
// @ts-expect-error
|
|
2
2
|
import { date as formatDate } from '@planningcenter/datetime-fmt'
|
|
3
3
|
import moment from 'moment-timezone'
|
|
4
4
|
|
|
@@ -24,3 +24,13 @@ const formatShorterDate = (date: DateProps) => {
|
|
|
24
24
|
// Drop the century from the year
|
|
25
25
|
return formatDate(date, { style: 'short', year: true }).replace(/20(\d{2})/, '$1')
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
export function getRelativeDateStatus(date: DateProps) {
|
|
29
|
+
const now = moment()
|
|
30
|
+
|
|
31
|
+
const isToday = now.isSame(date, 'day')
|
|
32
|
+
const isThisWeek = now.isSame(date, 'week')
|
|
33
|
+
const isThisYear = now.isSame(date, 'year')
|
|
34
|
+
|
|
35
|
+
return { isToday, isThisWeek, isThisYear }
|
|
36
|
+
}
|