@comergehq/studio 0.1.4 → 0.1.6

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": "@comergehq/studio",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Comerge studio",
5
5
  "main": "src/index.ts",
6
6
  "module": "dist/index.mjs",
@@ -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
- {overlay}
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
 
@@ -69,6 +69,17 @@ export function StudioBottomSheet({
69
69
 
70
70
  // Gorhom BottomSheet `index` is not reliably "fully controlled" across versions.
71
71
  // Ensure the visual sheet actually opens/closes when `open` changes (e.g. via header X button).
72
+ React.useEffect(() => {
73
+ if (Platform.OS !== 'ios') return;
74
+ const sub = Keyboard.addListener('keyboardDidHide', () => {
75
+ const sheet = resolvedSheetRef.current;
76
+ if (!sheet || !open) return;
77
+ const targetIndex = snapPoints.length - 1;
78
+ setTimeout(() => sheet.snapToIndex(targetIndex), 10);
79
+ });
80
+ return () => sub.remove();
81
+ }, [open, resolvedSheetRef, snapPoints.length]);
82
+
72
83
  React.useEffect(() => {
73
84
  const sheet = resolvedSheetRef.current;
74
85
  if (!sheet) return;
@@ -94,14 +105,14 @@ export function StudioBottomSheet({
94
105
  index={open ? snapPoints.length - 1 : -1}
95
106
  snapPoints={snapPoints}
96
107
  enablePanDownToClose
97
- keyboardBehavior="extend"
108
+ keyboardBehavior={Platform.OS === 'ios' ? 'interactive' : 'extend'}
98
109
  keyboardBlurBehavior="restore"
99
110
  android_keyboardInputMode="adjustResize"
100
111
  backgroundComponent={(props: BottomSheetBackgroundProps) => (
101
112
  <StudioSheetBackground {...props} renderBackground={background?.renderBackground} />
102
113
  )}
103
114
  topInset={insets.top}
104
- bottomInset={insets.bottom}
115
+ bottomInset={0}
105
116
  handleIndicatorStyle={{ backgroundColor: theme.colors.handleIndicator }}
106
117
  onChange={handleChange}
107
118
  {...bottomSheetProps}
@@ -1,3 +1,3 @@
1
- export const BASE_URL = "http://192.168.8.175:8080";
1
+ export const BASE_URL = "https://comerge.ai";
2
2
 
3
3
 
@@ -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(() => {