@linktr.ee/messaging-react 1.5.3 → 1.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -433,7 +433,7 @@ const ChannelViewInner: React.FC<{
433
433
 
434
434
  {/* Show custom empty state when no messages */}
435
435
  {!hasMessages && CustomChannelEmptyState && (
436
- <div className="absolute inset-0 flex items-start justify-center pt-8 bg-white">
436
+ <div className="absolute inset-0 flex bg-white">
437
437
  <CustomChannelEmptyState />
438
438
  </div>
439
439
  )}
@@ -49,6 +49,8 @@ Default.args = {
49
49
  ],
50
50
  onFaqClick: (id) => console.log('FAQ clicked:', id),
51
51
  headerText: 'Tap to send a suggested question by Brie Parson:',
52
+ avatarName: 'Brie Parson',
53
+ avatarImage: 'https://i.pravatar.cc/150?img=47',
52
54
  }
53
55
 
54
56
  export const WithoutHeader: StoryFn<ComponentProps> = Template.bind({})
@@ -135,3 +137,50 @@ Empty.args = {
135
137
  faqs: [],
136
138
  onFaqClick: (id) => console.log('FAQ clicked:', id),
137
139
  }
140
+
141
+ export const WithAvatarOnly: StoryFn<ComponentProps> = Template.bind({})
142
+ WithAvatarOnly.args = {
143
+ faqs: [
144
+ {
145
+ id: '1',
146
+ question: 'Book club slots',
147
+ answer: 'We ship worldwide...',
148
+ enabled: true,
149
+ order: 1,
150
+ },
151
+ {
152
+ id: '2',
153
+ question: 'Refunds',
154
+ answer: 'You can track...',
155
+ enabled: true,
156
+ order: 2,
157
+ },
158
+ ],
159
+ onFaqClick: (id) => console.log('FAQ clicked:', id),
160
+ headerText: 'Frequently Asked Questions',
161
+ avatarName: 'Account Owner',
162
+ }
163
+
164
+ export const WithAvatarAndImage: StoryFn<ComponentProps> = Template.bind({})
165
+ WithAvatarAndImage.args = {
166
+ faqs: [
167
+ {
168
+ id: '1',
169
+ question: 'How do I contact support?',
170
+ answer: 'You can reach us...',
171
+ enabled: true,
172
+ order: 1,
173
+ },
174
+ {
175
+ id: '2',
176
+ question: 'What are your hours?',
177
+ answer: 'We are available...',
178
+ enabled: true,
179
+ order: 2,
180
+ },
181
+ ],
182
+ onFaqClick: (id) => console.log('FAQ clicked:', id),
183
+ headerText: 'Questions for Sarah',
184
+ avatarName: 'Sarah Johnson',
185
+ avatarImage: 'https://i.pravatar.cc/150?img=28',
186
+ }
@@ -19,10 +19,11 @@ export const FaqListItem: React.FC<FaqListItemProps> = ({
19
19
  type="button"
20
20
  onClick={onClick}
21
21
  disabled={loading}
22
+ style={{ backgroundColor: '#E6E5E3' }}
22
23
  className={classNames(
23
- 'w-full text-center p-4 rounded-xl bg-chalk text-charcoal font-medium transition-colors',
24
+ 'w-full text-center p-4 rounded-xl text-charcoal font-medium transition-colors',
24
25
  {
25
- 'hover:bg-sand active:bg-sand': !loading,
26
+ 'hover:brightness-95 active:brightness-90': !loading,
26
27
  'opacity-50 cursor-not-allowed': loading,
27
28
  },
28
29
  className
@@ -1,6 +1,8 @@
1
1
  import classNames from 'classnames'
2
2
  import React from 'react'
3
3
 
4
+ import { Avatar } from '../Avatar'
5
+
4
6
  import { FaqListItem } from './FaqListItem'
5
7
 
6
8
  export interface Faq {
@@ -17,6 +19,8 @@ export interface FaqListProps {
17
19
  loadingFaqId?: string | null
18
20
  headerText?: string
19
21
  className?: string
22
+ avatarImage?: string
23
+ avatarName?: string
20
24
  }
21
25
 
22
26
  export const FaqList: React.FC<FaqListProps> = ({
@@ -25,6 +29,8 @@ export const FaqList: React.FC<FaqListProps> = ({
25
29
  loadingFaqId,
26
30
  headerText,
27
31
  className,
32
+ avatarImage,
33
+ avatarName,
28
34
  }) => {
29
35
  const enabledFaqs = faqs
30
36
  .filter((faq) => faq.enabled)
@@ -35,16 +41,38 @@ export const FaqList: React.FC<FaqListProps> = ({
35
41
  }
36
42
 
37
43
  return (
38
- <div className={classNames('px-4 py-6 space-y-3', className)}>
39
- {headerText && <p className="text-md text-charcoal mb-4">{headerText}</p>}
40
- {enabledFaqs.map((faq) => (
41
- <FaqListItem
42
- key={faq.id}
43
- question={faq.question}
44
- onClick={() => onFaqClick(faq.id)}
45
- loading={loadingFaqId === faq.id}
46
- />
47
- ))}
44
+ <div className={classNames('px-4 py-6', className)}>
45
+ <div className="flex gap-3 items-end">
46
+ {/* Avatar at bottom-left, outside grey background */}
47
+ {(avatarImage || avatarName) && (
48
+ <div className="flex-none">
49
+ <Avatar
50
+ id={avatarName || 'account'}
51
+ name={avatarName || 'Account'}
52
+ image={avatarImage}
53
+ size={24}
54
+ />
55
+ </div>
56
+ )}
57
+
58
+ {/* FAQs with grey background */}
59
+ <div
60
+ className="flex-1 rounded-lg p-4 space-y-3"
61
+ style={{ backgroundColor: '#F1F0EE' }}
62
+ >
63
+ {headerText && (
64
+ <p className="text-md text-charcoal mb-4">{headerText}</p>
65
+ )}
66
+ {enabledFaqs.map((faq) => (
67
+ <FaqListItem
68
+ key={faq.id}
69
+ question={faq.question}
70
+ onClick={() => onFaqClick(faq.id)}
71
+ loading={loadingFaqId === faq.id}
72
+ />
73
+ ))}
74
+ </div>
75
+ </div>
48
76
  </div>
49
77
  )
50
78
  }
@@ -24,6 +24,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
24
24
  initialParticipantFilter,
25
25
  initialParticipantData,
26
26
  CustomChannelEmptyState,
27
+ showChannelList = true,
27
28
  }) => {
28
29
  const {
29
30
  service,
@@ -386,14 +387,18 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
386
387
  className={classNames(
387
388
  'messaging-channel-list-sidebar min-h-0 min-w-0 bg-white lg:bg-chalk lg:flex lg:flex-col lg:border-r lg:border-sand',
388
389
  {
389
- // In direct conversation mode, always hide the channel list
390
- '!hidden': directConversationMode,
390
+ // Explicitly hidden via prop or in direct conversation mode
391
+ '!hidden': showChannelList === false || directConversationMode,
391
392
  // Normal mode: hide on mobile when channel selected, show on desktop
392
393
  'hidden lg:flex lg:w-80 lg:min-w-[280px] lg:max-w-[360px]':
393
- !directConversationMode && isChannelSelected,
394
+ showChannelList !== false &&
395
+ !directConversationMode &&
396
+ isChannelSelected,
394
397
  // Normal mode: show when no channel selected
395
398
  'flex flex-col w-full lg:flex-1 lg:max-w-2xl':
396
- !directConversationMode && !isChannelSelected,
399
+ showChannelList !== false &&
400
+ !directConversationMode &&
401
+ !isChannelSelected,
397
402
  }
398
403
  )}
399
404
  >
package/src/types.ts CHANGED
@@ -87,6 +87,13 @@ export interface MessagingShellProps {
87
87
  * Useful for showing FAQs or other contextual information in empty channels.
88
88
  */
89
89
  CustomChannelEmptyState?: React.ComponentType
90
+
91
+ /**
92
+ * Controls whether the channel list is shown. When false, the channel list
93
+ * is immediately hidden. Useful for direct conversation mode where you want
94
+ * to show only the channel view without the list.
95
+ */
96
+ showChannelList?: boolean
90
97
  }
91
98
 
92
99
  /**