@linktr.ee/messaging-react 3.31.0-rc-1785485102 → 3.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/{Card-2l_y20FM.js → Card-CmlaSw7i.js} +2 -2
  2. package/dist/{Card-2l_y20FM.js.map → Card-CmlaSw7i.js.map} +1 -1
  3. package/dist/{Card-Gr4HDVbn.cjs → Card-EWN07XIl.cjs} +2 -2
  4. package/dist/{Card-Gr4HDVbn.cjs.map → Card-EWN07XIl.cjs.map} +1 -1
  5. package/dist/assets/index.css +1 -1
  6. package/dist/{index-B96WLmx6.js → index-CN3errpB.js} +3055 -3109
  7. package/dist/index-CN3errpB.js.map +1 -0
  8. package/dist/index-CcitzVoW.cjs +5 -0
  9. package/dist/index-CcitzVoW.cjs.map +1 -0
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.d.ts +56 -60
  12. package/dist/index.js +1 -1
  13. package/package.json +2 -2
  14. package/src/components/ChannelView.broadcast-name.integration.test.tsx +116 -0
  15. package/src/components/ChannelView.tsx +17 -3
  16. package/src/components/CustomLinkPreviewList/CustomLinkPreviewList.test.tsx +15 -0
  17. package/src/components/CustomLinkPreviewList/index.tsx +24 -26
  18. package/src/components/CustomMessage/BroadcastNameContext.test.ts +43 -0
  19. package/src/components/CustomMessage/BroadcastNameContext.ts +95 -0
  20. package/src/components/CustomMessage/CustomMessage.stories.tsx +54 -22
  21. package/src/components/CustomMessage/CustomMessage.test.tsx +48 -0
  22. package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +62 -0
  23. package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +8 -2
  24. package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +0 -33
  25. package/src/components/CustomMessage/StreamAttachmentMessage.tsx +0 -8
  26. package/src/components/CustomMessage/index.tsx +23 -3
  27. package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +50 -2
  28. package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +180 -2
  29. package/src/components/CustomMessageInput/index.tsx +143 -16
  30. package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +57 -166
  31. package/src/components/MessageAttachment/Audio/index.tsx +77 -71
  32. package/src/components/MessageAttachment/MessageAttachment.test.tsx +75 -175
  33. package/src/components/MessageAttachment/index.tsx +9 -25
  34. package/src/components/MessageAttachment/types.ts +9 -19
  35. package/src/components/MessagingShell/index.tsx +2 -0
  36. package/src/index.ts +1 -0
  37. package/src/styles.css +5 -120
  38. package/src/types.ts +11 -0
  39. package/src/utils/formatRelativeTime.test.ts +63 -57
  40. package/src/utils/formatRelativeTime.ts +21 -20
  41. package/dist/index-B96WLmx6.js.map +0 -1
  42. package/dist/index-CCtHMrmO.cjs +0 -5
  43. package/dist/index-CCtHMrmO.cjs.map +0 -1
  44. package/src/components/MessageAttachment/Audio/WaveformAudioRow.tsx +0 -335
@@ -1,5 +1,8 @@
1
1
  import { formatRelativeTime } from './formatRelativeTime'
2
2
 
3
+ // Date strings deliberately omit the trailing 'Z' so they parse in the
4
+ // runner's local timezone — formatRelativeTime buckets by local calendar
5
+ // days, so this keeps assertions deterministic regardless of ambient TZ.
3
6
  describe('formatRelativeTime', () => {
4
7
  let originalDate: typeof Date
5
8
 
@@ -35,16 +38,16 @@ describe('formatRelativeTime', () => {
35
38
 
36
39
  describe('Just now', () => {
37
40
  it('should return "Just now" for messages less than 1 minute ago', () => {
38
- mockDate('2024-01-15T12:00:00Z')
39
- const date = new Date('2024-01-15T11:59:30Z') // 30 seconds ago
41
+ mockDate('2024-01-15T12:00:00')
42
+ const date = new Date('2024-01-15T11:59:30') // 30 seconds ago
40
43
  expect(formatRelativeTime(date)).toBe('Just now')
41
44
  })
42
45
  })
43
46
 
44
47
  describe('Today', () => {
45
48
  it('should return time in 12-hour format for messages from earlier today', () => {
46
- mockDate('2024-01-15T14:08:00Z')
47
- const date = new Date('2024-01-15T09:30:00Z')
49
+ mockDate('2024-01-15T14:08:00')
50
+ const date = new Date('2024-01-15T09:30:00')
48
51
  const result = formatRelativeTime(date)
49
52
  // Should be "9:30 AM" format
50
53
  expect(result).toMatch(/^\d{1,2}:\d{2} (AM|PM)$/i)
@@ -53,93 +56,96 @@ describe('formatRelativeTime', () => {
53
56
 
54
57
  describe('Yesterday', () => {
55
58
  it('should return "Yesterday" for messages from the previous calendar day', () => {
56
- mockDate('2024-01-15T12:00:00Z')
57
- const date = new Date('2024-01-14T12:00:00Z')
59
+ mockDate('2024-01-15T12:00:00')
60
+ const date = new Date('2024-01-14T12:00:00')
58
61
  expect(formatRelativeTime(date)).toBe('Yesterday')
59
62
  })
60
63
 
61
64
  it('should return "Yesterday" even if less than 24 hours ago but previous day', () => {
62
- mockDate('2024-01-15T01:00:00Z')
63
- const date = new Date('2024-01-14T23:00:00Z') // Only 2 hours ago
65
+ mockDate('2024-01-15T01:00:00')
66
+ const date = new Date('2024-01-14T23:00:00') // Only 2 hours ago
64
67
  expect(formatRelativeTime(date)).toBe('Yesterday')
65
68
  })
66
69
 
67
70
  it('should NOT return "Yesterday" for messages from 2 days ago', () => {
68
- mockDate('2024-01-15T12:00:00Z')
69
- const date = new Date('2024-01-13T12:00:00Z')
71
+ mockDate('2024-01-15T12:00:00')
72
+ const date = new Date('2024-01-13T12:00:00')
70
73
  expect(formatRelativeTime(date)).not.toBe('Yesterday')
71
- expect(formatRelativeTime(date)).toBe('2d')
72
74
  })
73
75
  })
74
76
 
75
- describe('Days ago', () => {
76
- it('should return "2d" for messages 2 days ago', () => {
77
- mockDate('2024-01-15T12:00:00Z')
78
- const date = new Date('2024-01-13T12:00:00Z')
79
- expect(formatRelativeTime(date)).toBe('2d')
77
+ describe('2-6 days ago (short weekday)', () => {
78
+ // The weekday branch uses the runtime locale
79
+ // (toLocaleDateString(undefined, …)), so assertions compare against
80
+ // the same locale-aware formatter rather than fixed English strings.
81
+ it('should return a short weekday name for messages 2 days ago', () => {
82
+ mockDate('2024-01-15T12:00:00')
83
+ const date = new Date('2024-01-13T12:00:00') // Saturday
84
+ expect(formatRelativeTime(date)).toBe(
85
+ date.toLocaleDateString(undefined, { weekday: 'short' })
86
+ )
80
87
  })
81
88
 
82
- it('should return "6d" for messages 6 days ago (boundary before weeks)', () => {
83
- mockDate('2024-01-15T12:00:00Z')
84
- const date = new Date('2024-01-09T12:00:00Z')
85
- expect(formatRelativeTime(date)).toBe('6d')
89
+ it('should return a short weekday name for messages 6 days ago (boundary before dates)', () => {
90
+ mockDate('2024-01-15T12:00:00')
91
+ const date = new Date('2024-01-09T12:00:00') // Tuesday
92
+ expect(formatRelativeTime(date)).toBe(
93
+ date.toLocaleDateString(undefined, { weekday: 'short' })
94
+ )
86
95
  })
87
96
  })
88
97
 
89
- describe('Weeks ago', () => {
90
- it('should return "1w" for messages 1 week ago', () => {
91
- mockDate('2024-01-15T12:00:00Z')
92
- const date = new Date('2024-01-08T12:00:00Z')
93
- expect(formatRelativeTime(date)).toBe('1w')
98
+ describe('7+ days ago, same year (month and day)', () => {
99
+ // Compare against the same locale-aware formatter rather than
100
+ // hard-coded digits, which would fail under non-Latin numbering
101
+ // systems (e.g. ar_EG renders "٨ يناير").
102
+ it('should return month + day without year for messages 7 days ago', () => {
103
+ mockDate('2024-01-15T12:00:00')
104
+ const date = new Date('2024-01-08T12:00:00')
105
+ expect(formatRelativeTime(date)).toBe(
106
+ date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
107
+ )
94
108
  })
95
- })
96
109
 
97
- describe('Date format', () => {
98
- // The absolute-date branch intentionally uses the runtime locale
99
- // (toLocaleDateString(undefined, …)), so assertions avoid a fixed
100
- // string that would depend on the test runner's ambient ICU locale.
101
- // Instead we assert the locale-independent behaviour: a spelled-out
102
- // month (alphabetic) rather than the old all-numeric M/D/YY, plus the
103
- // day and 2-digit year in any order.
104
- const numericDatePattern = /^\d{1,2}\/\d{1,2}\/\d{2}$/
105
-
106
- it('should return a spelled-month date for messages more than 4 weeks ago', () => {
107
- mockDate('2024-02-15T12:00:00Z')
108
- const date = new Date('2024-01-01T12:00:00Z')
109
- const result = formatRelativeTime(date)
110
- expect(result).not.toMatch(numericDatePattern)
111
- expect(result).toMatch(/\p{L}/u) // spelled-out month
112
- expect(result).toMatch(/\b1\b/) // day
113
- expect(result).toMatch(/24/) // 2-digit year
110
+ it('should return month + day without year for messages more than 4 weeks ago in the same year', () => {
111
+ mockDate('2024-02-15T12:00:00')
112
+ const date = new Date('2024-01-01T12:00:00')
113
+ expect(formatRelativeTime(date)).toBe(
114
+ date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
115
+ )
114
116
  })
117
+ })
115
118
 
116
- it('should return a spelled-month date for messages from last year', () => {
117
- mockDate('2024-01-15T12:00:00Z')
118
- const date = new Date('2023-12-01T12:00:00Z')
119
- const result = formatRelativeTime(date)
120
- expect(result).not.toMatch(numericDatePattern)
121
- expect(result).toMatch(/\p{L}/u) // spelled-out month
122
- expect(result).toMatch(/\b1\b/) // day
123
- expect(result).toMatch(/23/) // 2-digit year
119
+ describe('Previous years (month, day and year)', () => {
120
+ it('should include the year for messages from a previous year', () => {
121
+ mockDate('2024-01-15T12:00:00')
122
+ const date = new Date('2023-12-01T12:00:00')
123
+ expect(formatRelativeTime(date)).toBe(
124
+ date.toLocaleDateString(undefined, {
125
+ month: 'short',
126
+ day: 'numeric',
127
+ year: 'numeric',
128
+ })
129
+ )
124
130
  })
125
131
  })
126
132
 
127
133
  describe('Edge cases', () => {
128
134
  it('should handle month boundaries correctly', () => {
129
- mockDate('2024-02-01T12:00:00Z')
130
- const date = new Date('2024-01-31T12:00:00Z')
135
+ mockDate('2024-02-01T12:00:00')
136
+ const date = new Date('2024-01-31T12:00:00')
131
137
  expect(formatRelativeTime(date)).toBe('Yesterday')
132
138
  })
133
139
 
134
140
  it('should handle year boundaries correctly', () => {
135
- mockDate('2024-01-01T12:00:00Z')
136
- const date = new Date('2023-12-31T12:00:00Z')
141
+ mockDate('2024-01-01T12:00:00')
142
+ const date = new Date('2023-12-31T12:00:00')
137
143
  expect(formatRelativeTime(date)).toBe('Yesterday')
138
144
  })
139
145
 
140
146
  it('should handle leap year correctly', () => {
141
- mockDate('2024-03-01T12:00:00Z')
142
- const date = new Date('2024-02-29T12:00:00Z')
147
+ mockDate('2024-03-01T12:00:00')
148
+ const date = new Date('2024-02-29T12:00:00')
143
149
  expect(formatRelativeTime(date)).toBe('Yesterday')
144
150
  })
145
151
  })
@@ -1,21 +1,20 @@
1
1
  /**
2
- * Get the number of days between two dates (calendar days in UTC, not 24-hour periods)
3
- * Uses UTC to ensure consistent day calculations globally since messages are stored in UTC
2
+ * Get the number of days between two dates (calendar days in the viewer's
3
+ * local timezone, not 24-hour periods). Uses local days so the bucket a
4
+ * message falls into always agrees with the locale-formatted output
5
+ * (time of day, weekday name, date), which is also rendered in local time.
4
6
  */
5
7
  const getDaysDifference = (date1: Date, date2: Date): number => {
6
- const d1 = new Date(
7
- Date.UTC(date1.getUTCFullYear(), date1.getUTCMonth(), date1.getUTCDate())
8
- )
9
- const d2 = new Date(
10
- Date.UTC(date2.getUTCFullYear(), date2.getUTCMonth(), date2.getUTCDate())
11
- )
12
- const diffTime = d2.getTime() - d1.getTime()
13
- return Math.floor(diffTime / (1000 * 60 * 60 * 24))
8
+ const d1 = Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate())
9
+ const d2 = Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate())
10
+ return Math.floor((d2 - d1) / (1000 * 60 * 60 * 24))
14
11
  }
15
12
 
16
13
  /**
17
14
  * Format a date - shows time for today, relative time for older messages
18
- * (e.g., "Just now", "2:08 PM" for today, "Yesterday" for yesterday, "2d" for 2 days ago)
15
+ * (e.g., "Just now", "2:08 PM" for today, "Yesterday" for yesterday,
16
+ * "Mon" for 2-6 days ago, "Jul 14" for older dates this year,
17
+ * "Jul 14, 2025" for previous years)
19
18
  */
20
19
  export const formatRelativeTime = (date: Date): string => {
21
20
  const now = new Date()
@@ -43,21 +42,23 @@ export const formatRelativeTime = (date: Date): string => {
43
42
  return 'Yesterday'
44
43
  }
45
44
 
46
- // Less than 7 days - show days
45
+ // 2-6 days ago - show short weekday name (e.g., "Mon")
47
46
  if (daysDiff < 7) {
48
- return `${daysDiff}d`
47
+ return date.toLocaleDateString(undefined, { weekday: 'short' })
49
48
  }
50
49
 
51
- // Less than 4 weeks - show weeks
52
- if (daysDiff < 28) {
53
- const weeks = Math.floor(daysDiff / 7)
54
- return `${weeks}w`
50
+ // 7+ days ago, same local year - show month and day (e.g., "Jul 14")
51
+ if (date.getFullYear() === now.getFullYear()) {
52
+ return date.toLocaleDateString(undefined, {
53
+ month: 'short',
54
+ day: 'numeric',
55
+ })
55
56
  }
56
57
 
57
- // More than 4 weeks - show locale-aware date with spelled-out month
58
+ // Previous years - include the year (e.g., "Jul 14, 2025")
58
59
  return date.toLocaleDateString(undefined, {
59
- day: 'numeric',
60
60
  month: 'short',
61
- year: '2-digit',
61
+ day: 'numeric',
62
+ year: 'numeric',
62
63
  })
63
64
  }