@comergehq/studio 0.1.5 → 0.1.7
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/dist/index.js +87 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +101 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/ChatPage.tsx +55 -8
- package/src/components/studio-sheet/StudioBottomSheet.tsx +13 -4
- package/src/studio/ui/StudioOverlay.tsx +18 -2
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { View, type ViewStyle } from 'react-native';
|
|
2
|
+
import { Keyboard, Platform, View, type ViewStyle } from 'react-native';
|
|
3
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
|
+
import Animated, { useAnimatedKeyboard, useAnimatedStyle } from 'react-native-reanimated';
|
|
3
5
|
|
|
4
6
|
import type { ChatMessage } from '../models/types';
|
|
5
7
|
import { useTheme } from '../../theme';
|
|
@@ -37,7 +39,37 @@ export function ChatPage({
|
|
|
37
39
|
listRef,
|
|
38
40
|
}: ChatPageProps) {
|
|
39
41
|
const theme = useTheme();
|
|
42
|
+
const insets = useSafeAreaInsets();
|
|
40
43
|
const [composerHeight, setComposerHeight] = React.useState(0);
|
|
44
|
+
const [keyboardVisible, setKeyboardVisible] = React.useState(false);
|
|
45
|
+
const animatedKeyboard = useAnimatedKeyboard();
|
|
46
|
+
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
if (Platform.OS !== 'ios') return;
|
|
49
|
+
|
|
50
|
+
const show = Keyboard.addListener('keyboardWillShow', () => setKeyboardVisible(true));
|
|
51
|
+
const hide = Keyboard.addListener('keyboardWillHide', () => setKeyboardVisible(false));
|
|
52
|
+
return () => {
|
|
53
|
+
show.remove();
|
|
54
|
+
hide.remove();
|
|
55
|
+
};
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const footerBottomPadding = Platform.OS === 'ios' ? (keyboardVisible ? 0 : insets.bottom) : insets.bottom + 10;
|
|
59
|
+
const footerAnimatedStyle = useAnimatedStyle(() => {
|
|
60
|
+
if (Platform.OS !== 'ios') return { paddingBottom: insets.bottom + 10 };
|
|
61
|
+
return { paddingBottom: animatedKeyboard.height.value > 0 ? 0 : insets.bottom };
|
|
62
|
+
});
|
|
63
|
+
const overlayBottom = composerHeight + footerBottomPadding + theme.spacing.lg;
|
|
64
|
+
|
|
65
|
+
const resolvedOverlay = React.useMemo(() => {
|
|
66
|
+
if (!overlay) return null;
|
|
67
|
+
if (!React.isValidElement(overlay)) return overlay;
|
|
68
|
+
const prevStyle = (overlay.props as any)?.style;
|
|
69
|
+
return React.cloneElement(overlay as any, {
|
|
70
|
+
style: [prevStyle, { bottom: overlayBottom }],
|
|
71
|
+
});
|
|
72
|
+
}, [overlay, overlayBottom]);
|
|
41
73
|
return (
|
|
42
74
|
<View style={[{ flex: 1 }, style]}>
|
|
43
75
|
{header ? <View>{header}</View> : null}
|
|
@@ -53,15 +85,30 @@ export function ChatPage({
|
|
|
53
85
|
showTypingIndicator={showTypingIndicator}
|
|
54
86
|
renderMessageContent={renderMessageContent}
|
|
55
87
|
onNearBottomChange={onNearBottomChange}
|
|
56
|
-
contentStyle={{ paddingBottom: theme.spacing.xl + composerHeight }}
|
|
88
|
+
contentStyle={{ paddingBottom: theme.spacing.xl + composerHeight + footerBottomPadding }}
|
|
57
89
|
/>
|
|
58
|
-
{
|
|
90
|
+
{resolvedOverlay}
|
|
91
|
+
|
|
92
|
+
<Animated.View
|
|
93
|
+
style={[
|
|
94
|
+
{
|
|
95
|
+
position: 'absolute',
|
|
96
|
+
left: 0,
|
|
97
|
+
right: 0,
|
|
98
|
+
bottom: 0,
|
|
99
|
+
paddingHorizontal: theme.spacing.lg,
|
|
100
|
+
paddingTop: theme.spacing.sm,
|
|
101
|
+
},
|
|
102
|
+
footerAnimatedStyle,
|
|
103
|
+
]}
|
|
104
|
+
>
|
|
105
|
+
<ChatComposer
|
|
106
|
+
{...composer}
|
|
107
|
+
attachments={composer.attachments ?? []}
|
|
108
|
+
onLayout={({ height }) => setComposerHeight(height)}
|
|
109
|
+
/>
|
|
110
|
+
</Animated.View>
|
|
59
111
|
</View>
|
|
60
|
-
<ChatComposer
|
|
61
|
-
{...composer}
|
|
62
|
-
attachments={composer.attachments ?? []}
|
|
63
|
-
onLayout={({ height }) => setComposerHeight(height)}
|
|
64
|
-
/>
|
|
65
112
|
</View>
|
|
66
113
|
);
|
|
67
114
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
2
|
+
import { Keyboard, Platform, View } from 'react-native';
|
|
3
3
|
import BottomSheet, { type BottomSheetBackgroundProps, type BottomSheetProps } from '@gorhom/bottom-sheet';
|
|
4
4
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
5
|
|
|
@@ -67,8 +67,17 @@ export function StudioBottomSheet({
|
|
|
67
67
|
const internalSheetRef = React.useRef<BottomSheet | null>(null);
|
|
68
68
|
const resolvedSheetRef = sheetRef ?? internalSheetRef;
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
if (Platform.OS !== 'ios') return;
|
|
72
|
+
const sub = Keyboard.addListener('keyboardDidHide', () => {
|
|
73
|
+
const sheet = resolvedSheetRef.current;
|
|
74
|
+
if (!sheet || !open) return;
|
|
75
|
+
const targetIndex = snapPoints.length - 1;
|
|
76
|
+
setTimeout(() => sheet.snapToIndex(targetIndex), 10);
|
|
77
|
+
});
|
|
78
|
+
return () => sub.remove();
|
|
79
|
+
}, [open, resolvedSheetRef, snapPoints.length]);
|
|
80
|
+
|
|
72
81
|
React.useEffect(() => {
|
|
73
82
|
const sheet = resolvedSheetRef.current;
|
|
74
83
|
if (!sheet) return;
|
|
@@ -101,7 +110,7 @@ export function StudioBottomSheet({
|
|
|
101
110
|
<StudioSheetBackground {...props} renderBackground={background?.renderBackground} />
|
|
102
111
|
)}
|
|
103
112
|
topInset={insets.top}
|
|
104
|
-
bottomInset={
|
|
113
|
+
bottomInset={0}
|
|
105
114
|
handleIndicatorStyle={{ backgroundColor: theme.colors.handleIndicator }}
|
|
106
115
|
onChange={handleChange}
|
|
107
116
|
{...bottomSheetProps}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { Keyboard, View, useWindowDimensions } from 'react-native';
|
|
2
|
+
import { Keyboard, Platform, View, useWindowDimensions } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type { App } from '../../data/apps/types';
|
|
5
5
|
import type { MergeRequest } from '../../data/merge-requests/types';
|
|
@@ -117,8 +117,24 @@ export function StudioOverlay({
|
|
|
117
117
|
}, [openSheet]);
|
|
118
118
|
|
|
119
119
|
const backToPreview = React.useCallback(() => {
|
|
120
|
+
if (Platform.OS !== 'ios') {
|
|
121
|
+
Keyboard.dismiss();
|
|
122
|
+
setActivePage('preview');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let done = false;
|
|
127
|
+
const finalize = () => {
|
|
128
|
+
if (done) return;
|
|
129
|
+
done = true;
|
|
130
|
+
sub.remove();
|
|
131
|
+
clearTimeout(t);
|
|
132
|
+
setActivePage('preview');
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const sub = Keyboard.addListener('keyboardDidHide', finalize);
|
|
136
|
+
const t = setTimeout(finalize, 350);
|
|
120
137
|
Keyboard.dismiss();
|
|
121
|
-
setActivePage('preview');
|
|
122
138
|
}, []);
|
|
123
139
|
|
|
124
140
|
const startDraw = React.useCallback(() => {
|