@planningcenter/chat-react-native 4.4.1-qa-staging.9 → 4.4.1-qa-staging.11

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 (49) hide show
  1. package/build/components/conversation/system_message.d.ts.map +1 -1
  2. package/build/components/conversation/system_message.js +85 -74
  3. package/build/components/conversation/system_message.js.map +1 -1
  4. package/build/components/conversations/conversation_preview.js +2 -0
  5. package/build/components/conversations/conversation_preview.js.map +1 -1
  6. package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
  7. package/build/hooks/use_mark_latest_message_read.js +3 -3
  8. package/build/hooks/use_mark_latest_message_read.js.map +1 -1
  9. package/build/types/jolt_events/message_events.d.ts +2 -0
  10. package/build/types/jolt_events/message_events.d.ts.map +1 -1
  11. package/build/types/jolt_events/message_events.js.map +1 -1
  12. package/build/types/resources/conversation.d.ts +1 -1
  13. package/build/types/resources/conversation.d.ts.map +1 -1
  14. package/build/types/resources/conversation.js.map +1 -1
  15. package/build/types/resources/message.d.ts +2 -0
  16. package/build/types/resources/message.d.ts.map +1 -1
  17. package/build/types/resources/message.js.map +1 -1
  18. package/build/utils/date.d.ts +1 -0
  19. package/build/utils/date.d.ts.map +1 -1
  20. package/build/utils/date.js +15 -1
  21. package/build/utils/date.js.map +1 -1
  22. package/build/utils/jolt/transform_message_event_data_to_message_resource.d.ts.map +1 -1
  23. package/build/utils/jolt/transform_message_event_data_to_message_resource.js +2 -1
  24. package/build/utils/jolt/transform_message_event_data_to_message_resource.js.map +1 -1
  25. package/build/utils/request/messages_data_options.d.ts.map +1 -1
  26. package/build/utils/request/messages_data_options.js +1 -1
  27. package/build/utils/request/messages_data_options.js.map +1 -1
  28. package/build/utils/system_messages.d.ts +7 -5
  29. package/build/utils/system_messages.d.ts.map +1 -1
  30. package/build/utils/system_messages.js +10 -2
  31. package/build/utils/system_messages.js.map +1 -1
  32. package/package.json +3 -3
  33. package/src/__tests__/utils/system_messages.ts +19 -32
  34. package/src/components/conversation/__tests__/system_message.test.tsx +104 -0
  35. package/src/components/conversation/system_message.tsx +20 -6
  36. package/src/components/conversations/__tests__/conversation_preview.test.tsx +70 -0
  37. package/src/components/conversations/conversation_preview.tsx +4 -0
  38. package/src/hooks/__tests__/use_mark_latest_message_read.test.tsx +21 -7
  39. package/src/hooks/use_mark_latest_message_read.ts +4 -3
  40. package/src/types/jolt_events/message_events.ts +2 -0
  41. package/src/types/resources/conversation.ts +1 -1
  42. package/src/types/resources/message.ts +3 -0
  43. package/src/utils/__tests__/date.test.ts +19 -0
  44. package/src/utils/__tests__/group_messages.test.ts +1 -0
  45. package/src/utils/date.ts +8 -1
  46. package/src/utils/jolt/__tests__/transform_message_event_data_to_message_resource.test.ts +35 -0
  47. package/src/utils/jolt/transform_message_event_data_to_message_resource.ts +1 -0
  48. package/src/utils/request/messages_data_options.ts +1 -0
  49. package/src/utils/system_messages.ts +19 -10
@@ -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,EAAE,uCAAuC,EAAE,MAAM,+CAA+C,CAAA;AACvG,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,GAAG,IAAI,CAAA;IACnB,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;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IAClC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACzC,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAGnF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,cAAc,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IACnD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAGlC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,uCAAuC,EAAE,CAAA;CACjE"}
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,EAAE,uCAAuC,EAAE,MAAM,+CAA+C,CAAA;AACvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEjD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,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;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IAClC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACzC,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACnF,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAG1C,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,cAAc,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IACnD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAGlC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,uCAAuC,EAAE,CAAA;CACjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"message.js","names":[],"sources":["../../../src/types/resources/message.ts"],"sourcesContent":["import { DenormalizedAttachmentResource } from './denormalized_attachment_resource'\nimport { DenormalizedAttachmentResourceForCreate } from './denormalized_attachment_resource_for_create'\nimport type { PersonResource } from './person'\nimport type { ReactionCountResource } from './reaction'\n\nexport interface MessageResource {\n type: 'Message'\n id: string\n text: string | null\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 replyCount: number\n replyRootId?: string | null\n replyRoot?: MessageResource | null\n idempotentKey?: string | null\n messageType: string\n personIdsForSystemEvent?: number[] | null\n systemTextParts?: { names: string[]; overflowCount: number; action: string } | null\n\n // Custom Local Properties we set for rendering\n renderAuthor?: boolean\n renderTime?: boolean\n myLatestInConversation?: boolean\n lastInGroup?: boolean\n threadPosition?: 'first' | 'center' | 'last' | null\n prevIsMyReply?: boolean\n nextIsMyReply?: boolean\n nextRendersAuthor?: boolean\n isReplyShadowMessage?: boolean\n nextIsReplyShadowMessage?: boolean\n\n // Properties for mutation state\n pending?: boolean // Indicates if the message is optimistically created and pending server response\n error?: string // Error message if the message failed to send\n attachmentsForCreate?: DenormalizedAttachmentResourceForCreate[]\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"message.js","names":[],"sources":["../../../src/types/resources/message.ts"],"sourcesContent":["import { DenormalizedAttachmentResource } from './denormalized_attachment_resource'\nimport { DenormalizedAttachmentResourceForCreate } from './denormalized_attachment_resource_for_create'\nimport type { PersonResource } from './person'\nimport type { ReactionCountResource } from './reaction'\n\nexport type SystemTextFormat = 'people' | 'actor'\n\nexport interface MessageResource {\n type: 'Message'\n id: string\n text: string | null\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 replyCount: number\n replyRootId?: string | null\n replyRoot?: MessageResource | null\n idempotentKey?: string | null\n messageType: string\n personIdsForSystemEvent?: number[] | null\n systemTextParts?: { names: string[]; overflowCount: number; action: string } | null\n systemTextFormat?: SystemTextFormat | null\n\n // Custom Local Properties we set for rendering\n renderAuthor?: boolean\n renderTime?: boolean\n myLatestInConversation?: boolean\n lastInGroup?: boolean\n threadPosition?: 'first' | 'center' | 'last' | null\n prevIsMyReply?: boolean\n nextIsMyReply?: boolean\n nextRendersAuthor?: boolean\n isReplyShadowMessage?: boolean\n nextIsReplyShadowMessage?: boolean\n\n // Properties for mutation state\n pending?: boolean // Indicates if the message is optimistically created and pending server response\n error?: string // Error message if the message failed to send\n attachmentsForCreate?: DenormalizedAttachmentResourceForCreate[]\n}\n"],"mappings":"","ignoreList":[]}
@@ -7,4 +7,5 @@ export declare function getRelativeDateStatus(date: DateProps): {
7
7
  };
8
8
  export declare function relativeDateTime(dateTimeString: string): string;
9
9
  export declare function relativeTime(date: DateProps): string;
10
+ export declare function formatEventDateTime(date: DateProps): string;
10
11
  //# sourceMappingURL=date.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;AAE9C,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,SAAS,UAcjD;AAOD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS;;;;EAQpD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAc/D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAUpD"}
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;AAE9C,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,SAAS,UAcjD;AAOD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS;;;;EAQpD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAc/D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAUpD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,UAKlD"}
@@ -1,4 +1,4 @@
1
- import { date as formatDate } from '@planningcenter/datetime-fmt';
1
+ import { date as formatDate, time as formatTime } from '@planningcenter/datetime-fmt';
2
2
  import dayjs from './dayjs';
3
3
  export function formatDatePreview(date) {
4
4
  if (!date) return '';
@@ -65,4 +65,18 @@ export function relativeTime(date) {
65
65
  return dayjs(date).format('h:mma');
66
66
  }
67
67
  }
68
+ export function formatEventDateTime(date) {
69
+ const {
70
+ isThisYear
71
+ } = getRelativeDateStatus(date);
72
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
73
+ const day = formatDate(date, {
74
+ style: 'abbreviated',
75
+ year: !isThisYear,
76
+ timeZone
77
+ });
78
+ return `${day} at ${formatTime(date, {
79
+ timeZone
80
+ })}`;
81
+ }
68
82
  //# sourceMappingURL=date.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"date.js","names":["date","formatDate","dayjs","formatDatePreview","now","isToday","isSame","isThisWeek","isThisYear","format","style","formatShorterDate","year","replace","getRelativeDateStatus","relativeDateTime","dateTimeString","Date","toDateString","getFullYear","toLocaleTimeString","hour","minute","hour12","toLocaleDateString","month","day","relativeTime","then","duration","diff","asDays","fromNow"],"sources":["../../src/utils/date.ts"],"sourcesContent":["import { date as formatDate } from '@planningcenter/datetime-fmt'\nimport dayjs from './dayjs'\n\nexport type DateProps = string | number | Date\n\nexport function formatDatePreview(date?: DateProps) {\n if (!date) return ''\n\n const now = dayjs()\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 dayjs(date).format('h:mma')\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 = dayjs()\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\nexport function relativeDateTime(dateTimeString: string): string {\n const date = new Date(dateTimeString)\n const now = new Date()\n\n const isToday = date.toDateString() === now.toDateString()\n const isThisYear = date.getFullYear() === now.getFullYear()\n\n if (isToday) {\n return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true })\n } else if (isThisYear) {\n return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n } else {\n return date.toLocaleDateString('en-US')\n }\n}\n\nexport function relativeTime(date: DateProps): string {\n const now = dayjs()\n const then = dayjs(date)\n const duration = dayjs.duration(now.diff(then))\n\n if (duration.asDays() < 1) {\n return then.fromNow()\n } else {\n return dayjs(date).format('h:mma')\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"date.js","names":["date","formatDate","time","formatTime","dayjs","formatDatePreview","now","isToday","isSame","isThisWeek","isThisYear","format","style","formatShorterDate","year","replace","getRelativeDateStatus","relativeDateTime","dateTimeString","Date","toDateString","getFullYear","toLocaleTimeString","hour","minute","hour12","toLocaleDateString","month","day","relativeTime","then","duration","diff","asDays","fromNow","formatEventDateTime","timeZone","Intl","DateTimeFormat","resolvedOptions"],"sources":["../../src/utils/date.ts"],"sourcesContent":["import { date as formatDate, time as formatTime } from '@planningcenter/datetime-fmt'\nimport dayjs from './dayjs'\n\nexport type DateProps = string | number | Date\n\nexport function formatDatePreview(date?: DateProps) {\n if (!date) return ''\n\n const now = dayjs()\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 dayjs(date).format('h:mma')\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 = dayjs()\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\nexport function relativeDateTime(dateTimeString: string): string {\n const date = new Date(dateTimeString)\n const now = new Date()\n\n const isToday = date.toDateString() === now.toDateString()\n const isThisYear = date.getFullYear() === now.getFullYear()\n\n if (isToday) {\n return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true })\n } else if (isThisYear) {\n return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n } else {\n return date.toLocaleDateString('en-US')\n }\n}\n\nexport function relativeTime(date: DateProps): string {\n const now = dayjs()\n const then = dayjs(date)\n const duration = dayjs.duration(now.diff(then))\n\n if (duration.asDays() < 1) {\n return then.fromNow()\n } else {\n return dayjs(date).format('h:mma')\n }\n}\n\nexport function formatEventDateTime(date: DateProps) {\n const { isThisYear } = getRelativeDateStatus(date)\n const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone\n const day = formatDate(date, { style: 'abbreviated', year: !isThisYear, timeZone })\n return `${day} at ${formatTime(date, { timeZone })}`\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"transform_message_event_data_to_message_resource.d.ts","sourceRoot":"","sources":["../../../src/utils/jolt/transform_message_event_data_to_message_resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAI5E,wBAAgB,0CAA0C,CAAC,EACzD,IAAI,EACJ,eAAe,GAChB,EAAE;IACD,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;IACzC,eAAe,EAAE,MAAM,CAAA;CACxB,GAAG,eAAe,CA+BlB"}
1
+ {"version":3,"file":"transform_message_event_data_to_message_resource.d.ts","sourceRoot":"","sources":["../../../src/utils/jolt/transform_message_event_data_to_message_resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAI5E,wBAAgB,0CAA0C,CAAC,EACzD,IAAI,EACJ,eAAe,GAChB,EAAE;IACD,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;IACzC,eAAe,EAAE,MAAM,CAAA;CACxB,GAAG,eAAe,CAgClB"}
@@ -29,7 +29,8 @@ export function transformMessageEventDataToMessageResource({
29
29
  names: data.system_text_parts.names,
30
30
  overflowCount: data.system_text_parts.overflow_count,
31
31
  action: data.system_text_parts.action
32
- } : null
32
+ } : null,
33
+ systemTextFormat: data.system_text_format
33
34
  };
34
35
  }
35
36
  //# sourceMappingURL=transform_message_event_data_to_message_resource.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transform_message_event_data_to_message_resource.js","names":["deepCamelCaseKeys","transformMessageEventDataToMessageResource","data","currentPersonId","type","id","sort_key","text","html","createdAt","created_at","deletedAt","deleted_at","textEditedAt","text_edited_at","mine","author_id","attachments","author","name","author_name","avatar","author_avatar","reactionCounts","replyCount","reply_count","replyRootId","reply_root_id","idempotentKey","idempotent_key","messageType","message_type","personIdsForSystemEvent","system_event_person_ids","systemTextParts","system_text_parts","names","overflowCount","overflow_count","action"],"sources":["../../../src/utils/jolt/transform_message_event_data_to_message_resource.ts"],"sourcesContent":["import { MessageResource } from '../../types'\nimport { MessageCreatedEvent } from '../../types/jolt_events/message_events'\nimport { DenormalizedAttachmentResource } from '../../types/resources/denormalized_attachment_resource'\nimport { deepCamelCaseKeys } from '../deepCamelCaseKeys'\n\nexport function transformMessageEventDataToMessageResource({\n data,\n currentPersonId,\n}: {\n data: MessageCreatedEvent['data']['data']\n currentPersonId: number\n}): MessageResource {\n return {\n type: 'Message',\n id: data.sort_key,\n text: data.text,\n html: data.html,\n createdAt: data.created_at,\n deletedAt: data.deleted_at,\n textEditedAt: data.text_edited_at,\n mine: data.author_id === currentPersonId,\n attachments: deepCamelCaseKeys<DenormalizedAttachmentResource[]>(data.attachments) || [],\n author: {\n type: 'Person',\n id: data.author_id,\n name: data.author_name,\n avatar: data.author_avatar,\n },\n reactionCounts: [],\n replyCount: data.reply_count || 0,\n replyRootId: data.reply_root_id || null,\n idempotentKey: data.idempotent_key ?? null,\n messageType: data.message_type,\n personIdsForSystemEvent: data.system_event_person_ids,\n systemTextParts: data.system_text_parts\n ? {\n names: data.system_text_parts.names,\n overflowCount: data.system_text_parts.overflow_count,\n action: data.system_text_parts.action,\n }\n : null,\n }\n}\n"],"mappings":"AAGA,SAASA,iBAAiB,QAAQ,sBAAsB;AAExD,OAAO,SAASC,0CAA0CA,CAAC;EACzDC,IAAI;EACJC;AAIF,CAAC,EAAmB;EAClB,OAAO;IACLC,IAAI,EAAE,SAAS;IACfC,EAAE,EAAEH,IAAI,CAACI,QAAQ;IACjBC,IAAI,EAAEL,IAAI,CAACK,IAAI;IACfC,IAAI,EAAEN,IAAI,CAACM,IAAI;IACfC,SAAS,EAAEP,IAAI,CAACQ,UAAU;IAC1BC,SAAS,EAAET,IAAI,CAACU,UAAU;IAC1BC,YAAY,EAAEX,IAAI,CAACY,cAAc;IACjCC,IAAI,EAAEb,IAAI,CAACc,SAAS,KAAKb,eAAe;IACxCc,WAAW,EAAEjB,iBAAiB,CAAmCE,IAAI,CAACe,WAAW,CAAC,IAAI,EAAE;IACxFC,MAAM,EAAE;MACNd,IAAI,EAAE,QAAQ;MACdC,EAAE,EAAEH,IAAI,CAACc,SAAS;MAClBG,IAAI,EAAEjB,IAAI,CAACkB,WAAW;MACtBC,MAAM,EAAEnB,IAAI,CAACoB;IACf,CAAC;IACDC,cAAc,EAAE,EAAE;IAClBC,UAAU,EAAEtB,IAAI,CAACuB,WAAW,IAAI,CAAC;IACjCC,WAAW,EAAExB,IAAI,CAACyB,aAAa,IAAI,IAAI;IACvCC,aAAa,EAAE1B,IAAI,CAAC2B,cAAc,IAAI,IAAI;IAC1CC,WAAW,EAAE5B,IAAI,CAAC6B,YAAY;IAC9BC,uBAAuB,EAAE9B,IAAI,CAAC+B,uBAAuB;IACrDC,eAAe,EAAEhC,IAAI,CAACiC,iBAAiB,GACnC;MACEC,KAAK,EAAElC,IAAI,CAACiC,iBAAiB,CAACC,KAAK;MACnCC,aAAa,EAAEnC,IAAI,CAACiC,iBAAiB,CAACG,cAAc;MACpDC,MAAM,EAAErC,IAAI,CAACiC,iBAAiB,CAACI;IACjC,CAAC,GACD;EACN,CAAC;AACH","ignoreList":[]}
1
+ {"version":3,"file":"transform_message_event_data_to_message_resource.js","names":["deepCamelCaseKeys","transformMessageEventDataToMessageResource","data","currentPersonId","type","id","sort_key","text","html","createdAt","created_at","deletedAt","deleted_at","textEditedAt","text_edited_at","mine","author_id","attachments","author","name","author_name","avatar","author_avatar","reactionCounts","replyCount","reply_count","replyRootId","reply_root_id","idempotentKey","idempotent_key","messageType","message_type","personIdsForSystemEvent","system_event_person_ids","systemTextParts","system_text_parts","names","overflowCount","overflow_count","action","systemTextFormat","system_text_format"],"sources":["../../../src/utils/jolt/transform_message_event_data_to_message_resource.ts"],"sourcesContent":["import { MessageResource } from '../../types'\nimport { MessageCreatedEvent } from '../../types/jolt_events/message_events'\nimport { DenormalizedAttachmentResource } from '../../types/resources/denormalized_attachment_resource'\nimport { deepCamelCaseKeys } from '../deepCamelCaseKeys'\n\nexport function transformMessageEventDataToMessageResource({\n data,\n currentPersonId,\n}: {\n data: MessageCreatedEvent['data']['data']\n currentPersonId: number\n}): MessageResource {\n return {\n type: 'Message',\n id: data.sort_key,\n text: data.text,\n html: data.html,\n createdAt: data.created_at,\n deletedAt: data.deleted_at,\n textEditedAt: data.text_edited_at,\n mine: data.author_id === currentPersonId,\n attachments: deepCamelCaseKeys<DenormalizedAttachmentResource[]>(data.attachments) || [],\n author: {\n type: 'Person',\n id: data.author_id,\n name: data.author_name,\n avatar: data.author_avatar,\n },\n reactionCounts: [],\n replyCount: data.reply_count || 0,\n replyRootId: data.reply_root_id || null,\n idempotentKey: data.idempotent_key ?? null,\n messageType: data.message_type,\n personIdsForSystemEvent: data.system_event_person_ids,\n systemTextParts: data.system_text_parts\n ? {\n names: data.system_text_parts.names,\n overflowCount: data.system_text_parts.overflow_count,\n action: data.system_text_parts.action,\n }\n : null,\n systemTextFormat: data.system_text_format,\n }\n}\n"],"mappings":"AAGA,SAASA,iBAAiB,QAAQ,sBAAsB;AAExD,OAAO,SAASC,0CAA0CA,CAAC;EACzDC,IAAI;EACJC;AAIF,CAAC,EAAmB;EAClB,OAAO;IACLC,IAAI,EAAE,SAAS;IACfC,EAAE,EAAEH,IAAI,CAACI,QAAQ;IACjBC,IAAI,EAAEL,IAAI,CAACK,IAAI;IACfC,IAAI,EAAEN,IAAI,CAACM,IAAI;IACfC,SAAS,EAAEP,IAAI,CAACQ,UAAU;IAC1BC,SAAS,EAAET,IAAI,CAACU,UAAU;IAC1BC,YAAY,EAAEX,IAAI,CAACY,cAAc;IACjCC,IAAI,EAAEb,IAAI,CAACc,SAAS,KAAKb,eAAe;IACxCc,WAAW,EAAEjB,iBAAiB,CAAmCE,IAAI,CAACe,WAAW,CAAC,IAAI,EAAE;IACxFC,MAAM,EAAE;MACNd,IAAI,EAAE,QAAQ;MACdC,EAAE,EAAEH,IAAI,CAACc,SAAS;MAClBG,IAAI,EAAEjB,IAAI,CAACkB,WAAW;MACtBC,MAAM,EAAEnB,IAAI,CAACoB;IACf,CAAC;IACDC,cAAc,EAAE,EAAE;IAClBC,UAAU,EAAEtB,IAAI,CAACuB,WAAW,IAAI,CAAC;IACjCC,WAAW,EAAExB,IAAI,CAACyB,aAAa,IAAI,IAAI;IACvCC,aAAa,EAAE1B,IAAI,CAAC2B,cAAc,IAAI,IAAI;IAC1CC,WAAW,EAAE5B,IAAI,CAAC6B,YAAY;IAC9BC,uBAAuB,EAAE9B,IAAI,CAAC+B,uBAAuB;IACrDC,eAAe,EAAEhC,IAAI,CAACiC,iBAAiB,GACnC;MACEC,KAAK,EAAElC,IAAI,CAACiC,iBAAiB,CAACC,KAAK;MACnCC,aAAa,EAAEnC,IAAI,CAACiC,iBAAiB,CAACG,cAAc;MACpDC,MAAM,EAAErC,IAAI,CAACiC,iBAAiB,CAACI;IACjC,CAAC,GACD,IAAI;IACRC,gBAAgB,EAAEtC,IAAI,CAACuC;EACzB,CAAC;AACH","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"messages_data_options.d.ts","sourceRoot":"","sources":["../../../src/utils/request/messages_data_options.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB;;;;CAmB5B,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAmE,CAAA"}
1
+ {"version":3,"file":"messages_data_options.d.ts","sourceRoot":"","sources":["../../../src/utils/request/messages_data_options.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB;;;;CAoB5B,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAmE,CAAA"}
@@ -1,5 +1,5 @@
1
1
  export const getMessageFields = {
2
- Message: ['text', 'text_edited_at', 'mine', 'attachments', 'created_at', 'deleted_at', 'author', 'reaction_counts', 'reply_count', 'reply_root', 'idempotent_key', 'message_type', 'person_ids_for_system_event', 'system_text_parts'],
2
+ Message: ['text', 'text_edited_at', 'mine', 'attachments', 'created_at', 'deleted_at', 'author', 'reaction_counts', 'reply_count', 'reply_root', 'idempotent_key', 'message_type', 'person_ids_for_system_event', 'system_text_parts', 'system_text_format'],
3
3
  Person: ['name', 'avatar'],
4
4
  ReactionCount: ['value', 'count', 'mine', 'message_id', 'author_ids']
5
5
  };
@@ -1 +1 @@
1
- {"version":3,"file":"messages_data_options.js","names":["getMessageFields","Message","Person","ReactionCount","getMessagesInclude"],"sources":["../../../src/utils/request/messages_data_options.ts"],"sourcesContent":["export const getMessageFields = {\n Message: [\n 'text',\n 'text_edited_at',\n 'mine',\n 'attachments',\n 'created_at',\n 'deleted_at',\n 'author',\n 'reaction_counts',\n 'reply_count',\n 'reply_root',\n 'idempotent_key',\n 'message_type',\n 'person_ids_for_system_event',\n 'system_text_parts',\n ],\n Person: ['name', 'avatar'],\n ReactionCount: ['value', 'count', 'mine', 'message_id', 'author_ids'],\n}\n\nexport const getMessagesInclude = ['author', 'reaction_counts', 'reply_root', 'reply_root.author']\n"],"mappings":"AAAA,OAAO,MAAMA,gBAAgB,GAAG;EAC9BC,OAAO,EAAE,CACP,MAAM,EACN,gBAAgB,EAChB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,6BAA6B,EAC7B,mBAAmB,CACpB;EACDC,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;EAC1BC,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY;AACtE,CAAC;AAED,OAAO,MAAMC,kBAAkB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,EAAE,YAAY,EAAE,mBAAmB,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"messages_data_options.js","names":["getMessageFields","Message","Person","ReactionCount","getMessagesInclude"],"sources":["../../../src/utils/request/messages_data_options.ts"],"sourcesContent":["export const getMessageFields = {\n Message: [\n 'text',\n 'text_edited_at',\n 'mine',\n 'attachments',\n 'created_at',\n 'deleted_at',\n 'author',\n 'reaction_counts',\n 'reply_count',\n 'reply_root',\n 'idempotent_key',\n 'message_type',\n 'person_ids_for_system_event',\n 'system_text_parts',\n 'system_text_format',\n ],\n Person: ['name', 'avatar'],\n ReactionCount: ['value', 'count', 'mine', 'message_id', 'author_ids'],\n}\n\nexport const getMessagesInclude = ['author', 'reaction_counts', 'reply_root', 'reply_root.author']\n"],"mappings":"AAAA,OAAO,MAAMA,gBAAgB,GAAG;EAC9BC,OAAO,EAAE,CACP,MAAM,EACN,gBAAgB,EAChB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,6BAA6B,EAC7B,mBAAmB,EACnB,oBAAoB,CACrB;EACDC,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;EAC1BC,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY;AACtE,CAAC;AAED,OAAO,MAAMC,kBAAkB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,EAAE,YAAY,EAAE,mBAAmB,CAAC","ignoreList":[]}
@@ -1,8 +1,6 @@
1
1
  import type { MessageResource } from '../types';
2
- declare const SYSTEM_MESSAGE_TYPES: readonly ["user_joined"];
3
- type SystemMessageType = (typeof SYSTEM_MESSAGE_TYPES)[number];
4
- export interface SystemMessageResource extends MessageResource {
5
- messageType: SystemMessageType;
2
+ export interface PeopleSystemMessageResource extends MessageResource {
3
+ systemTextFormat: 'people';
6
4
  systemTextParts: {
7
5
  names: string[];
8
6
  overflowCount: number;
@@ -10,6 +8,10 @@ export interface SystemMessageResource extends MessageResource {
10
8
  };
11
9
  personIdsForSystemEvent: number[];
12
10
  }
11
+ export interface ActorSystemMessageResource extends MessageResource {
12
+ systemTextFormat: 'actor';
13
+ text: string;
14
+ }
15
+ export type SystemMessageResource = PeopleSystemMessageResource | ActorSystemMessageResource;
13
16
  export declare const isSystemMessage: (message: MessageResource) => message is SystemMessageResource;
14
- export {};
15
17
  //# sourceMappingURL=system_messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"system_messages.d.ts","sourceRoot":"","sources":["../../src/utils/system_messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,QAAA,MAAM,oBAAoB,0BAA2B,CAAA;AAErD,KAAK,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9D,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,WAAW,EAAE,iBAAiB,CAAA;IAC9B,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3E,uBAAuB,EAAE,MAAM,EAAE,CAAA;CAClC;AAED,eAAO,MAAM,eAAe,GAAI,SAAS,eAAe,KAAG,OAAO,IAAI,qBAG7B,CAAA"}
1
+ {"version":3,"file":"system_messages.d.ts","sourceRoot":"","sources":["../../src/utils/system_messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,MAAM,WAAW,2BAA4B,SAAQ,eAAe;IAClE,gBAAgB,EAAE,QAAQ,CAAA;IAC1B,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3E,uBAAuB,EAAE,MAAM,EAAE,CAAA;CAClC;AAED,MAAM,WAAW,0BAA2B,SAAQ,eAAe;IACjE,gBAAgB,EAAE,OAAO,CAAA;IACzB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,GAAG,0BAA0B,CAAA;AAE5F,eAAO,MAAM,eAAe,GAAI,SAAS,eAAe,KAAG,OAAO,IAAI,qBASrE,CAAA"}
@@ -1,3 +1,11 @@
1
- const SYSTEM_MESSAGE_TYPES = ['user_joined'];
2
- export const isSystemMessage = message => SYSTEM_MESSAGE_TYPES.includes(message.messageType) && message.systemTextParts != null && message.personIdsForSystemEvent != null;
1
+ export const isSystemMessage = message => {
2
+ switch (message.systemTextFormat) {
3
+ case 'people':
4
+ return message.systemTextParts != null && message.personIdsForSystemEvent != null;
5
+ case 'actor':
6
+ return !!message.text;
7
+ default:
8
+ return false;
9
+ }
10
+ };
3
11
  //# sourceMappingURL=system_messages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"system_messages.js","names":["SYSTEM_MESSAGE_TYPES","isSystemMessage","message","includes","messageType","systemTextParts","personIdsForSystemEvent"],"sources":["../../src/utils/system_messages.ts"],"sourcesContent":["import type { MessageResource } from '../types'\n\nconst SYSTEM_MESSAGE_TYPES = ['user_joined'] as const\n\ntype SystemMessageType = (typeof SYSTEM_MESSAGE_TYPES)[number]\n\nexport interface SystemMessageResource extends MessageResource {\n messageType: SystemMessageType\n systemTextParts: { names: string[]; overflowCount: number; action: string }\n personIdsForSystemEvent: number[]\n}\n\nexport const isSystemMessage = (message: MessageResource): message is SystemMessageResource =>\n SYSTEM_MESSAGE_TYPES.includes(message.messageType as SystemMessageType) &&\n message.systemTextParts != null &&\n message.personIdsForSystemEvent != null\n"],"mappings":"AAEA,MAAMA,oBAAoB,GAAG,CAAC,aAAa,CAAU;AAUrD,OAAO,MAAMC,eAAe,GAAIC,OAAwB,IACtDF,oBAAoB,CAACG,QAAQ,CAACD,OAAO,CAACE,WAAgC,CAAC,IACvEF,OAAO,CAACG,eAAe,IAAI,IAAI,IAC/BH,OAAO,CAACI,uBAAuB,IAAI,IAAI","ignoreList":[]}
1
+ {"version":3,"file":"system_messages.js","names":["isSystemMessage","message","systemTextFormat","systemTextParts","personIdsForSystemEvent","text"],"sources":["../../src/utils/system_messages.ts"],"sourcesContent":["import type { MessageResource } from '../types'\n\nexport interface PeopleSystemMessageResource extends MessageResource {\n systemTextFormat: 'people'\n systemTextParts: { names: string[]; overflowCount: number; action: string }\n personIdsForSystemEvent: number[]\n}\n\nexport interface ActorSystemMessageResource extends MessageResource {\n systemTextFormat: 'actor'\n text: string\n}\n\nexport type SystemMessageResource = PeopleSystemMessageResource | ActorSystemMessageResource\n\nexport const isSystemMessage = (message: MessageResource): message is SystemMessageResource => {\n switch (message.systemTextFormat) {\n case 'people':\n return message.systemTextParts != null && message.personIdsForSystemEvent != null\n case 'actor':\n return !!message.text\n default:\n return false\n }\n}\n"],"mappings":"AAeA,OAAO,MAAMA,eAAe,GAAIC,OAAwB,IAAuC;EAC7F,QAAQA,OAAO,CAACC,gBAAgB;IAC9B,KAAK,QAAQ;MACX,OAAOD,OAAO,CAACE,eAAe,IAAI,IAAI,IAAIF,OAAO,CAACG,uBAAuB,IAAI,IAAI;IACnF,KAAK,OAAO;MACV,OAAO,CAAC,CAACH,OAAO,CAACI,IAAI;IACvB;MACE,OAAO,KAAK;EAChB;AACF,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "4.4.1-qa-staging.9",
3
+ "version": "4.4.1-qa-staging.11",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
35
35
  "@fortawesome/react-native-fontawesome": "^0.3.2",
36
- "@planningcenter/emoji-keyboard": "4.4.1-qa-staging.9",
36
+ "@planningcenter/emoji-keyboard": "4.4.1-qa-staging.11",
37
37
  "lodash-inflection": "^1.5.0"
38
38
  },
39
39
  "peerDependencies": {
@@ -84,5 +84,5 @@
84
84
  "sf-symbols-typescript": "^2.2.0",
85
85
  "typescript": "~6.0.3"
86
86
  },
87
- "gitHead": "8c317c49799d8e8fc39e9ec68b8fd38317a039cc"
87
+ "gitHead": "f32cabf4c0fcc893fd82b53d6b780bf1371a930b"
88
88
  }
@@ -18,62 +18,49 @@ const makeMessage = (overrides: Partial<MessageResource> = {}): MessageResource
18
18
  messageType: 'user',
19
19
  personIdsForSystemEvent: null,
20
20
  systemTextParts: null,
21
+ systemTextFormat: null,
21
22
  ...overrides,
22
23
  })
23
24
 
25
+ const joinedParts = { names: ['Alice'], overflowCount: 0, action: 'joined the conversation' }
26
+
24
27
  describe('isSystemMessage', () => {
25
- it('returns true for user_joined messages', () => {
28
+ it('returns true for people messages with names and person ids', () => {
26
29
  const message = makeMessage({
27
30
  messageType: 'user_joined',
31
+ systemTextFormat: 'people',
28
32
  personIdsForSystemEvent: [1],
29
- systemTextParts: { names: ['Alice'], overflowCount: 0, action: 'joined the conversation' },
33
+ systemTextParts: joinedParts,
30
34
  })
31
35
  expect(isSystemMessage(message)).toBe(true)
32
36
  })
33
37
 
34
- it('narrows the type so systemTextParts and personIdsForSystemEvent are non-null', () => {
38
+ it('returns true for actor messages of a type the client does not know', () => {
35
39
  const message = makeMessage({
36
- messageType: 'user_joined',
37
- personIdsForSystemEvent: [1, 2],
38
- systemTextParts: {
39
- names: ['Alice', 'Bob'],
40
- overflowCount: 0,
41
- action: 'joined the conversation',
42
- },
40
+ messageType: 'some_future_type',
41
+ systemTextFormat: 'actor',
42
+ text: 'This conversation was archived by Alice',
43
43
  })
44
- if (isSystemMessage(message)) {
45
- expect(message.systemTextParts.names).toEqual(['Alice', 'Bob'])
46
- expect(message.personIdsForSystemEvent).toEqual([1, 2])
47
- } else {
48
- throw new Error('Expected isSystemMessage to return true')
49
- }
44
+ expect(isSystemMessage(message)).toBe(true)
50
45
  })
51
46
 
52
- it('returns false for regular user messages', () => {
53
- const message = makeMessage({ messageType: 'user' })
47
+ it('returns false for messages without a system text format', () => {
48
+ const message = makeMessage({ messageType: 'user_joined', systemTextParts: joinedParts })
54
49
  expect(isSystemMessage(message)).toBe(false)
55
50
  })
56
51
 
57
- it('returns false for unknown message types', () => {
58
- const message = makeMessage({ messageType: 'some_future_type' })
52
+ it('returns false for people messages without systemTextParts', () => {
53
+ const message = makeMessage({ systemTextFormat: 'people', personIdsForSystemEvent: [1] })
59
54
  expect(isSystemMessage(message)).toBe(false)
60
55
  })
61
56
 
62
- it('returns false when systemTextParts is null', () => {
63
- const message = makeMessage({
64
- messageType: 'user_joined',
65
- personIdsForSystemEvent: [1],
66
- systemTextParts: null,
67
- })
57
+ it('returns false for people messages without personIdsForSystemEvent', () => {
58
+ const message = makeMessage({ systemTextFormat: 'people', systemTextParts: joinedParts })
68
59
  expect(isSystemMessage(message)).toBe(false)
69
60
  })
70
61
 
71
- it('returns false when personIdsForSystemEvent is null', () => {
72
- const message = makeMessage({
73
- messageType: 'user_joined',
74
- personIdsForSystemEvent: null,
75
- systemTextParts: { names: ['Alice'], overflowCount: 0, action: 'joined the conversation' },
76
- })
62
+ it.each([null, ''])('returns false for actor messages with text %p', text => {
63
+ const message = makeMessage({ systemTextFormat: 'actor', text })
77
64
  expect(isSystemMessage(message)).toBe(false)
78
65
  })
79
66
  })
@@ -0,0 +1,104 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2
+ import { render } from '@testing-library/react-native'
3
+ import React from 'react'
4
+ import { buildTestQueryClient } from '../../../__utils__/query_client'
5
+ import { ChatProvider } from '../../../contexts/chat_context'
6
+ import type { PersonResource } from '../../../types/resources/person'
7
+ import type { SystemMessageResource } from '../../../utils/system_messages'
8
+ import { SystemMessage } from '../system_message'
9
+
10
+ jest.mock('@react-navigation/native', () => ({
11
+ useNavigation: () => ({ navigate: jest.fn() }),
12
+ }))
13
+ jest.mock('../../../hooks/use_animated_message_background_color', () => ({
14
+ useAnimatedMessageBackgroundColor: () => ({
15
+ animatedBackgroundColor: {},
16
+ handleMessagePressIn: jest.fn(),
17
+ handleMessagePressOut: jest.fn(),
18
+ }),
19
+ }))
20
+
21
+ const author = { id: 1, type: 'Person', name: 'Wes', avatar: null } as unknown as PersonResource
22
+
23
+ const systemMessage = (overrides: Partial<SystemMessageResource>): SystemMessageResource =>
24
+ ({
25
+ type: 'Message',
26
+ id: '01',
27
+ text: null,
28
+ html: '',
29
+ createdAt: '2026-02-03T14:18:00',
30
+ deletedAt: null,
31
+ textEditedAt: null,
32
+ mine: false,
33
+ attachments: [],
34
+ author,
35
+ reactionCounts: [],
36
+ replyCount: 0,
37
+ replyRootId: null,
38
+ messageType: 'user_joined',
39
+ personIdsForSystemEvent: null,
40
+ systemTextParts: null,
41
+ ...overrides,
42
+ }) as SystemMessageResource
43
+
44
+ let queryClient: QueryClient
45
+
46
+ const Wrapper = ({ children }: { children: React.ReactNode }) => (
47
+ <QueryClientProvider client={queryClient}>
48
+ <ChatProvider
49
+ value={{
50
+ token: {
51
+ access_token: '',
52
+ created_at: 0,
53
+ expires_in: 0,
54
+ scope: '',
55
+ refresh_token: '',
56
+ token_type: '',
57
+ },
58
+ onUnauthorizedResponse: () => {},
59
+ theme: { theme: { colors: { interaction: 'rgb(0, 0, 255)' } } },
60
+ }}
61
+ >
62
+ {children}
63
+ </ChatProvider>
64
+ </QueryClientProvider>
65
+ )
66
+
67
+ const renderSystemMessage = (message: SystemMessageResource) =>
68
+ render(<SystemMessage message={message} conversationId={1} />, { wrapper: Wrapper })
69
+
70
+ beforeEach(() => {
71
+ jest.useFakeTimers({ now: new Date(2026, 5, 15, 12, 0) })
72
+ queryClient = buildTestQueryClient()
73
+ })
74
+
75
+ afterEach(() => {
76
+ queryClient.clear()
77
+ jest.useRealTimers()
78
+ })
79
+
80
+ describe('SystemMessage', () => {
81
+ it('renders actor messages as their text followed by the local date and time', () => {
82
+ const view = renderSystemMessage(
83
+ systemMessage({
84
+ messageType: 'conversation_archived',
85
+ systemTextFormat: 'actor',
86
+ text: 'This conversation was archived by Wes',
87
+ })
88
+ )
89
+
90
+ expect(view.getByText('This conversation was archived by Wes on Feb 3 at 2:18pm')).toBeTruthy()
91
+ })
92
+
93
+ it('builds people messages from their names', () => {
94
+ const view = renderSystemMessage(
95
+ systemMessage({
96
+ systemTextFormat: 'people',
97
+ systemTextParts: { names: ['Ann', 'Bo'], overflowCount: 0, action: 'joined' },
98
+ personIdsForSystemEvent: [1, 2],
99
+ })
100
+ )
101
+
102
+ expect(view.getByText('Ann and Bo joined')).toBeTruthy()
103
+ })
104
+ })
@@ -4,6 +4,7 @@ import { Pressable, StyleSheet, View } from 'react-native'
4
4
  import Animated from 'react-native-reanimated'
5
5
  import { useAnimatedMessageBackgroundColor, useTheme } from '../../hooks'
6
6
  import { ReactionCountResource } from '../../types/resources/reaction'
7
+ import { formatEventDateTime } from '../../utils/date'
7
8
  import { Haptic } from '../../utils/native_adapters'
8
9
  import { SystemMessageResource } from '../../utils/system_messages'
9
10
  import { Text } from '../display'
@@ -15,21 +16,23 @@ interface SystemMessageProps {
15
16
  }
16
17
 
17
18
  export function SystemMessage({ message, conversationId }: SystemMessageProps) {
18
- const { reactionCounts, systemTextParts, personIdsForSystemEvent } = message
19
- const { names, overflowCount, action } = systemTextParts
19
+ const { reactionCounts } = message
20
20
  const styles = useStyles()
21
21
  const navigation = useNavigation()
22
22
  const { colors } = useTheme()
23
- const text = systemMessageText(names, overflowCount, action)
24
- const isTruncated = overflowCount > 0
23
+ const text = systemMessageText(message)
24
+ const isTruncated =
25
+ message.systemTextFormat === 'people' && message.systemTextParts.overflowCount > 0
25
26
  const hasReactions = reactionCounts.length > 0
26
27
  const { animatedBackgroundColor, handleMessagePressIn, handleMessagePressOut } =
27
28
  useAnimatedMessageBackgroundColor()
28
29
 
29
30
  const handlePress = () => {
31
+ if (message.systemTextFormat !== 'people') return
32
+
30
33
  navigation.navigate('SystemMessagePeople', {
31
34
  conversation_id: conversationId,
32
- person_ids: personIdsForSystemEvent,
35
+ person_ids: message.personIdsForSystemEvent,
33
36
  })
34
37
  }
35
38
 
@@ -87,7 +90,18 @@ export function SystemMessage({ message, conversationId }: SystemMessageProps) {
87
90
  )
88
91
  }
89
92
 
90
- function systemMessageText(names: string[], overflowCount: number, action: string): string {
93
+ function systemMessageText(message: SystemMessageResource): string {
94
+ switch (message.systemTextFormat) {
95
+ case 'people': {
96
+ const { names, overflowCount, action } = message.systemTextParts
97
+ return peopleSystemMessageText(names, overflowCount, action)
98
+ }
99
+ case 'actor':
100
+ return `${message.text} on ${formatEventDateTime(message.createdAt)}`
101
+ }
102
+ }
103
+
104
+ function peopleSystemMessageText(names: string[], overflowCount: number, action: string): string {
91
105
  if (overflowCount > 0) {
92
106
  const overflowLabel = `${overflowCount} ${overflowCount === 1 ? 'other' : 'others'}`
93
107
  return `${names.join(', ')}, and ${overflowLabel} ${action}`
@@ -0,0 +1,70 @@
1
+ import { render } from '@testing-library/react-native'
2
+ import React from 'react'
3
+ import type { ConversationPreviewData, ConversationResource } from '../../../types'
4
+ import { formatDatePreview } from '../../../utils/date'
5
+ import { ConversationPreview } from '../conversation_preview'
6
+
7
+ jest.mock('../conversation_actions', () => ({
8
+ ConversationActions: ({
9
+ children,
10
+ accessibilityLabel,
11
+ }: {
12
+ children: React.ReactNode
13
+ accessibilityLabel?: string
14
+ }) => {
15
+ const { View: MockView } = jest.requireActual('react-native')
16
+ return <MockView accessibilityLabel={accessibilityLabel}>{children}</MockView>
17
+ },
18
+ }))
19
+
20
+ const createdAt = '2026-03-04T15:30:00Z'
21
+
22
+ const conversation = (conversationPreview: ConversationPreviewData): ConversationResource => ({
23
+ type: 'Conversation',
24
+ id: 7,
25
+ title: 'Worship Team',
26
+ conversationPreview,
27
+ createdAt,
28
+ lastMessageCreatedAt: createdAt,
29
+ muted: false,
30
+ repliesDisabled: false,
31
+ unreadCount: 0,
32
+ unreadReaction: false,
33
+ updatedAt: createdAt,
34
+ })
35
+
36
+ const renderPreview = (preview: ConversationPreviewData) =>
37
+ render(<ConversationPreview conversation={conversation(preview)} onPress={jest.fn()} />)
38
+
39
+ describe('ConversationPreview', () => {
40
+ it('prefixes message previews with the author', () => {
41
+ const view = renderPreview({
42
+ kind: 'message',
43
+ authorName: 'Ada Lovelace',
44
+ value: null,
45
+ text: 'See you Sunday',
46
+ createdAt,
47
+ })
48
+
49
+ expect(view.getByText('Ada Lovelace: See you Sunday')).toBeTruthy()
50
+ })
51
+
52
+ it('shows system previews as their text alone', () => {
53
+ const text = 'This conversation was unarchived by Ada Lovelace'
54
+ const view = renderPreview({
55
+ kind: 'system',
56
+ authorName: 'System',
57
+ value: null,
58
+ text,
59
+ createdAt,
60
+ })
61
+
62
+ expect(view.getByText(text)).toBeTruthy()
63
+ expect(view.queryByText('System', { exact: false })).toBeNull()
64
+ expect(
65
+ view.getByLabelText(
66
+ `Select Worship Team, Latest activity: ${text} at ${formatDatePreview(createdAt)}`
67
+ )
68
+ ).toBeTruthy()
69
+ })
70
+ })
@@ -168,6 +168,8 @@ function reactionEmoji(value: ConversationPreviewData['value']): string | null {
168
168
  }
169
169
 
170
170
  function formatConversationPreviewText(preview: ConversationPreviewData) {
171
+ if (preview.kind === 'system') return preview.text
172
+
171
173
  if (preview.kind === 'reaction') {
172
174
  const emoji = reactionEmoji(preview.value)
173
175
  return `${preview.authorName}${emoji ? ` ${emoji}` : ' reacted'} to: "${preview.text}"`
@@ -179,6 +181,8 @@ function formatConversationPreviewText(preview: ConversationPreviewData) {
179
181
  function formatConversationPreviewAccessibilityLabel(preview: ConversationPreviewData) {
180
182
  const date = formatDatePreview(preview.createdAt)
181
183
 
184
+ if (preview.kind === 'system') return `Latest activity: ${preview.text} at ${date}`
185
+
182
186
  if (preview.kind === 'reaction') {
183
187
  return `${preview.authorName} reacted to your message sent ${date} and says ${preview.text}`
184
188
  }
@@ -1,4 +1,4 @@
1
- import { renderHook } from '@testing-library/react-native'
1
+ import { act, renderHook } from '@testing-library/react-native'
2
2
  import type { ConversationResource } from '../../types'
3
3
  import { useMarkLatestMessageRead } from '../use_mark_latest_message_read'
4
4
 
@@ -19,15 +19,29 @@ describe('useMarkLatestMessageRead', () => {
19
19
  expect(mockMarkRead).toHaveBeenCalledTimes(1)
20
20
  })
21
21
 
22
- it('waits while disabled and marks read once enabled', () => {
23
- const { rerender } = renderHook<void, { enabled: boolean }>(
24
- ({ enabled }) => useMarkLatestMessageRead({ conversation, messages: [], enabled }),
25
- { initialProps: { enabled: false } }
22
+ it('marks read once on mount even while disabled', () => {
23
+ renderHook(() => useMarkLatestMessageRead({ conversation, messages: [], enabled: false }))
24
+
25
+ expect(mockMarkRead).toHaveBeenCalledTimes(1)
26
+ })
27
+
28
+ it('ignores unread bumps while disabled and marks read once enabled', () => {
29
+ jest.useFakeTimers()
30
+ const { rerender } = renderHook<void, { conversation: ConversationResource; enabled: boolean }>(
31
+ props => useMarkLatestMessageRead({ ...props, messages: [] }),
32
+ { initialProps: { conversation: { ...conversation, unreadCount: 0 }, enabled: false } }
26
33
  )
27
- expect(mockMarkRead).not.toHaveBeenCalled()
34
+ expect(mockMarkRead).toHaveBeenCalledTimes(1)
28
35
 
29
- rerender({ enabled: true })
36
+ rerender({ conversation: { ...conversation, unreadCount: 2 }, enabled: false })
37
+ act(() => jest.advanceTimersByTime(1000))
30
38
 
31
39
  expect(mockMarkRead).toHaveBeenCalledTimes(1)
40
+
41
+ rerender({ conversation: { ...conversation, unreadCount: 2 }, enabled: true })
42
+ act(() => jest.advanceTimersByTime(1000))
43
+
44
+ expect(mockMarkRead).toHaveBeenCalledTimes(2)
45
+ jest.useRealTimers()
32
46
  })
33
47
  })
@@ -20,15 +20,16 @@ export function useMarkLatestMessageRead({ conversation, enabled = true }: Props
20
20
  const unreadCount = conversation.unreadCount
21
21
  const unreadReaction = conversation.unreadReaction
22
22
 
23
- const shouldMarkRead = Boolean(unreadCount >= 1 || unreadReaction || !firedOnce.current)
23
+ const shouldMarkRead =
24
+ !firedOnce.current || (enabled && Boolean(unreadCount >= 1 || unreadReaction))
24
25
  const appState = useAppState()
25
26
  const isActive = appState === 'active'
26
27
 
27
28
  useEffect(() => {
28
- if (!enabled || !isActive || !shouldMarkRead) return
29
+ if (!isActive || !shouldMarkRead) return
29
30
 
30
31
  firedOnce.current = true
31
32
 
32
33
  debouncedMarkRead(true)
33
- }, [debouncedMarkRead, enabled, isActive, shouldMarkRead, unreadReaction])
34
+ }, [debouncedMarkRead, isActive, shouldMarkRead, unreadReaction])
34
35
  }