@oxy.so/bloom 6.0.0 → 6.2.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/README.md +1 -1
- package/docs/adoption-matrix.mdx +5 -3
- package/docs/chat-composer.mdx +5 -1
- package/docs/cover-header.mdx +56 -0
- package/docs/message-media.mdx +5 -0
- package/docs/sticker.mdx +64 -0
- package/lib/commonjs/ai-profile-card/AiProfileCard.js +186 -263
- package/lib/commonjs/ai-profile-card/AiProfileCard.js.map +1 -1
- package/lib/commonjs/chat-composer/SuggestionList.js +3 -1
- package/lib/commonjs/chat-composer/SuggestionList.js.map +1 -1
- package/lib/commonjs/chat-composer/SuggestionList.web.js +3 -1
- package/lib/commonjs/chat-composer/SuggestionList.web.js.map +1 -1
- package/lib/commonjs/chat-composer/create-suggestion-list.js +64 -10
- package/lib/commonjs/chat-composer/create-suggestion-list.js.map +1 -1
- package/lib/commonjs/chat-composer/messages.js +7 -0
- package/lib/commonjs/chat-composer/messages.js.map +1 -1
- package/lib/commonjs/cover-header/CoverHeader.js +83 -0
- package/lib/commonjs/cover-header/CoverHeader.js.map +1 -0
- package/lib/commonjs/cover-header/CoverImage.js +72 -0
- package/lib/commonjs/cover-header/CoverImage.js.map +1 -0
- package/lib/commonjs/cover-header/index.js +13 -0
- package/lib/commonjs/cover-header/index.js.map +1 -0
- package/lib/commonjs/cover-header/types.js +6 -0
- package/lib/commonjs/cover-header/types.js.map +1 -0
- package/lib/commonjs/index.js +972 -938
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +982 -948
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/locale/translations/ar.js +7 -0
- package/lib/commonjs/locale/translations/ar.js.map +1 -1
- package/lib/commonjs/locale/translations/bn.js +7 -0
- package/lib/commonjs/locale/translations/bn.js.map +1 -1
- package/lib/commonjs/locale/translations/ca.js +7 -0
- package/lib/commonjs/locale/translations/ca.js.map +1 -1
- package/lib/commonjs/locale/translations/de.js +7 -0
- package/lib/commonjs/locale/translations/de.js.map +1 -1
- package/lib/commonjs/locale/translations/es.js +7 -0
- package/lib/commonjs/locale/translations/es.js.map +1 -1
- package/lib/commonjs/locale/translations/fr.js +7 -0
- package/lib/commonjs/locale/translations/fr.js.map +1 -1
- package/lib/commonjs/locale/translations/hi.js +7 -0
- package/lib/commonjs/locale/translations/hi.js.map +1 -1
- package/lib/commonjs/locale/translations/id.js +7 -0
- package/lib/commonjs/locale/translations/id.js.map +1 -1
- package/lib/commonjs/locale/translations/it.js +7 -0
- package/lib/commonjs/locale/translations/it.js.map +1 -1
- package/lib/commonjs/locale/translations/ja.js +7 -0
- package/lib/commonjs/locale/translations/ja.js.map +1 -1
- package/lib/commonjs/locale/translations/pt.js +7 -0
- package/lib/commonjs/locale/translations/pt.js.map +1 -1
- package/lib/commonjs/locale/translations/ru.js +7 -0
- package/lib/commonjs/locale/translations/ru.js.map +1 -1
- package/lib/commonjs/locale/translations/tr.js +7 -0
- package/lib/commonjs/locale/translations/tr.js.map +1 -1
- package/lib/commonjs/locale/translations/zh.js +7 -0
- package/lib/commonjs/locale/translations/zh.js.map +1 -1
- package/lib/commonjs/message-media/StickerMessage.js +11 -2
- package/lib/commonjs/message-media/StickerMessage.js.map +1 -1
- package/lib/commonjs/sticker/Sticker.js +14 -0
- package/lib/commonjs/sticker/Sticker.js.map +1 -0
- package/lib/commonjs/sticker/Sticker.web.js +14 -0
- package/lib/commonjs/sticker/Sticker.web.js.map +1 -0
- package/lib/commonjs/sticker/create-sticker.js +118 -0
- package/lib/commonjs/sticker/create-sticker.js.map +1 -0
- package/lib/commonjs/sticker/index.js +26 -0
- package/lib/commonjs/sticker/index.js.map +1 -0
- package/lib/commonjs/sticker/index.web.js +26 -0
- package/lib/commonjs/sticker/index.web.js.map +1 -0
- package/lib/commonjs/sticker/lottie-module.js +97 -0
- package/lib/commonjs/sticker/lottie-module.js.map +1 -0
- package/lib/commonjs/sticker/lottie-module.web.js +96 -0
- package/lib/commonjs/sticker/lottie-module.web.js.map +1 -0
- package/lib/commonjs/sticker/types.js +6 -0
- package/lib/commonjs/sticker/types.js.map +1 -0
- package/lib/module/ai-profile-card/AiProfileCard.js +185 -262
- package/lib/module/ai-profile-card/AiProfileCard.js.map +1 -1
- package/lib/module/chat-composer/SuggestionList.js +3 -1
- package/lib/module/chat-composer/SuggestionList.js.map +1 -1
- package/lib/module/chat-composer/SuggestionList.web.js +3 -1
- package/lib/module/chat-composer/SuggestionList.web.js.map +1 -1
- package/lib/module/chat-composer/create-suggestion-list.js +64 -10
- package/lib/module/chat-composer/create-suggestion-list.js.map +1 -1
- package/lib/module/chat-composer/messages.js +7 -0
- package/lib/module/chat-composer/messages.js.map +1 -1
- package/lib/module/cover-header/CoverHeader.js +78 -0
- package/lib/module/cover-header/CoverHeader.js.map +1 -0
- package/lib/module/cover-header/CoverImage.js +68 -0
- package/lib/module/cover-header/CoverImage.js.map +1 -0
- package/lib/module/cover-header/index.js +4 -0
- package/lib/module/cover-header/index.js.map +1 -0
- package/lib/module/cover-header/types.js +4 -0
- package/lib/module/cover-header/types.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +2 -0
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/locale/translations/ar.js +7 -0
- package/lib/module/locale/translations/ar.js.map +1 -1
- package/lib/module/locale/translations/bn.js +7 -0
- package/lib/module/locale/translations/bn.js.map +1 -1
- package/lib/module/locale/translations/ca.js +7 -0
- package/lib/module/locale/translations/ca.js.map +1 -1
- package/lib/module/locale/translations/de.js +7 -0
- package/lib/module/locale/translations/de.js.map +1 -1
- package/lib/module/locale/translations/es.js +7 -0
- package/lib/module/locale/translations/es.js.map +1 -1
- package/lib/module/locale/translations/fr.js +7 -0
- package/lib/module/locale/translations/fr.js.map +1 -1
- package/lib/module/locale/translations/hi.js +7 -0
- package/lib/module/locale/translations/hi.js.map +1 -1
- package/lib/module/locale/translations/id.js +7 -0
- package/lib/module/locale/translations/id.js.map +1 -1
- package/lib/module/locale/translations/it.js +7 -0
- package/lib/module/locale/translations/it.js.map +1 -1
- package/lib/module/locale/translations/ja.js +7 -0
- package/lib/module/locale/translations/ja.js.map +1 -1
- package/lib/module/locale/translations/pt.js +7 -0
- package/lib/module/locale/translations/pt.js.map +1 -1
- package/lib/module/locale/translations/ru.js +7 -0
- package/lib/module/locale/translations/ru.js.map +1 -1
- package/lib/module/locale/translations/tr.js +7 -0
- package/lib/module/locale/translations/tr.js.map +1 -1
- package/lib/module/locale/translations/zh.js +7 -0
- package/lib/module/locale/translations/zh.js.map +1 -1
- package/lib/module/message-media/StickerMessage.js +11 -2
- package/lib/module/message-media/StickerMessage.js.map +1 -1
- package/lib/module/sticker/Sticker.js +11 -0
- package/lib/module/sticker/Sticker.js.map +1 -0
- package/lib/module/sticker/Sticker.web.js +11 -0
- package/lib/module/sticker/Sticker.web.js.map +1 -0
- package/lib/module/sticker/create-sticker.js +113 -0
- package/lib/module/sticker/create-sticker.js.map +1 -0
- package/lib/module/sticker/index.js +5 -0
- package/lib/module/sticker/index.js.map +1 -0
- package/lib/module/sticker/index.web.js +10 -0
- package/lib/module/sticker/index.web.js.map +1 -0
- package/lib/module/sticker/lottie-module.js +90 -0
- package/lib/module/sticker/lottie-module.js.map +1 -0
- package/lib/module/sticker/lottie-module.web.js +88 -0
- package/lib/module/sticker/lottie-module.web.js.map +1 -0
- package/lib/module/sticker/types.js +4 -0
- package/lib/module/sticker/types.js.map +1 -0
- package/lib/typescript/commonjs/ai-profile-card/AiProfileCard.d.ts.map +1 -1
- package/lib/typescript/commonjs/chat-composer/SuggestionList.d.ts +1 -1
- package/lib/typescript/commonjs/chat-composer/SuggestionList.d.ts.map +1 -1
- package/lib/typescript/commonjs/chat-composer/SuggestionList.web.d.ts +1 -1
- package/lib/typescript/commonjs/chat-composer/SuggestionList.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/chat-composer/create-suggestion-list.d.ts +4 -2
- package/lib/typescript/commonjs/chat-composer/create-suggestion-list.d.ts.map +1 -1
- package/lib/typescript/commonjs/chat-composer/messages.d.ts +6 -0
- package/lib/typescript/commonjs/chat-composer/messages.d.ts.map +1 -1
- package/lib/typescript/commonjs/chat-composer/types.d.ts +17 -0
- package/lib/typescript/commonjs/chat-composer/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/cover-header/CoverHeader.d.ts +21 -0
- package/lib/typescript/commonjs/cover-header/CoverHeader.d.ts.map +1 -0
- package/lib/typescript/commonjs/cover-header/CoverImage.d.ts +11 -0
- package/lib/typescript/commonjs/cover-header/CoverImage.d.ts.map +1 -0
- package/lib/typescript/commonjs/cover-header/index.d.ts +3 -0
- package/lib/typescript/commonjs/cover-header/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/cover-header/types.d.ts +33 -0
- package/lib/typescript/commonjs/cover-header/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +3 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +3 -0
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/ar.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/bn.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/ca.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/de.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/es.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/fr.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/hi.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/id.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/it.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/ja.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/pt.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/ru.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/tr.d.ts.map +1 -1
- package/lib/typescript/commonjs/locale/translations/zh.d.ts.map +1 -1
- package/lib/typescript/commonjs/message-media/StickerMessage.d.ts +3 -2
- package/lib/typescript/commonjs/message-media/StickerMessage.d.ts.map +1 -1
- package/lib/typescript/commonjs/message-media/types.d.ts +3 -0
- package/lib/typescript/commonjs/message-media/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/sticker/Sticker.d.ts +3 -0
- package/lib/typescript/commonjs/sticker/Sticker.d.ts.map +1 -0
- package/lib/typescript/commonjs/sticker/Sticker.web.d.ts +3 -0
- package/lib/typescript/commonjs/sticker/Sticker.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/sticker/create-sticker.d.ts +23 -0
- package/lib/typescript/commonjs/sticker/create-sticker.d.ts.map +1 -0
- package/lib/typescript/commonjs/sticker/index.d.ts +4 -0
- package/lib/typescript/commonjs/sticker/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/sticker/index.web.d.ts +4 -0
- package/lib/typescript/commonjs/sticker/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/sticker/lottie-module.d.ts +23 -0
- package/lib/typescript/commonjs/sticker/lottie-module.d.ts.map +1 -0
- package/lib/typescript/commonjs/sticker/lottie-module.web.d.ts +18 -0
- package/lib/typescript/commonjs/sticker/lottie-module.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/sticker/types.d.ts +43 -0
- package/lib/typescript/commonjs/sticker/types.d.ts.map +1 -0
- package/lib/typescript/module/ai-profile-card/AiProfileCard.d.ts.map +1 -1
- package/lib/typescript/module/chat-composer/SuggestionList.d.ts +1 -1
- package/lib/typescript/module/chat-composer/SuggestionList.d.ts.map +1 -1
- package/lib/typescript/module/chat-composer/SuggestionList.web.d.ts +1 -1
- package/lib/typescript/module/chat-composer/SuggestionList.web.d.ts.map +1 -1
- package/lib/typescript/module/chat-composer/create-suggestion-list.d.ts +4 -2
- package/lib/typescript/module/chat-composer/create-suggestion-list.d.ts.map +1 -1
- package/lib/typescript/module/chat-composer/messages.d.ts +6 -0
- package/lib/typescript/module/chat-composer/messages.d.ts.map +1 -1
- package/lib/typescript/module/chat-composer/types.d.ts +17 -0
- package/lib/typescript/module/chat-composer/types.d.ts.map +1 -1
- package/lib/typescript/module/cover-header/CoverHeader.d.ts +21 -0
- package/lib/typescript/module/cover-header/CoverHeader.d.ts.map +1 -0
- package/lib/typescript/module/cover-header/CoverImage.d.ts +11 -0
- package/lib/typescript/module/cover-header/CoverImage.d.ts.map +1 -0
- package/lib/typescript/module/cover-header/index.d.ts +3 -0
- package/lib/typescript/module/cover-header/index.d.ts.map +1 -0
- package/lib/typescript/module/cover-header/types.d.ts +33 -0
- package/lib/typescript/module/cover-header/types.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +3 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +3 -0
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/ar.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/bn.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/ca.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/de.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/es.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/fr.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/hi.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/id.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/it.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/ja.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/pt.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/ru.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/tr.d.ts.map +1 -1
- package/lib/typescript/module/locale/translations/zh.d.ts.map +1 -1
- package/lib/typescript/module/message-media/StickerMessage.d.ts +3 -2
- package/lib/typescript/module/message-media/StickerMessage.d.ts.map +1 -1
- package/lib/typescript/module/message-media/types.d.ts +3 -0
- package/lib/typescript/module/message-media/types.d.ts.map +1 -1
- package/lib/typescript/module/sticker/Sticker.d.ts +3 -0
- package/lib/typescript/module/sticker/Sticker.d.ts.map +1 -0
- package/lib/typescript/module/sticker/Sticker.web.d.ts +3 -0
- package/lib/typescript/module/sticker/Sticker.web.d.ts.map +1 -0
- package/lib/typescript/module/sticker/create-sticker.d.ts +23 -0
- package/lib/typescript/module/sticker/create-sticker.d.ts.map +1 -0
- package/lib/typescript/module/sticker/index.d.ts +4 -0
- package/lib/typescript/module/sticker/index.d.ts.map +1 -0
- package/lib/typescript/module/sticker/index.web.d.ts +4 -0
- package/lib/typescript/module/sticker/index.web.d.ts.map +1 -0
- package/lib/typescript/module/sticker/lottie-module.d.ts +23 -0
- package/lib/typescript/module/sticker/lottie-module.d.ts.map +1 -0
- package/lib/typescript/module/sticker/lottie-module.web.d.ts +18 -0
- package/lib/typescript/module/sticker/lottie-module.web.d.ts.map +1 -0
- package/lib/typescript/module/sticker/types.d.ts +43 -0
- package/lib/typescript/module/sticker/types.d.ts.map +1 -0
- package/package.json +48 -1
- package/src/ai-profile-card/AiProfileCard.tsx +159 -241
- package/src/chat-composer/SuggestionList.tsx +2 -1
- package/src/chat-composer/SuggestionList.web.tsx +2 -1
- package/src/chat-composer/create-suggestion-list.tsx +54 -8
- package/src/chat-composer/messages.ts +9 -0
- package/src/chat-composer/types.ts +17 -0
- package/src/cover-header/CoverHeader.tsx +75 -0
- package/src/cover-header/CoverImage.tsx +60 -0
- package/src/cover-header/index.ts +3 -0
- package/src/cover-header/types.ts +30 -0
- package/src/index.ts +3 -0
- package/src/index.web.ts +3 -0
- package/src/locale/translations/ar.ts +3 -0
- package/src/locale/translations/bn.ts +3 -0
- package/src/locale/translations/ca.ts +3 -0
- package/src/locale/translations/de.ts +3 -0
- package/src/locale/translations/es.ts +3 -0
- package/src/locale/translations/fr.ts +3 -0
- package/src/locale/translations/hi.ts +3 -0
- package/src/locale/translations/id.ts +3 -0
- package/src/locale/translations/it.ts +3 -0
- package/src/locale/translations/ja.ts +3 -0
- package/src/locale/translations/pt.ts +3 -0
- package/src/locale/translations/ru.ts +3 -0
- package/src/locale/translations/tr.ts +3 -0
- package/src/locale/translations/zh.ts +3 -0
- package/src/message-media/StickerMessage.tsx +21 -8
- package/src/message-media/types.ts +3 -0
- package/src/sticker/Sticker.tsx +5 -0
- package/src/sticker/Sticker.web.tsx +5 -0
- package/src/sticker/create-sticker.tsx +129 -0
- package/src/sticker/index.ts +3 -0
- package/src/sticker/index.web.ts +8 -0
- package/src/sticker/lottie-module.ts +109 -0
- package/src/sticker/lottie-module.web.ts +107 -0
- package/src/sticker/types.ts +45 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import { CoverImage } from './CoverImage';
|
|
6
|
+
import type { CoverHeaderProps } from './types';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_POSITION = { x: 0.5, y: 0.5 };
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A cover band with content rising into its bottom edge — a profile's banner
|
|
12
|
+
* and the avatar that overlaps it.
|
|
13
|
+
*
|
|
14
|
+
* cover absolute over the top `coverHeight`, clipped, background-tertiary
|
|
15
|
+
* under the photo; the FIRST child
|
|
16
|
+
* content in flow, `paddingTop: coverHeight - overlap`; the LAST child
|
|
17
|
+
*
|
|
18
|
+
* The overlap is made by layout, not by moving anything: the band is taken out
|
|
19
|
+
* of flow and the content starts `overlap` short of its bottom edge. No
|
|
20
|
+
* negative margin, no transform, no zIndex. The content is the later sibling,
|
|
21
|
+
* so it paints over the band on iOS, Android and web alike, and the measured
|
|
22
|
+
* height is the real one — a list header or a sticky section after it starts
|
|
23
|
+
* exactly where the content ends.
|
|
24
|
+
*/
|
|
25
|
+
function CoverHeaderComponent({
|
|
26
|
+
coverSource,
|
|
27
|
+
coverPosition = DEFAULT_POSITION,
|
|
28
|
+
cover,
|
|
29
|
+
coverHeight = 170,
|
|
30
|
+
overlap = 45,
|
|
31
|
+
coverStyle,
|
|
32
|
+
contentStyle,
|
|
33
|
+
children,
|
|
34
|
+
style,
|
|
35
|
+
testID,
|
|
36
|
+
}: CoverHeaderProps) {
|
|
37
|
+
const theme = useTheme();
|
|
38
|
+
const rise = Math.min(Math.max(overlap, 0), coverHeight);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<View testID={testID} style={[{ position: 'relative', width: '100%' }, style]}>
|
|
42
|
+
<View
|
|
43
|
+
testID={testID ? `${testID}-cover` : undefined}
|
|
44
|
+
style={[
|
|
45
|
+
{
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
top: 0,
|
|
48
|
+
left: 0,
|
|
49
|
+
right: 0,
|
|
50
|
+
height: coverHeight,
|
|
51
|
+
overflow: 'hidden',
|
|
52
|
+
backgroundColor: theme.colors.backgroundTertiary,
|
|
53
|
+
},
|
|
54
|
+
coverStyle,
|
|
55
|
+
]}>
|
|
56
|
+
{cover ??
|
|
57
|
+
(coverSource ? (
|
|
58
|
+
<CoverImage
|
|
59
|
+
source={coverSource}
|
|
60
|
+
position={coverPosition}
|
|
61
|
+
testID={testID ? `${testID}-cover-image` : undefined}
|
|
62
|
+
/>
|
|
63
|
+
) : null)}
|
|
64
|
+
</View>
|
|
65
|
+
<View
|
|
66
|
+
testID={testID ? `${testID}-content` : undefined}
|
|
67
|
+
style={[{ position: 'relative', width: '100%' }, contentStyle, { paddingTop: coverHeight - rise }]}>
|
|
68
|
+
{children}
|
|
69
|
+
</View>
|
|
70
|
+
</View>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const CoverHeader = memo(CoverHeaderComponent);
|
|
75
|
+
CoverHeader.displayName = 'CoverHeader';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Image,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
View,
|
|
6
|
+
type ImageLoadEventData,
|
|
7
|
+
type ImageSourcePropType,
|
|
8
|
+
type LayoutChangeEvent,
|
|
9
|
+
type NativeSyntheticEvent,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
|
|
12
|
+
/** `object-fit: cover` with an `object-position`: the image sized to cover the box, offset by the fractions. */
|
|
13
|
+
export function CoverImage({
|
|
14
|
+
source,
|
|
15
|
+
position,
|
|
16
|
+
testID,
|
|
17
|
+
}: {
|
|
18
|
+
source: string | ImageSourcePropType;
|
|
19
|
+
position: { x: number; y: number };
|
|
20
|
+
testID?: string;
|
|
21
|
+
}) {
|
|
22
|
+
const [box, setBox] = useState<{ width: number; height: number } | null>(null);
|
|
23
|
+
const [natural, setNatural] = useState<{ width: number; height: number } | null>(null);
|
|
24
|
+
const resolved: ImageSourcePropType = typeof source === 'string' ? { uri: source } : source;
|
|
25
|
+
|
|
26
|
+
const onLayout = useCallback((event: LayoutChangeEvent) => {
|
|
27
|
+
const { width, height } = event.nativeEvent.layout;
|
|
28
|
+
setBox((prev) => (prev && prev.width === width && prev.height === height ? prev : { width, height }));
|
|
29
|
+
}, []);
|
|
30
|
+
const onLoad = useCallback((event: NativeSyntheticEvent<ImageLoadEventData>) => {
|
|
31
|
+
const s = event.nativeEvent?.source;
|
|
32
|
+
if (s && s.width > 0 && s.height > 0) setNatural({ width: s.width, height: s.height });
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const frame = useMemo(() => {
|
|
36
|
+
if (!box || !natural) return null;
|
|
37
|
+
const scale = Math.max(box.width / natural.width, box.height / natural.height);
|
|
38
|
+
const width = natural.width * scale;
|
|
39
|
+
const height = natural.height * scale;
|
|
40
|
+
return { width, height, left: (box.width - width) * position.x, top: (box.height - height) * position.y };
|
|
41
|
+
}, [box, natural, position.x, position.y]);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View style={StyleSheet.absoluteFill} onLayout={onLayout} testID={testID ? `${testID}-frame` : undefined}>
|
|
45
|
+
<Image
|
|
46
|
+
testID={testID}
|
|
47
|
+
source={resolved}
|
|
48
|
+
onLoad={onLoad}
|
|
49
|
+
accessibilityIgnoresInvertColors
|
|
50
|
+
aria-hidden
|
|
51
|
+
resizeMode="cover"
|
|
52
|
+
style={
|
|
53
|
+
frame
|
|
54
|
+
? { position: 'absolute', left: frame.left, top: frame.top, width: frame.width, height: frame.height }
|
|
55
|
+
: StyleSheet.absoluteFill
|
|
56
|
+
}
|
|
57
|
+
/>
|
|
58
|
+
</View>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface CoverHeaderProps {
|
|
5
|
+
/** Photo across the cover band. Without one the band shows `background-tertiary`. */
|
|
6
|
+
coverSource?: string | ImageSourcePropType | null;
|
|
7
|
+
/**
|
|
8
|
+
* Where the photo's crop sits, as fractions (`object-position`). Default
|
|
9
|
+
* `{ x: 0.5, y: 0.5 }` — centred, as `resizeMode="cover"`.
|
|
10
|
+
*/
|
|
11
|
+
coverPosition?: { x: number; y: number };
|
|
12
|
+
/** Drawn in the band instead of the photo (an editor, a gradient, a video poster). */
|
|
13
|
+
cover?: ReactNode;
|
|
14
|
+
/** Height of the cover band. Default `170`. */
|
|
15
|
+
coverHeight?: number;
|
|
16
|
+
/**
|
|
17
|
+
* How far the content rises into the cover band. Default `45` — half a 90px
|
|
18
|
+
* avatar. Clamped to `0..coverHeight`.
|
|
19
|
+
*/
|
|
20
|
+
overlap?: number;
|
|
21
|
+
/** The band's own style: corner radii, a different fill. */
|
|
22
|
+
coverStyle?: StyleProp<ViewStyle>;
|
|
23
|
+
/** The content column's style. Its `paddingTop` is the component's and is ignored. */
|
|
24
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
25
|
+
/** The content: the avatar row first, then whatever follows it. */
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
style?: StyleProp<ViewStyle>;
|
|
28
|
+
/** Also names the parts: `-cover`, `-cover-image`, `-content`. */
|
|
29
|
+
testID?: string;
|
|
30
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -198,6 +198,7 @@ export * from './calendar';
|
|
|
198
198
|
export * from './stat-cards';
|
|
199
199
|
export * from './recent-hires-card';
|
|
200
200
|
export * from './ai-profile-card';
|
|
201
|
+
export * from './cover-header';
|
|
201
202
|
export * from './important-alerts-card';
|
|
202
203
|
export * from './patient-info-card';
|
|
203
204
|
export * from './sidebar';
|
|
@@ -595,6 +596,8 @@ export type {
|
|
|
595
596
|
VoiceMessageProps,
|
|
596
597
|
VoicePlaybackRate,
|
|
597
598
|
} from './message-media';
|
|
599
|
+
export { Sticker, provideLottiePlayer, resetLottiePlayer } from './sticker';
|
|
600
|
+
export type { LottiePlayerProps, StickerProps } from './sticker';
|
|
598
601
|
export {
|
|
599
602
|
BUBBLE_RADIUS,
|
|
600
603
|
BUBBLE_TAIL_RADIUS,
|
package/src/index.web.ts
CHANGED
|
@@ -203,6 +203,7 @@ export * from './calendar';
|
|
|
203
203
|
export * from './stat-cards';
|
|
204
204
|
export * from './recent-hires-card';
|
|
205
205
|
export * from './ai-profile-card';
|
|
206
|
+
export * from './cover-header';
|
|
206
207
|
export * from './important-alerts-card';
|
|
207
208
|
export * from './patient-info-card';
|
|
208
209
|
export * from './sidebar';
|
|
@@ -600,6 +601,8 @@ export type {
|
|
|
600
601
|
VoiceMessageProps,
|
|
601
602
|
VoicePlaybackRate,
|
|
602
603
|
} from './message-media';
|
|
604
|
+
export { Sticker, provideLottiePlayer, resetLottiePlayer } from './sticker/index.web';
|
|
605
|
+
export type { LottiePlayerProps, StickerProps } from './sticker/index.web';
|
|
603
606
|
export {
|
|
604
607
|
BUBBLE_RADIUS,
|
|
605
608
|
BUBBLE_TAIL_RADIUS,
|
|
@@ -176,6 +176,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
176
176
|
attachments: 'المرفقات',
|
|
177
177
|
removeAttachment: (name) => `إزالة ${name}`,
|
|
178
178
|
suggestions: { mention: 'الأشخاص', command: 'الأوامر', emoji: 'رموز تعبيرية' },
|
|
179
|
+
suggestionVerified: 'موثّق',
|
|
180
|
+
searchingSuggestions: 'جارٍ البحث…',
|
|
181
|
+
noSuggestions: { mention: 'لم يتم العثور على أشخاص', command: 'لم يتم العثور على أوامر', emoji: 'لم يتم العثور على رموز تعبيرية' },
|
|
179
182
|
attachmentItems: { gallery: 'المعرض', camera: 'الكاميرا', file: 'ملف', location: 'الموقع', contact: 'جهة اتصال', poll: 'استطلاع', music: 'موسيقى' },
|
|
180
183
|
};
|
|
181
184
|
|
|
@@ -158,6 +158,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
158
158
|
attachments: 'সংযুক্তি',
|
|
159
159
|
removeAttachment: (name) => `${name} সরান`,
|
|
160
160
|
suggestions: { mention: 'লোকজন', command: 'কমান্ড', emoji: 'ইমোজি' },
|
|
161
|
+
suggestionVerified: 'যাচাইকৃত',
|
|
162
|
+
searchingSuggestions: 'খোঁজা হচ্ছে…',
|
|
163
|
+
noSuggestions: { mention: 'কাউকে পাওয়া যায়নি', command: 'কোনো কমান্ড পাওয়া যায়নি', emoji: 'কোনো ইমোজি পাওয়া যায়নি' },
|
|
161
164
|
attachmentItems: { gallery: 'গ্যালারি', camera: 'ক্যামেরা', file: 'ফাইল', location: 'অবস্থান', contact: 'পরিচিতি', poll: 'পোল', music: 'সংগীত' },
|
|
162
165
|
};
|
|
163
166
|
|
|
@@ -165,6 +165,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
165
165
|
attachments: 'Fitxers adjunts',
|
|
166
166
|
removeAttachment: (name) => `Treu ${name}`,
|
|
167
167
|
suggestions: { mention: 'Persones', command: 'Ordres', emoji: 'Emoji' },
|
|
168
|
+
suggestionVerified: 'Verificat',
|
|
169
|
+
searchingSuggestions: 'Cercant…',
|
|
170
|
+
noSuggestions: { mention: "No s'ha trobat ningú", command: "No s'han trobat ordres", emoji: "No s'han trobat emojis" },
|
|
168
171
|
attachmentItems: { gallery: 'Galeria', camera: 'Càmera', file: 'Fitxer', location: 'Ubicació', contact: 'Contacte', poll: 'Enquesta', music: 'Música' },
|
|
169
172
|
};
|
|
170
173
|
|
|
@@ -160,6 +160,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
160
160
|
attachments: 'Anhänge',
|
|
161
161
|
removeAttachment: (name) => `${name} entfernen`,
|
|
162
162
|
suggestions: { mention: 'Personen', command: 'Befehle', emoji: 'Emoji' },
|
|
163
|
+
suggestionVerified: 'Verifiziert',
|
|
164
|
+
searchingSuggestions: 'Suche läuft…',
|
|
165
|
+
noSuggestions: { mention: 'Keine Personen gefunden', command: 'Keine Befehle gefunden', emoji: 'Keine Emojis gefunden' },
|
|
163
166
|
attachmentItems: { gallery: 'Galerie', camera: 'Kamera', file: 'Datei', location: 'Standort', contact: 'Kontakt', poll: 'Umfrage', music: 'Musik' },
|
|
164
167
|
};
|
|
165
168
|
|
|
@@ -165,6 +165,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
165
165
|
attachments: 'Archivos adjuntos',
|
|
166
166
|
removeAttachment: (name) => `Quitar ${name}`,
|
|
167
167
|
suggestions: { mention: 'Personas', command: 'Comandos', emoji: 'Emoji' },
|
|
168
|
+
suggestionVerified: 'Verificado',
|
|
169
|
+
searchingSuggestions: 'Buscando…',
|
|
170
|
+
noSuggestions: { mention: 'No se encontraron personas', command: 'No se encontraron comandos', emoji: 'No se encontraron emojis' },
|
|
168
171
|
attachmentItems: { gallery: 'Galería', camera: 'Cámara', file: 'Archivo', location: 'Ubicación', contact: 'Contacto', poll: 'Encuesta', music: 'Música' },
|
|
169
172
|
};
|
|
170
173
|
|
|
@@ -165,6 +165,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
165
165
|
attachments: 'Pièces jointes',
|
|
166
166
|
removeAttachment: (name) => `Retirer ${name}`,
|
|
167
167
|
suggestions: { mention: 'Personnes', command: 'Commandes', emoji: 'Émoji' },
|
|
168
|
+
suggestionVerified: 'Vérifié',
|
|
169
|
+
searchingSuggestions: 'Recherche…',
|
|
170
|
+
noSuggestions: { mention: 'Aucune personne trouvée', command: 'Aucune commande trouvée', emoji: 'Aucun émoji trouvé' },
|
|
168
171
|
attachmentItems: { gallery: 'Galerie', camera: 'Appareil photo', file: 'Fichier', location: 'Position', contact: 'Contact', poll: 'Sondage', music: 'Musique' },
|
|
169
172
|
};
|
|
170
173
|
|
|
@@ -158,6 +158,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
158
158
|
attachments: 'अटैचमेंट',
|
|
159
159
|
removeAttachment: (name) => `${name} हटाएं`,
|
|
160
160
|
suggestions: { mention: 'लोग', command: 'कमांड', emoji: 'इमोजी' },
|
|
161
|
+
suggestionVerified: 'सत्यापित',
|
|
162
|
+
searchingSuggestions: 'खोज रहे हैं…',
|
|
163
|
+
noSuggestions: { mention: 'कोई व्यक्ति नहीं मिला', command: 'कोई कमांड नहीं मिला', emoji: 'कोई इमोजी नहीं मिला' },
|
|
161
164
|
attachmentItems: { gallery: 'गैलरी', camera: 'कैमरा', file: 'फ़ाइल', location: 'लोकेशन', contact: 'संपर्क', poll: 'पोल', music: 'संगीत' },
|
|
162
165
|
};
|
|
163
166
|
|
|
@@ -158,6 +158,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
158
158
|
attachments: 'Lampiran',
|
|
159
159
|
removeAttachment: (name) => `Hapus ${name}`,
|
|
160
160
|
suggestions: { mention: 'Orang', command: 'Perintah', emoji: 'Emoji' },
|
|
161
|
+
suggestionVerified: 'Terverifikasi',
|
|
162
|
+
searchingSuggestions: 'Mencari…',
|
|
163
|
+
noSuggestions: { mention: 'Orang tidak ditemukan', command: 'Perintah tidak ditemukan', emoji: 'Emoji tidak ditemukan' },
|
|
161
164
|
attachmentItems: { gallery: 'Galeri', camera: 'Kamera', file: 'File', location: 'Lokasi', contact: 'Kontak', poll: 'Polling', music: 'Musik' },
|
|
162
165
|
};
|
|
163
166
|
|
|
@@ -165,6 +165,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
165
165
|
attachments: 'Allegati',
|
|
166
166
|
removeAttachment: (name) => `Rimuovi ${name}`,
|
|
167
167
|
suggestions: { mention: 'Persone', command: 'Comandi', emoji: 'Emoji' },
|
|
168
|
+
suggestionVerified: 'Verificato',
|
|
169
|
+
searchingSuggestions: 'Ricerca in corso…',
|
|
170
|
+
noSuggestions: { mention: 'Nessuna persona trovata', command: 'Nessun comando trovato', emoji: 'Nessuna emoji trovata' },
|
|
168
171
|
attachmentItems: { gallery: 'Galleria', camera: 'Fotocamera', file: 'File', location: 'Posizione', contact: 'Contatto', poll: 'Sondaggio', music: 'Musica' },
|
|
169
172
|
};
|
|
170
173
|
|
|
@@ -158,6 +158,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
158
158
|
attachments: '添付ファイル',
|
|
159
159
|
removeAttachment: (name) => `${name}を削除`,
|
|
160
160
|
suggestions: { mention: 'ユーザー', command: 'コマンド', emoji: '絵文字' },
|
|
161
|
+
suggestionVerified: '認証済み',
|
|
162
|
+
searchingSuggestions: '検索中…',
|
|
163
|
+
noSuggestions: { mention: 'ユーザーが見つかりません', command: 'コマンドが見つかりません', emoji: '絵文字が見つかりません' },
|
|
161
164
|
attachmentItems: { gallery: 'ギャラリー', camera: 'カメラ', file: 'ファイル', location: '位置情報', contact: '連絡先', poll: 'アンケート', music: '音楽' },
|
|
162
165
|
};
|
|
163
166
|
|
|
@@ -165,6 +165,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
165
165
|
attachments: 'Anexos',
|
|
166
166
|
removeAttachment: (name) => `Remover ${name}`,
|
|
167
167
|
suggestions: { mention: 'Pessoas', command: 'Comandos', emoji: 'Emoji' },
|
|
168
|
+
suggestionVerified: 'Verificado',
|
|
169
|
+
searchingSuggestions: 'Pesquisando…',
|
|
170
|
+
noSuggestions: { mention: 'Nenhuma pessoa encontrada', command: 'Nenhum comando encontrado', emoji: 'Nenhum emoji encontrado' },
|
|
168
171
|
attachmentItems: { gallery: 'Galeria', camera: 'Câmera', file: 'Arquivo', location: 'Localização', contact: 'Contato', poll: 'Enquete', music: 'Música' },
|
|
169
172
|
};
|
|
170
173
|
|
|
@@ -163,6 +163,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
163
163
|
attachments: 'Вложения',
|
|
164
164
|
removeAttachment: (name) => `Удалить ${name}`,
|
|
165
165
|
suggestions: { mention: 'Люди', command: 'Команды', emoji: 'Эмодзи' },
|
|
166
|
+
suggestionVerified: 'Подтверждён',
|
|
167
|
+
searchingSuggestions: 'Поиск…',
|
|
168
|
+
noSuggestions: { mention: 'Никого не найдено', command: 'Команды не найдены', emoji: 'Эмодзи не найдены' },
|
|
166
169
|
attachmentItems: { gallery: 'Галерея', camera: 'Камера', file: 'Файл', location: 'Геопозиция', contact: 'Контакт', poll: 'Опрос', music: 'Музыка' },
|
|
167
170
|
};
|
|
168
171
|
|
|
@@ -159,6 +159,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
159
159
|
attachments: 'Ekler',
|
|
160
160
|
removeAttachment: (name) => `Kaldır: ${name}`,
|
|
161
161
|
suggestions: { mention: 'Kişiler', command: 'Komutlar', emoji: 'Emoji' },
|
|
162
|
+
suggestionVerified: 'Doğrulanmış',
|
|
163
|
+
searchingSuggestions: 'Aranıyor…',
|
|
164
|
+
noSuggestions: { mention: 'Kişi bulunamadı', command: 'Komut bulunamadı', emoji: 'Emoji bulunamadı' },
|
|
162
165
|
attachmentItems: { gallery: 'Galeri', camera: 'Kamera', file: 'Dosya', location: 'Konum', contact: 'Kişi', poll: 'Anket', music: 'Müzik' },
|
|
163
166
|
};
|
|
164
167
|
|
|
@@ -158,6 +158,9 @@ const CHAT_COMPOSER_MESSAGES: Translations['CHAT_COMPOSER_MESSAGES'] = {
|
|
|
158
158
|
attachments: '附件',
|
|
159
159
|
removeAttachment: (name) => `移除 ${name}`,
|
|
160
160
|
suggestions: { mention: '用户', command: '命令', emoji: '表情' },
|
|
161
|
+
suggestionVerified: '已认证',
|
|
162
|
+
searchingSuggestions: '正在搜索…',
|
|
163
|
+
noSuggestions: { mention: '未找到用户', command: '未找到命令', emoji: '未找到表情' },
|
|
161
164
|
attachmentItems: { gallery: '相册', camera: '相机', file: '文件', location: '位置', contact: '联系人', poll: '投票', music: '音乐' },
|
|
162
165
|
};
|
|
163
166
|
|
|
@@ -2,6 +2,7 @@ import React, { memo, useMemo } from 'react';
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useMessages } from '../locale/messages';
|
|
5
|
+
import { Sticker } from '../sticker/Sticker';
|
|
5
6
|
import { useTheme } from '../theme/use-theme';
|
|
6
7
|
import { MESSAGE_MEDIA_MESSAGES } from './messages';
|
|
7
8
|
import { MediaFailure, MediaImage, MediaOverlay, MediaPressable, MediaProgressRing } from './parts';
|
|
@@ -12,7 +13,8 @@ import type { StickerMessageProps } from './types';
|
|
|
12
13
|
const TRANSPARENT = 'transparent';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
|
-
* A sticker: a transparent image and nothing else
|
|
16
|
+
* A sticker: a transparent image and nothing else — or, given `animation`, a
|
|
17
|
+
* Lottie animation drawn by `Sticker` over that same image.
|
|
16
18
|
*
|
|
17
19
|
* IT DRAWS NO SURFACE AT ALL — no background, no radius, no padding — because a
|
|
18
20
|
* sticker with a bubble behind it is a picture of a sticker. The shell renders it
|
|
@@ -30,6 +32,7 @@ const TRANSPARENT = 'transparent';
|
|
|
30
32
|
*/
|
|
31
33
|
function StickerMessageComponent({
|
|
32
34
|
source,
|
|
35
|
+
animation,
|
|
33
36
|
sourceVariant,
|
|
34
37
|
size = 128,
|
|
35
38
|
accessibilityLabel: accessibilityLabelProp,
|
|
@@ -61,13 +64,23 @@ function StickerMessageComponent({
|
|
|
61
64
|
ariaBusy={sending}
|
|
62
65
|
testID={testID ? `${testID}-frame` : undefined}
|
|
63
66
|
>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
{animation ? (
|
|
68
|
+
<Sticker
|
|
69
|
+
animation={animation}
|
|
70
|
+
fallback={source}
|
|
71
|
+
size={size}
|
|
72
|
+
decorative
|
|
73
|
+
testID={testID ? `${testID}-sticker` : undefined}
|
|
74
|
+
/>
|
|
75
|
+
) : (
|
|
76
|
+
<MediaImage
|
|
77
|
+
source={source}
|
|
78
|
+
sourceVariant={sourceVariant}
|
|
79
|
+
placeholder={TRANSPARENT}
|
|
80
|
+
resizeMode="contain"
|
|
81
|
+
testID={testID ? `${testID}-image` : undefined}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
71
84
|
{sending ? (
|
|
72
85
|
<MediaOverlay>
|
|
73
86
|
<MediaProgressRing
|
|
@@ -400,7 +400,10 @@ export interface PollMessageProps extends MessageMediaToneProps {
|
|
|
400
400
|
// ---------------------------------------------------------------------------
|
|
401
401
|
|
|
402
402
|
export interface StickerMessageProps extends MessageMediaToneProps {
|
|
403
|
+
/** The still image. With `animation`, it is what shows until (or unless) the animation plays. */
|
|
403
404
|
source: MessageMediaSource;
|
|
405
|
+
/** URL of the sticker's Lottie animation. Omit it for a still sticker. */
|
|
406
|
+
animation?: string;
|
|
404
407
|
sourceVariant?: string;
|
|
405
408
|
/** Square edge. Default 128. */
|
|
406
409
|
size?: number;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createSticker } from './create-sticker';
|
|
2
|
+
import { loadLottiePlayer, warnLottieUnavailable } from './lottie-module';
|
|
3
|
+
|
|
4
|
+
/** A sticker, animated with `lottie-react-native` when it is installed. */
|
|
5
|
+
export const Sticker = createSticker({ loadLottiePlayer, warnLottieUnavailable });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createSticker } from './create-sticker';
|
|
2
|
+
import { loadLottiePlayer, warnLottieUnavailable } from './lottie-module.web';
|
|
3
|
+
|
|
4
|
+
/** A sticker, animated with `@lottiefiles/dotlottie-react` when it is installed. */
|
|
5
|
+
export const Sticker = createSticker({ loadLottiePlayer, warnLottieUnavailable });
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, { memo, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { useReducedMotion } from 'react-native-reanimated';
|
|
4
|
+
|
|
5
|
+
import { useMessages } from '../locale/messages';
|
|
6
|
+
import { MESSAGE_MEDIA_MESSAGES } from '../message-media/messages';
|
|
7
|
+
import { MediaImage } from '../message-media/parts';
|
|
8
|
+
import type { ComponentType } from 'react';
|
|
9
|
+
import type { LottiePlayerProps, StickerProps } from './types';
|
|
10
|
+
|
|
11
|
+
export interface StickerPlatform {
|
|
12
|
+
/** The platform's Lottie player, or `null` when its optional peer is missing. */
|
|
13
|
+
loadLottiePlayer(): ComponentType<LottiePlayerProps> | null;
|
|
14
|
+
warnLottieUnavailable(): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A sticker's transparent parts must stay transparent: no placeholder colour. */
|
|
18
|
+
const TRANSPARENT = 'transparent';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A sticker, animated when it can be and still when it cannot.
|
|
22
|
+
*
|
|
23
|
+
* The still `fallback` is ALWAYS drawn first and stays until the animation
|
|
24
|
+
* reports it has loaded, so a sticker never flashes blank while its Lottie file
|
|
25
|
+
* arrives — and when the animation cannot play at all (no player installed on
|
|
26
|
+
* this platform, a failed load, a person who asked for reduced motion) the still
|
|
27
|
+
* is simply what remains. Every one of those outcomes looks like a sticker.
|
|
28
|
+
*
|
|
29
|
+
* Draws no surface: no background, no radius, no padding. A sticker with a
|
|
30
|
+
* bubble behind it is a picture of a sticker. `StickerMessage` is this plus the
|
|
31
|
+
* chat affordances (press, sending, failed).
|
|
32
|
+
*/
|
|
33
|
+
export function createSticker({
|
|
34
|
+
loadLottiePlayer,
|
|
35
|
+
warnLottieUnavailable,
|
|
36
|
+
}: StickerPlatform) {
|
|
37
|
+
function StickerComponent({
|
|
38
|
+
animation,
|
|
39
|
+
fallback,
|
|
40
|
+
size = 128,
|
|
41
|
+
loop = true,
|
|
42
|
+
paused = false,
|
|
43
|
+
accessibilityLabel: accessibilityLabelProp,
|
|
44
|
+
decorative = false,
|
|
45
|
+
style,
|
|
46
|
+
testID,
|
|
47
|
+
}: StickerProps) {
|
|
48
|
+
const { messages } = useMessages(MESSAGE_MEDIA_MESSAGES);
|
|
49
|
+
const reducedMotion = useReducedMotion();
|
|
50
|
+
const [loaded, setLoaded] = useState(false);
|
|
51
|
+
const [failed, setFailed] = useState(false);
|
|
52
|
+
|
|
53
|
+
// A new animation starts from its still again.
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
setLoaded(false);
|
|
56
|
+
setFailed(false);
|
|
57
|
+
}, [animation]);
|
|
58
|
+
|
|
59
|
+
const Player =
|
|
60
|
+
animation && !reducedMotion && !failed ? loadLottiePlayer() : null;
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (animation && !reducedMotion && !Player) warnLottieUnavailable();
|
|
63
|
+
}, [animation, reducedMotion, Player]);
|
|
64
|
+
|
|
65
|
+
const onLoad = useCallback(() => setLoaded(true), []);
|
|
66
|
+
const onError = useCallback(() => setFailed(true), []);
|
|
67
|
+
|
|
68
|
+
const showStill = !Player || !loaded;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<View
|
|
72
|
+
accessible={!decorative}
|
|
73
|
+
accessibilityRole={decorative ? undefined : 'image'}
|
|
74
|
+
accessibilityLabel={decorative ? undefined : accessibilityLabelProp ?? messages.sticker}
|
|
75
|
+
aria-hidden={decorative}
|
|
76
|
+
style={[
|
|
77
|
+
{ width: size, height: size, position: 'relative' },
|
|
78
|
+
style ?? null,
|
|
79
|
+
]}
|
|
80
|
+
testID={testID}
|
|
81
|
+
>
|
|
82
|
+
{showStill ? (
|
|
83
|
+
<View
|
|
84
|
+
style={{
|
|
85
|
+
position: 'absolute',
|
|
86
|
+
top: 0,
|
|
87
|
+
left: 0,
|
|
88
|
+
width: size,
|
|
89
|
+
height: size,
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
<MediaImage
|
|
93
|
+
source={fallback}
|
|
94
|
+
placeholder={TRANSPARENT}
|
|
95
|
+
resizeMode='contain'
|
|
96
|
+
testID={testID ? `${testID}-still` : undefined}
|
|
97
|
+
/>
|
|
98
|
+
</View>
|
|
99
|
+
) : null}
|
|
100
|
+
{Player && animation ? (
|
|
101
|
+
<View
|
|
102
|
+
style={{
|
|
103
|
+
position: 'absolute',
|
|
104
|
+
top: 0,
|
|
105
|
+
left: 0,
|
|
106
|
+
width: size,
|
|
107
|
+
height: size,
|
|
108
|
+
opacity: loaded ? 1 : 0,
|
|
109
|
+
}}
|
|
110
|
+
testID={testID ? `${testID}-animation` : undefined}
|
|
111
|
+
>
|
|
112
|
+
<Player
|
|
113
|
+
uri={animation}
|
|
114
|
+
loop={loop}
|
|
115
|
+
paused={paused}
|
|
116
|
+
onLoad={onLoad}
|
|
117
|
+
onError={onError}
|
|
118
|
+
style={{ width: size, height: size }}
|
|
119
|
+
/>
|
|
120
|
+
</View>
|
|
121
|
+
) : null}
|
|
122
|
+
</View>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const Sticker = memo(StickerComponent);
|
|
127
|
+
Sticker.displayName = 'Sticker';
|
|
128
|
+
return Sticker;
|
|
129
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/generate-platform-exports.mjs — DO NOT EDIT.
|
|
2
|
+
// Source of truth: src/sticker/index.ts.
|
|
3
|
+
// Re-run `bun run generate:exports` (or any `bun run build`) after
|
|
4
|
+
// changing that barrel or the set of web-forked subpaths.
|
|
5
|
+
|
|
6
|
+
export { Sticker } from './Sticker.web';
|
|
7
|
+
export { provideLottiePlayer, resetLottiePlayer } from './lottie-module.web';
|
|
8
|
+
export type { LottiePlayerProps, StickerProps } from './types';
|