@sendbird/uikit-react-native-foundation 3.1.2 → 3.3.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 (254) hide show
  1. package/lib/commonjs/assets/icon/icon-audio-off-filled.png +0 -0
  2. package/lib/commonjs/assets/icon/icon-audio-off-filled@2x.png +0 -0
  3. package/lib/commonjs/assets/icon/icon-audio-off-filled@3x.png +0 -0
  4. package/lib/commonjs/assets/icon/icon-audio-off.png +0 -0
  5. package/lib/commonjs/assets/icon/icon-audio-off@2x.png +0 -0
  6. package/lib/commonjs/assets/icon/icon-audio-off@3x.png +0 -0
  7. package/lib/commonjs/assets/icon/icon-audio-on-filled.png +0 -0
  8. package/lib/commonjs/assets/icon/icon-audio-on-filled@2x.png +0 -0
  9. package/lib/commonjs/assets/icon/icon-audio-on-filled@3x.png +0 -0
  10. package/lib/commonjs/assets/icon/icon-audio-on.png +0 -0
  11. package/lib/commonjs/assets/icon/icon-audio-on@2x.png +0 -0
  12. package/lib/commonjs/assets/icon/icon-audio-on@3x.png +0 -0
  13. package/lib/commonjs/assets/icon/icon-pause.png +0 -0
  14. package/lib/commonjs/assets/icon/icon-pause@2x.png +0 -0
  15. package/lib/commonjs/assets/icon/icon-pause@3x.png +0 -0
  16. package/lib/commonjs/assets/icon/icon-recording.png +0 -0
  17. package/lib/commonjs/assets/icon/icon-recording@2x.png +0 -0
  18. package/lib/commonjs/assets/icon/icon-recording@3x.png +0 -0
  19. package/lib/commonjs/assets/icon/icon-stop.png +0 -0
  20. package/lib/commonjs/assets/icon/icon-stop@2x.png +0 -0
  21. package/lib/commonjs/assets/icon/icon-stop@3x.png +0 -0
  22. package/lib/commonjs/assets/icon/index.js +7 -0
  23. package/lib/commonjs/assets/icon/index.js.map +1 -1
  24. package/lib/commonjs/components/Box/index.js +2 -2
  25. package/lib/commonjs/components/Box/index.js.map +1 -1
  26. package/lib/commonjs/components/Icon/index.js +4 -3
  27. package/lib/commonjs/components/Icon/index.js.map +1 -1
  28. package/lib/commonjs/components/Image/FastImageInternal.js.map +1 -1
  29. package/lib/commonjs/components/Image/index.js.map +1 -1
  30. package/lib/commonjs/components/Modal/index.js +1 -1
  31. package/lib/commonjs/components/Modal/index.js.map +1 -1
  32. package/lib/commonjs/components/PressBox/index.js.map +1 -1
  33. package/lib/commonjs/components/ProgressBar/index.js +72 -0
  34. package/lib/commonjs/components/ProgressBar/index.js.map +1 -0
  35. package/lib/commonjs/components/RegexText/index.js.map +1 -1
  36. package/lib/commonjs/components/TextInput/index.js +10 -3
  37. package/lib/commonjs/components/TextInput/index.js.map +1 -1
  38. package/lib/commonjs/index.js +16 -2
  39. package/lib/commonjs/index.js.map +1 -1
  40. package/lib/commonjs/styles/HeaderStyleContext.js.map +1 -1
  41. package/lib/commonjs/styles/createScaleFactor.js +5 -4
  42. package/lib/commonjs/styles/createScaleFactor.js.map +1 -1
  43. package/lib/commonjs/styles/createStyleSheet.js +4 -0
  44. package/lib/commonjs/styles/createStyleSheet.js.map +1 -1
  45. package/lib/commonjs/theme/DarkUIKitTheme.js +80 -36
  46. package/lib/commonjs/theme/DarkUIKitTheme.js.map +1 -1
  47. package/lib/commonjs/theme/LightUIKitTheme.js +80 -36
  48. package/lib/commonjs/theme/LightUIKitTheme.js.map +1 -1
  49. package/lib/commonjs/types.js.map +1 -1
  50. package/lib/commonjs/ui/Avatar/AvatarStack.js +96 -0
  51. package/lib/commonjs/ui/Avatar/AvatarStack.js.map +1 -0
  52. package/lib/commonjs/ui/Avatar/index.js +6 -3
  53. package/lib/commonjs/ui/Avatar/index.js.map +1 -1
  54. package/lib/commonjs/ui/BottomSheet/index.js.map +1 -1
  55. package/lib/commonjs/ui/Dialog/DialogSheet.js.map +1 -1
  56. package/lib/commonjs/ui/GroupChannelMessage/Message.file.voice.js +108 -0
  57. package/lib/commonjs/ui/GroupChannelMessage/Message.file.voice.js.map +1 -0
  58. package/lib/commonjs/ui/GroupChannelMessage/Message.unknown.js +1 -1
  59. package/lib/commonjs/ui/GroupChannelMessage/Message.unknown.js.map +1 -1
  60. package/lib/commonjs/ui/GroupChannelMessage/index.js +2 -0
  61. package/lib/commonjs/ui/GroupChannelMessage/index.js.map +1 -1
  62. package/lib/commonjs/ui/GroupChannelPreview/index.js.map +1 -1
  63. package/lib/commonjs/ui/Header/index.js +5 -4
  64. package/lib/commonjs/ui/Header/index.js.map +1 -1
  65. package/lib/commonjs/ui/OpenChannelMessage/MessageContainer.js.map +1 -1
  66. package/lib/commonjs/ui/OpenChannelPreview/index.js.map +1 -1
  67. package/lib/commonjs/ui/ProfileCard/index.js.map +1 -1
  68. package/lib/commonjs/ui/TypingIndicatorBubble/index.js +118 -0
  69. package/lib/commonjs/ui/TypingIndicatorBubble/index.js.map +1 -0
  70. package/lib/module/assets/icon/icon-audio-off-filled.png +0 -0
  71. package/lib/module/assets/icon/icon-audio-off-filled@2x.png +0 -0
  72. package/lib/module/assets/icon/icon-audio-off-filled@3x.png +0 -0
  73. package/lib/module/assets/icon/icon-audio-off.png +0 -0
  74. package/lib/module/assets/icon/icon-audio-off@2x.png +0 -0
  75. package/lib/module/assets/icon/icon-audio-off@3x.png +0 -0
  76. package/lib/module/assets/icon/icon-audio-on-filled.png +0 -0
  77. package/lib/module/assets/icon/icon-audio-on-filled@2x.png +0 -0
  78. package/lib/module/assets/icon/icon-audio-on-filled@3x.png +0 -0
  79. package/lib/module/assets/icon/icon-audio-on.png +0 -0
  80. package/lib/module/assets/icon/icon-audio-on@2x.png +0 -0
  81. package/lib/module/assets/icon/icon-audio-on@3x.png +0 -0
  82. package/lib/module/assets/icon/icon-pause.png +0 -0
  83. package/lib/module/assets/icon/icon-pause@2x.png +0 -0
  84. package/lib/module/assets/icon/icon-pause@3x.png +0 -0
  85. package/lib/module/assets/icon/icon-recording.png +0 -0
  86. package/lib/module/assets/icon/icon-recording@2x.png +0 -0
  87. package/lib/module/assets/icon/icon-recording@3x.png +0 -0
  88. package/lib/module/assets/icon/icon-stop.png +0 -0
  89. package/lib/module/assets/icon/icon-stop@2x.png +0 -0
  90. package/lib/module/assets/icon/icon-stop@3x.png +0 -0
  91. package/lib/module/assets/icon/index.js +7 -0
  92. package/lib/module/assets/icon/index.js.map +1 -1
  93. package/lib/module/components/Box/index.js +2 -2
  94. package/lib/module/components/Box/index.js.map +1 -1
  95. package/lib/module/components/Icon/index.js +4 -3
  96. package/lib/module/components/Icon/index.js.map +1 -1
  97. package/lib/module/components/Image/FastImageInternal.js.map +1 -1
  98. package/lib/module/components/Image/index.js.map +1 -1
  99. package/lib/module/components/Modal/index.js +1 -1
  100. package/lib/module/components/Modal/index.js.map +1 -1
  101. package/lib/module/components/PressBox/index.js.map +1 -1
  102. package/lib/module/components/ProgressBar/index.js +62 -0
  103. package/lib/module/components/ProgressBar/index.js.map +1 -0
  104. package/lib/module/components/RegexText/index.js.map +1 -1
  105. package/lib/module/components/TextInput/index.js +10 -3
  106. package/lib/module/components/TextInput/index.js.map +1 -1
  107. package/lib/module/index.js +4 -2
  108. package/lib/module/index.js.map +1 -1
  109. package/lib/module/styles/HeaderStyleContext.js.map +1 -1
  110. package/lib/module/styles/createScaleFactor.js +5 -4
  111. package/lib/module/styles/createScaleFactor.js.map +1 -1
  112. package/lib/module/styles/createStyleSheet.js +4 -0
  113. package/lib/module/styles/createStyleSheet.js.map +1 -1
  114. package/lib/module/theme/DarkUIKitTheme.js +80 -36
  115. package/lib/module/theme/DarkUIKitTheme.js.map +1 -1
  116. package/lib/module/theme/LightUIKitTheme.js +80 -36
  117. package/lib/module/theme/LightUIKitTheme.js.map +1 -1
  118. package/lib/module/types.js.map +1 -1
  119. package/lib/module/ui/Avatar/AvatarStack.js +88 -0
  120. package/lib/module/ui/Avatar/AvatarStack.js.map +1 -0
  121. package/lib/module/ui/Avatar/index.js +6 -3
  122. package/lib/module/ui/Avatar/index.js.map +1 -1
  123. package/lib/module/ui/BottomSheet/index.js.map +1 -1
  124. package/lib/module/ui/Dialog/DialogSheet.js.map +1 -1
  125. package/lib/module/ui/GroupChannelMessage/Message.file.voice.js +98 -0
  126. package/lib/module/ui/GroupChannelMessage/Message.file.voice.js.map +1 -0
  127. package/lib/module/ui/GroupChannelMessage/Message.unknown.js +1 -1
  128. package/lib/module/ui/GroupChannelMessage/Message.unknown.js.map +1 -1
  129. package/lib/module/ui/GroupChannelMessage/index.js +2 -0
  130. package/lib/module/ui/GroupChannelMessage/index.js.map +1 -1
  131. package/lib/module/ui/GroupChannelPreview/index.js.map +1 -1
  132. package/lib/module/ui/Header/index.js +5 -4
  133. package/lib/module/ui/Header/index.js.map +1 -1
  134. package/lib/module/ui/OpenChannelMessage/MessageContainer.js.map +1 -1
  135. package/lib/module/ui/OpenChannelPreview/index.js.map +1 -1
  136. package/lib/module/ui/ProfileCard/index.js.map +1 -1
  137. package/lib/module/ui/TypingIndicatorBubble/index.js +108 -0
  138. package/lib/module/ui/TypingIndicatorBubble/index.js.map +1 -0
  139. package/lib/typescript/src/assets/icon/index.d.ts +7 -0
  140. package/lib/typescript/src/components/Box/index.d.ts +2 -1
  141. package/lib/typescript/src/components/Divider/index.d.ts +2 -1
  142. package/lib/typescript/src/components/Icon/index.d.ts +76 -7
  143. package/lib/typescript/src/components/Image/FastImageInternal.d.ts +2 -1
  144. package/lib/typescript/src/components/Image/index.d.ts +2 -1
  145. package/lib/typescript/src/components/ImageWithPlaceholder/index.d.ts +2 -1
  146. package/lib/typescript/src/components/Modal/index.d.ts +2 -1
  147. package/lib/typescript/src/components/PressBox/index.d.ts +2 -1
  148. package/lib/typescript/src/components/ProgressBar/index.d.ts +12 -0
  149. package/lib/typescript/src/components/RegexText/index.d.ts +3 -2
  150. package/lib/typescript/src/components/Switch/index.d.ts +1 -1
  151. package/lib/typescript/src/components/Text/index.d.ts +2 -1
  152. package/lib/typescript/src/components/VideoThumbnail/index.d.ts +2 -1
  153. package/lib/typescript/src/index.d.ts +5 -3
  154. package/lib/typescript/src/styles/HeaderStyleContext.d.ts +2 -2
  155. package/lib/typescript/src/styles/createScaleFactor.d.ts +4 -5
  156. package/lib/typescript/src/theme/UIKitThemeProvider.d.ts +1 -1
  157. package/lib/typescript/src/types.d.ts +11 -7
  158. package/lib/typescript/src/ui/ActionMenu/index.d.ts +2 -1
  159. package/lib/typescript/src/ui/Alert/index.d.ts +2 -1
  160. package/lib/typescript/src/ui/Avatar/AvatarGroup.d.ts +1 -1
  161. package/lib/typescript/src/ui/Avatar/AvatarIcon.d.ts +2 -1
  162. package/lib/typescript/src/ui/Avatar/AvatarStack.d.ts +16 -0
  163. package/lib/typescript/src/ui/Avatar/index.d.ts +30 -8
  164. package/lib/typescript/src/ui/Badge/index.d.ts +2 -1
  165. package/lib/typescript/src/ui/BottomSheet/index.d.ts +3 -2
  166. package/lib/typescript/src/ui/Button/index.d.ts +1 -1
  167. package/lib/typescript/src/ui/ChannelFrozenBanner/index.d.ts +2 -1
  168. package/lib/typescript/src/ui/Dialog/DialogBox.d.ts +1 -1
  169. package/lib/typescript/src/ui/Dialog/DialogSheet.d.ts +3 -3
  170. package/lib/typescript/src/ui/Dialog/index.d.ts +1 -1
  171. package/lib/typescript/src/ui/GroupChannelMessage/Message.admin.d.ts +2 -1
  172. package/lib/typescript/src/ui/GroupChannelMessage/Message.file.d.ts +2 -1
  173. package/lib/typescript/src/ui/GroupChannelMessage/Message.file.image.d.ts +2 -1
  174. package/lib/typescript/src/ui/GroupChannelMessage/Message.file.video.d.ts +2 -1
  175. package/lib/typescript/src/ui/GroupChannelMessage/Message.file.voice.d.ts +14 -0
  176. package/lib/typescript/src/ui/GroupChannelMessage/Message.unknown.d.ts +2 -1
  177. package/lib/typescript/src/ui/GroupChannelMessage/Message.user.d.ts +2 -1
  178. package/lib/typescript/src/ui/GroupChannelMessage/Message.user.og.d.ts +2 -1
  179. package/lib/typescript/src/ui/GroupChannelMessage/MessageBubbleWithText.d.ts +2 -1
  180. package/lib/typescript/src/ui/GroupChannelMessage/MessageContainer.d.ts +4 -3
  181. package/lib/typescript/src/ui/GroupChannelMessage/MessageOpenGraph.d.ts +2 -1
  182. package/lib/typescript/src/ui/GroupChannelMessage/index.d.ts +43 -7
  183. package/lib/typescript/src/ui/GroupChannelPreview/index.d.ts +3 -3
  184. package/lib/typescript/src/ui/Header/index.d.ts +8 -12
  185. package/lib/typescript/src/ui/LoadingSpinner/index.d.ts +2 -1
  186. package/lib/typescript/src/ui/MenuBar/index.d.ts +1 -1
  187. package/lib/typescript/src/ui/OpenChannelMessage/Message.admin.d.ts +2 -1
  188. package/lib/typescript/src/ui/OpenChannelMessage/Message.file.d.ts +2 -1
  189. package/lib/typescript/src/ui/OpenChannelMessage/Message.file.image.d.ts +2 -1
  190. package/lib/typescript/src/ui/OpenChannelMessage/Message.file.video.d.ts +2 -1
  191. package/lib/typescript/src/ui/OpenChannelMessage/Message.unknown.d.ts +2 -1
  192. package/lib/typescript/src/ui/OpenChannelMessage/Message.user.d.ts +2 -1
  193. package/lib/typescript/src/ui/OpenChannelMessage/Message.user.og.d.ts +2 -1
  194. package/lib/typescript/src/ui/OpenChannelMessage/MessageContainer.d.ts +2 -1
  195. package/lib/typescript/src/ui/OpenChannelMessage/index.d.ts +7 -7
  196. package/lib/typescript/src/ui/OpenChannelPreview/index.d.ts +2 -2
  197. package/lib/typescript/src/ui/OutlinedButton/index.d.ts +2 -1
  198. package/lib/typescript/src/ui/Placeholder/index.d.ts +2 -1
  199. package/lib/typescript/src/ui/ProfileCard/index.d.ts +3 -2
  200. package/lib/typescript/src/ui/Prompt/index.d.ts +2 -1
  201. package/lib/typescript/src/ui/Toast/index.d.ts +2 -2
  202. package/lib/typescript/src/ui/TypingIndicatorBubble/index.d.ts +10 -0
  203. package/package.json +5 -9
  204. package/src/assets/icon/icon-audio-off-filled.png +0 -0
  205. package/src/assets/icon/icon-audio-off-filled@2x.png +0 -0
  206. package/src/assets/icon/icon-audio-off-filled@3x.png +0 -0
  207. package/src/assets/icon/icon-audio-off.png +0 -0
  208. package/src/assets/icon/icon-audio-off@2x.png +0 -0
  209. package/src/assets/icon/icon-audio-off@3x.png +0 -0
  210. package/src/assets/icon/icon-audio-on-filled.png +0 -0
  211. package/src/assets/icon/icon-audio-on-filled@2x.png +0 -0
  212. package/src/assets/icon/icon-audio-on-filled@3x.png +0 -0
  213. package/src/assets/icon/icon-audio-on.png +0 -0
  214. package/src/assets/icon/icon-audio-on@2x.png +0 -0
  215. package/src/assets/icon/icon-audio-on@3x.png +0 -0
  216. package/src/assets/icon/icon-pause.png +0 -0
  217. package/src/assets/icon/icon-pause@2x.png +0 -0
  218. package/src/assets/icon/icon-pause@3x.png +0 -0
  219. package/src/assets/icon/icon-recording.png +0 -0
  220. package/src/assets/icon/icon-recording@2x.png +0 -0
  221. package/src/assets/icon/icon-recording@3x.png +0 -0
  222. package/src/assets/icon/icon-stop.png +0 -0
  223. package/src/assets/icon/icon-stop@2x.png +0 -0
  224. package/src/assets/icon/icon-stop@3x.png +0 -0
  225. package/src/assets/icon/index.ts +7 -0
  226. package/src/components/Box/index.tsx +1 -1
  227. package/src/components/Icon/index.tsx +6 -7
  228. package/src/components/Image/FastImageInternal.tsx +3 -2
  229. package/src/components/Image/index.ts +2 -1
  230. package/src/components/Modal/index.tsx +1 -1
  231. package/src/components/PressBox/index.tsx +1 -0
  232. package/src/components/ProgressBar/index.tsx +72 -0
  233. package/src/components/RegexText/index.tsx +3 -3
  234. package/src/components/TextInput/index.tsx +10 -3
  235. package/src/index.ts +5 -4
  236. package/src/styles/HeaderStyleContext.tsx +1 -1
  237. package/src/styles/createScaleFactor.ts +5 -5
  238. package/src/styles/createStyleSheet.ts +4 -0
  239. package/src/theme/DarkUIKitTheme.ts +80 -37
  240. package/src/theme/LightUIKitTheme.ts +80 -37
  241. package/src/types.ts +31 -7
  242. package/src/ui/Avatar/AvatarStack.tsx +103 -0
  243. package/src/ui/Avatar/index.tsx +8 -14
  244. package/src/ui/BottomSheet/index.tsx +2 -2
  245. package/src/ui/Dialog/DialogSheet.tsx +2 -2
  246. package/src/ui/GroupChannelMessage/Message.file.voice.tsx +123 -0
  247. package/src/ui/GroupChannelMessage/Message.unknown.tsx +1 -1
  248. package/src/ui/GroupChannelMessage/index.ts +7 -0
  249. package/src/ui/GroupChannelPreview/index.tsx +2 -2
  250. package/src/ui/Header/index.tsx +8 -12
  251. package/src/ui/OpenChannelMessage/MessageContainer.tsx +1 -1
  252. package/src/ui/OpenChannelPreview/index.tsx +1 -1
  253. package/src/ui/ProfileCard/index.tsx +2 -2
  254. package/src/ui/TypingIndicatorBubble/index.tsx +120 -0
@@ -0,0 +1,103 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { StyleProp, View, ViewStyle } from 'react-native';
3
+
4
+ import Text from '../../components/Text';
5
+ import useUIKitTheme from '../../theme/useUIKitTheme';
6
+
7
+ const DEFAULT_MAX = 3;
8
+ const DEFAULT_BORDER_WIDTH = 2;
9
+ const DEFAULT_AVATAR_GAP = -4;
10
+ const DEFAULT_AVATAR_SIZE = 26;
11
+ const DEFAULT_REMAINS_MAX = 99;
12
+
13
+ type Props = React.PropsWithChildren<{
14
+ size?: number;
15
+ containerStyle?: StyleProp<ViewStyle>;
16
+ maxAvatar?: number;
17
+ avatarGap?: number;
18
+ styles?: {
19
+ borderWidth?: number;
20
+ borderColor?: string;
21
+ remainsTextColor?: string;
22
+ remainsBackgroundColor?: string;
23
+ };
24
+ }>;
25
+
26
+ const AvatarStack = ({
27
+ children,
28
+ containerStyle,
29
+ styles,
30
+ maxAvatar = DEFAULT_MAX,
31
+ size = DEFAULT_AVATAR_SIZE,
32
+ avatarGap = DEFAULT_AVATAR_GAP,
33
+ }: Props) => {
34
+ const { colors, palette, select } = useUIKitTheme();
35
+ const defaultStyles = {
36
+ borderWidth: DEFAULT_BORDER_WIDTH,
37
+ borderColor: colors.background,
38
+ remainsTextColor: colors.onBackground02,
39
+ remainsBackgroundColor: select({ light: palette.background100, dark: palette.background600 }),
40
+ };
41
+ const avatarStyles = { ...defaultStyles, ...styles };
42
+
43
+ const childrenArray = React.Children.toArray(children).filter((it) => React.isValidElement(it));
44
+ const remains = childrenArray.length - maxAvatar;
45
+ const shouldRenderRemains = remains > 0;
46
+
47
+ const actualSize = size + avatarStyles.borderWidth * 2;
48
+ const actualGap = avatarGap - avatarStyles.borderWidth;
49
+
50
+ const renderAvatars = () => {
51
+ return childrenArray.slice(0, maxAvatar).map((child, index) =>
52
+ React.cloneElement(child as ReactElement, {
53
+ size: actualSize,
54
+ containerStyle: {
55
+ left: actualGap * index,
56
+ borderWidth: avatarStyles.borderWidth,
57
+ borderColor: avatarStyles.borderColor,
58
+ },
59
+ }),
60
+ );
61
+ };
62
+
63
+ const renderRemainsCount = () => {
64
+ if (!shouldRenderRemains) return null;
65
+ return (
66
+ <View
67
+ style={[
68
+ avatarStyles,
69
+ {
70
+ left: actualGap * maxAvatar,
71
+ width: actualSize,
72
+ height: actualSize,
73
+ borderRadius: actualSize / 2,
74
+ alignItems: 'center',
75
+ justifyContent: 'center',
76
+ backgroundColor: avatarStyles.remainsBackgroundColor,
77
+ },
78
+ ]}
79
+ >
80
+ <Text style={{ color: avatarStyles.remainsTextColor, fontSize: 8 }} caption4>
81
+ {`+${Math.min(remains, DEFAULT_REMAINS_MAX)}`}
82
+ </Text>
83
+ </View>
84
+ );
85
+ };
86
+
87
+ const calculateWidth = () => {
88
+ const widthEach = actualSize + actualGap;
89
+ const avatarCountOffset = shouldRenderRemains ? 1 : 0;
90
+ const avatarCount = shouldRenderRemains ? maxAvatar : childrenArray.length;
91
+ const count = avatarCount + avatarCountOffset;
92
+ return widthEach * count + avatarStyles.borderWidth;
93
+ };
94
+
95
+ return (
96
+ <View style={[containerStyle, { left: -avatarStyles.borderWidth, flexDirection: 'row', width: calculateWidth() }]}>
97
+ {renderAvatars()}
98
+ {renderRemainsCount()}
99
+ </View>
100
+ );
101
+ };
102
+
103
+ export default AvatarStack;
@@ -9,11 +9,8 @@ import createStyleSheet from '../../styles/createStyleSheet';
9
9
  import useUIKitTheme from '../../theme/useUIKitTheme';
10
10
  import AvatarGroup from './AvatarGroup';
11
11
  import AvatarIcon from './AvatarIcon';
12
+ import AvatarStack from './AvatarStack';
12
13
 
13
- type SubComponents = {
14
- Group: typeof AvatarGroup;
15
- Icon: typeof AvatarIcon;
16
- };
17
14
  type Props = {
18
15
  uri?: string;
19
16
  size?: number;
@@ -21,13 +18,8 @@ type Props = {
21
18
  muted?: boolean;
22
19
  containerStyle?: StyleProp<ViewStyle>;
23
20
  };
24
- const Avatar: ((props: Props) => JSX.Element) & SubComponents = ({
25
- uri,
26
- square,
27
- muted = false,
28
- size = 56,
29
- containerStyle,
30
- }) => {
21
+
22
+ const Avatar = ({ uri, square, muted = false, size = 56, containerStyle }: Props) => {
31
23
  const { colors, palette } = useUIKitTheme();
32
24
  const [loadFailure, setLoadFailure] = useState(false);
33
25
 
@@ -74,6 +66,8 @@ const styles = createStyleSheet({
74
66
  },
75
67
  });
76
68
 
77
- Avatar.Group = AvatarGroup;
78
- Avatar.Icon = AvatarIcon;
79
- export default Avatar;
69
+ export default Object.assign(Avatar, {
70
+ Group: AvatarGroup,
71
+ Icon: AvatarIcon,
72
+ Stack: AvatarStack,
73
+ });
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
2
  import { TouchableOpacity, useWindowDimensions } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
4
 
@@ -17,7 +17,7 @@ export type BottomSheetItem = {
17
17
  disabled?: boolean;
18
18
  onPress: () => void;
19
19
  }[];
20
- HeaderComponent?: (props: HeaderProps) => JSX.Element;
20
+ HeaderComponent?: (props: HeaderProps) => ReactNode;
21
21
  };
22
22
  type Props = {
23
23
  visible: boolean;
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
2
  import { StyleProp, View, ViewStyle } from 'react-native';
3
3
 
4
4
  import Icon from '../../components/Icon';
@@ -9,7 +9,7 @@ import useUIKitTheme from '../../theme/useUIKitTheme';
9
9
  type Props = React.PropsWithChildren<{
10
10
  style?: StyleProp<ViewStyle>;
11
11
  }>;
12
- const DialogSheet: ((props: Props) => JSX.Element) & { Item: typeof SheetItem } = ({ style, children }) => {
12
+ const DialogSheet: ((props: Props) => ReactNode) & { Item: typeof SheetItem } = ({ style, children }) => {
13
13
  const { colors } = useUIKitTheme();
14
14
  return (
15
15
  <View style={[styles.container, { backgroundColor: colors.ui.dialog.default.none.background }, style]}>
@@ -0,0 +1,123 @@
1
+ import React, { useEffect, useState } from 'react';
2
+
3
+ import type { SendbirdFileMessage } from '@sendbird/uikit-utils';
4
+ import { millsToMSS } from '@sendbird/uikit-utils';
5
+
6
+ import Box from '../../components/Box';
7
+ import Icon from '../../components/Icon';
8
+ import PressBox from '../../components/PressBox';
9
+ import ProgressBar from '../../components/ProgressBar';
10
+ import Text from '../../components/Text';
11
+ import createStyleSheet from '../../styles/createStyleSheet';
12
+ import useUIKitTheme from '../../theme/useUIKitTheme';
13
+ import LoadingSpinner from '../LoadingSpinner';
14
+ import MessageContainer from './MessageContainer';
15
+ import type { GroupChannelMessageProps } from './index';
16
+
17
+ export type VoiceFileMessageState = {
18
+ status: 'preparing' | 'playing' | 'paused';
19
+ currentTime: number;
20
+ duration: number;
21
+ };
22
+
23
+ type Props = GroupChannelMessageProps<
24
+ SendbirdFileMessage,
25
+ {
26
+ durationMetaArrayKey?: string;
27
+ onUnmount: () => void;
28
+ }
29
+ >;
30
+ const VoiceFileMessage = (props: Props) => {
31
+ const {
32
+ onLongPress,
33
+ variant = 'incoming',
34
+ onToggleVoiceMessage,
35
+ message,
36
+ durationMetaArrayKey = 'KEY_VOICE_MESSAGE_DURATION',
37
+ onUnmount,
38
+ } = props;
39
+
40
+ const { colors } = useUIKitTheme();
41
+
42
+ const [state, setState] = useState<VoiceFileMessageState>(() => {
43
+ const meta = message.metaArrays.find((it) => it.key === durationMetaArrayKey);
44
+ const value = meta?.value?.[0];
45
+ const initialDuration = value ? parseInt(value, 10) : 0;
46
+ return {
47
+ status: 'paused',
48
+ currentTime: 0,
49
+ duration: initialDuration,
50
+ };
51
+ });
52
+
53
+ useEffect(() => {
54
+ return () => {
55
+ onUnmount();
56
+ };
57
+ }, []);
58
+
59
+ const uiColors = colors.ui.groupChannelMessage[variant];
60
+ const remainingTime = state.duration - state.currentTime;
61
+
62
+ return (
63
+ <MessageContainer {...props}>
64
+ <Box style={styles.container} backgroundColor={uiColors.enabled.background}>
65
+ <PressBox onPress={() => onToggleVoiceMessage?.(state, setState)} onLongPress={onLongPress}>
66
+ <ProgressBar
67
+ current={state.currentTime}
68
+ total={state.duration}
69
+ style={{ minWidth: 136, height: 44 }}
70
+ trackColor={uiColors.enabled.voiceProgressTrack}
71
+ overlay={
72
+ <Box
73
+ flex={1}
74
+ flexDirection={'row'}
75
+ alignItems={'center'}
76
+ justifyContent={'space-between'}
77
+ paddingHorizontal={12}
78
+ >
79
+ {state.status === 'preparing' ? (
80
+ <LoadingSpinner size={24} color={uiColors.enabled.voiceSpinner} />
81
+ ) : (
82
+ <Icon
83
+ size={16}
84
+ containerStyle={{
85
+ backgroundColor: uiColors.enabled.voiceActionIconBackground,
86
+ padding: 6,
87
+ borderRadius: 16,
88
+ }}
89
+ icon={state.status === 'paused' ? 'play' : 'pause'}
90
+ />
91
+ )}
92
+ <Text
93
+ body3
94
+ style={{ lineHeight: undefined, marginLeft: 6, opacity: 0.88 }}
95
+ color={uiColors.enabled.textVoicePlaytime}
96
+ >
97
+ {millsToMSS(state.currentTime === 0 ? state.duration : remainingTime)}
98
+ </Text>
99
+ </Box>
100
+ }
101
+ />
102
+ </PressBox>
103
+ {props.children}
104
+ </Box>
105
+ </MessageContainer>
106
+ );
107
+ };
108
+
109
+ const styles = createStyleSheet({
110
+ container: {
111
+ borderRadius: 16,
112
+ overflow: 'hidden',
113
+ },
114
+ image: {
115
+ maxWidth: 240,
116
+ width: 240,
117
+ height: 160,
118
+ borderRadius: 16,
119
+ overflow: 'hidden',
120
+ },
121
+ });
122
+
123
+ export default VoiceFileMessage;
@@ -14,7 +14,7 @@ const UnknownMessage = (props: GroupChannelMessageProps<SendbirdMessage>) => {
14
14
  const { variant = 'incoming' } = props;
15
15
 
16
16
  const { colors } = useUIKitTheme();
17
- const color = colors.ui.groupChannelMessage['incoming'];
17
+ const color = colors.ui.groupChannelMessage[variant];
18
18
  const titleColor = variant === 'incoming' ? colors.onBackground01 : colors.onBackgroundReverse01;
19
19
  const descColor = variant === 'incoming' ? colors.onBackground02 : colors.onBackgroundReverse02;
20
20
 
@@ -6,6 +6,8 @@ import AdminMessage from './Message.admin';
6
6
  import FileMessage from './Message.file';
7
7
  import ImageFileMessage from './Message.file.image';
8
8
  import VideoFileMessage from './Message.file.video';
9
+ import VoiceFileMessage from './Message.file.voice';
10
+ import type { VoiceFileMessageState } from './Message.file.voice';
9
11
  import UnknownMessage from './Message.unknown';
10
12
  import UserMessage from './Message.user';
11
13
  import OpenGraphUser from './Message.user.og';
@@ -34,6 +36,10 @@ export type GroupChannelMessageProps<T extends SendbirdMessage, AdditionalProps
34
36
  onPressAvatar?: () => void;
35
37
  onPressURL?: (url: string) => void;
36
38
  onPressMentionedUser?: (mentionedUser?: SendbirdUser) => void;
39
+ onToggleVoiceMessage?: (
40
+ state: VoiceFileMessageState,
41
+ setState: React.Dispatch<React.SetStateAction<VoiceFileMessageState>>,
42
+ ) => Promise<void>;
37
43
  } & AdditionalProps;
38
44
 
39
45
  const GroupChannelMessage = {
@@ -42,6 +48,7 @@ const GroupChannelMessage = {
42
48
  File: FileMessage,
43
49
  ImageFile: ImageFileMessage,
44
50
  VideoFile: VideoFileMessage,
51
+ VoiceFile: VoiceFileMessage,
45
52
  Admin: AdminMessage,
46
53
  Unknown: UnknownMessage,
47
54
  };
@@ -11,13 +11,13 @@ import useUIKitTheme from '../../theme/useUIKitTheme';
11
11
  import Badge from '../Badge';
12
12
 
13
13
  type Props = {
14
- customCover?: React.ReactElement;
14
+ customCover?: React.ReactNode;
15
15
  coverUrl: string;
16
16
 
17
17
  title: string;
18
18
 
19
19
  titleCaption: string;
20
- titleCaptionLeft?: React.ReactElement;
20
+ titleCaptionLeft?: React.ReactNode;
21
21
 
22
22
  bodyIcon?: keyof typeof Icon.Assets;
23
23
  body: string;
@@ -5,12 +5,11 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
5
  import { conditionChaining } from '@sendbird/uikit-utils';
6
6
 
7
7
  import Text, { TextProps } from '../../components/Text';
8
- import type { BaseHeaderProps } from '../../index';
8
+ import type { BaseHeaderProps, HeaderElement } from '../../index';
9
9
  import createStyleSheet from '../../styles/createStyleSheet';
10
10
  import useHeaderStyle from '../../styles/useHeaderStyle';
11
11
  import useUIKitTheme from '../../theme/useUIKitTheme';
12
12
 
13
- type HeaderElement = string | React.ReactElement | null;
14
13
  export type HeaderProps = BaseHeaderProps<
15
14
  {
16
15
  title?: HeaderElement;
@@ -27,11 +26,7 @@ export type HeaderProps = BaseHeaderProps<
27
26
  >;
28
27
 
29
28
  const AlignMapper = { left: 'flex-start', center: 'center', right: 'flex-end' } as const;
30
- const Header: ((props: HeaderProps) => JSX.Element) & {
31
- Button: typeof HeaderButton;
32
- Title: typeof HeaderTitle;
33
- Subtitle: typeof HeaderSubtitle;
34
- } = ({
29
+ const Header = ({
35
30
  children,
36
31
  titleAlign,
37
32
  title = null,
@@ -42,7 +37,7 @@ const Header: ((props: HeaderProps) => JSX.Element) & {
42
37
  clearTitleMargin = false,
43
38
  clearStatusBarTopInset = false,
44
39
  statusBarTopInsetAs = 'padding',
45
- }) => {
40
+ }: HeaderProps) => {
46
41
  const { topInset, defaultTitleAlign, defaultHeight } = useHeaderStyle();
47
42
 
48
43
  const { colors } = useUIKitTheme();
@@ -184,7 +179,8 @@ const styles = createStyleSheet({
184
179
  },
185
180
  });
186
181
 
187
- Header.Button = HeaderButton;
188
- Header.Title = HeaderTitle;
189
- Header.Subtitle = HeaderSubtitle;
190
- export default Header;
182
+ export default Object.assign(Header, {
183
+ Button: HeaderButton,
184
+ Title: HeaderTitle,
185
+ Subtitle: HeaderSubtitle,
186
+ });
@@ -101,7 +101,7 @@ const MessageContainer = ({
101
101
  );
102
102
  };
103
103
 
104
- const SendingStatusContainer = ({ children }: { children: React.ReactElement }) => {
104
+ const SendingStatusContainer = ({ children }: { children: React.ReactNode }) => {
105
105
  return (
106
106
  <Box flexDirection={'row'}>
107
107
  <Box marginTop={2}>{children}</Box>
@@ -10,7 +10,7 @@ import createStyleSheet from '../../styles/createStyleSheet';
10
10
  import useUIKitTheme from '../../theme/useUIKitTheme';
11
11
 
12
12
  type Props = {
13
- customCover?: React.ReactElement;
13
+ customCover?: React.ReactNode;
14
14
  coverUrl: string;
15
15
  title: string;
16
16
  participantsCount?: number;
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
2
  import { StyleProp, View, ViewStyle } from 'react-native';
3
3
 
4
4
  import Divider from '../../components/Divider';
@@ -11,7 +11,7 @@ type Props = {
11
11
  uri: string;
12
12
  username: string;
13
13
 
14
- button?: JSX.Element;
14
+ button?: ReactNode;
15
15
 
16
16
  bodyLabel: string;
17
17
  body: string;
@@ -0,0 +1,120 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Animated, Easing, StyleProp, ViewStyle } from 'react-native';
3
+
4
+ import type { SendbirdUser } from '@sendbird/uikit-utils';
5
+
6
+ import Box from '../../components/Box';
7
+ import createStyleSheet from '../../styles/createStyleSheet';
8
+ import useUIKitTheme from '../../theme/useUIKitTheme';
9
+ import Avatar from '../Avatar';
10
+
11
+ type Props = {
12
+ typingUsers: SendbirdUser[];
13
+ containerStyle?: StyleProp<ViewStyle>;
14
+ maxAvatar?: number;
15
+ };
16
+
17
+ const TypingIndicatorBubble = ({ typingUsers, containerStyle, maxAvatar }: Props) => {
18
+ const { select, palette, colors } = useUIKitTheme();
19
+
20
+ if (typingUsers.length === 0) return null;
21
+
22
+ return (
23
+ <Box flexDirection={'row'} justifyContent={'flex-start'} alignItems={'center'} style={containerStyle}>
24
+ <Avatar.Stack
25
+ size={26}
26
+ maxAvatar={maxAvatar}
27
+ styles={{
28
+ remainsTextColor: colors.onBackground02,
29
+ remainsBackgroundColor: select({ light: palette.background100, dark: palette.background400 }),
30
+ }}
31
+ containerStyle={{ marginRight: 12 }}
32
+ >
33
+ {typingUsers.map((user, index) => (
34
+ <Avatar key={index} uri={user.profileUrl} />
35
+ ))}
36
+ </Avatar.Stack>
37
+ <TypingDots
38
+ dotColor={select({ light: palette.background100, dark: palette.background400 })}
39
+ backgroundColor={colors.onBackground02}
40
+ />
41
+ </Box>
42
+ );
43
+ };
44
+
45
+ type TypingDotsProps = {
46
+ dotColor: string;
47
+ backgroundColor: string;
48
+ };
49
+ const TypingDots = ({ dotColor, backgroundColor }: TypingDotsProps) => {
50
+ const animation = useRef(new Animated.Value(0)).current;
51
+ const dots = matrix.map(([timeline, scale, opacity]) => [
52
+ animation.interpolate({ inputRange: timeline, outputRange: scale, extrapolate: 'clamp' }),
53
+ animation.interpolate({ inputRange: timeline, outputRange: opacity, extrapolate: 'clamp' }),
54
+ ]);
55
+
56
+ useEffect(() => {
57
+ const animated = Animated.loop(
58
+ Animated.timing(animation, { toValue: 1.4, duration: 1400, easing: Easing.linear, useNativeDriver: true }),
59
+ );
60
+ animated.start();
61
+ return () => animated.reset();
62
+ }, []);
63
+
64
+ return (
65
+ <Box
66
+ flexDirection={'row'}
67
+ alignItems={'center'}
68
+ justifyContent={'center'}
69
+ borderRadius={16}
70
+ paddingHorizontal={12}
71
+ height={34}
72
+ backgroundColor={dotColor}
73
+ >
74
+ {dots.map(([scale, opacity], index) => {
75
+ return (
76
+ <Animated.View
77
+ key={index}
78
+ style={[
79
+ styles.dot,
80
+ {
81
+ marginRight: index === dots.length - 1 ? 0 : 6,
82
+ opacity: opacity,
83
+ transform: [{ scale: scale }],
84
+ backgroundColor: backgroundColor,
85
+ },
86
+ ]}
87
+ />
88
+ );
89
+ })}
90
+ </Box>
91
+ );
92
+ };
93
+
94
+ const matrix = [
95
+ [
96
+ [0.4, 0.7, 1.0],
97
+ [1.0, 1.2, 1.0],
98
+ [0.12, 0.38, 0.12],
99
+ ],
100
+ [
101
+ [0.6, 0.9, 1.2],
102
+ [1.0, 1.2, 1.0],
103
+ [0.12, 0.38, 0.12],
104
+ ],
105
+ [
106
+ [0.8, 1.1, 1.4],
107
+ [1.0, 1.2, 1.0],
108
+ [0.12, 0.38, 0.12],
109
+ ],
110
+ ];
111
+
112
+ const styles = createStyleSheet({
113
+ dot: {
114
+ width: 8,
115
+ height: 8,
116
+ borderRadius: 4,
117
+ },
118
+ });
119
+
120
+ export default TypingIndicatorBubble;