Skip to content

Commit 90f9f05

Browse files
committed
Merge branch 'release/4.3.0'
2 parents 5672c7f + 0bb28af commit 90f9f05

File tree

28 files changed

+1005
-111
lines changed

28 files changed

+1005
-111
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
EXPO_PUBLIC_CHATWOOT_WEBSITE_TOKEN=
22
EXPO_PUBLIC_CHATWOOT_BASE_URL=https://app.chatwoot.com
33
EXPO_PUBLIC_JUNE_SDK_KEY=
4-
EXPO_PUBLIC_MINIMUM_CHATWOOT_VERSION=3.13.0
4+
EXPO_PUBLIC_MINIMUM_CHATWOOT_VERSION=4.1.0
55
EXPO_PUBLIC_SENTRY_DSN=
66
EXPO_PUBLIC_PROJECT_ID=
77
EXPO_PUBLIC_APP_SLUG=

app.config.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ export default ({ config }: ConfigContext): ExpoConfig => {
44
return {
55
name: 'Chatwoot',
66
slug: process.env.EXPO_PUBLIC_APP_SLUG || 'chatwoot-mobile',
7-
version: '4.2.0',
7+
version: '4.3.0',
88
orientation: 'portrait',
99
icon: './assets/icon.png',
1010
userInterfaceStyle: 'light',
1111
newArchEnabled: false,
12+
scheme: 'chatwootapp',
1213
splash: {
1314
image: './assets/splash.png',
1415
resizeMode: 'contain',
@@ -37,13 +38,7 @@ export default ({ config }: ConfigContext): ExpoConfig => {
3738
android: {
3839
adaptiveIcon: { foregroundImage: './assets/adaptive-icon.png', backgroundColor: '#ffffff' },
3940
package: 'com.chatwoot.app',
40-
permissions: [
41-
'android.permission.CAMERA',
42-
'android.permission.READ_EXTERNAL_STORAGE',
43-
'android.permission.WRITE_EXTERNAL_STORAGE',
44-
'android.permission.RECORD_AUDIO',
45-
'android.permission.READ_MEDIA_IMAGES',
46-
],
41+
permissions: ['android.permission.CAMERA', 'android.permission.RECORD_AUDIO'],
4742
// Please use the relative path to the google-services.json file
4843
googleServicesFile: process.env.EXPO_PUBLIC_ANDROID_GOOGLE_SERVICES_FILE,
4944
intentFilters: [
@@ -60,6 +55,15 @@ export default ({ config }: ConfigContext): ExpoConfig => {
6055
],
6156
category: ['BROWSABLE', 'DEFAULT'],
6257
},
58+
{
59+
action: 'VIEW',
60+
data: [
61+
{
62+
scheme: 'chatwootapp',
63+
},
64+
],
65+
category: ['BROWSABLE', 'DEFAULT'],
66+
},
6367
],
6468
},
6569
extra: {
@@ -89,7 +93,7 @@ export default ({ config }: ConfigContext): ExpoConfig => {
8993
android: {
9094
minSdkVersion: 24,
9195
compileSdkVersion: 35,
92-
targetSdkVersion: 34,
96+
targetSdkVersion: 35,
9397
enableProguardInReleaseBuilds: true,
9498
},
9599
ios: { useFrameworks: 'static' },

jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ module.exports = {
44
moduleNameMapper: {
55
'^@/(.*)$': '<rootDir>/src/$1',
66
},
7+
transformIgnorePatterns: [
8+
'node_modules/(?!(jest-)?@?react-native|@react-native-community|@react-navigation|@reduxjs|immer)',
9+
],
710
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@chatwoot/mobile-app",
3-
"version": "4.2.0",
3+
"version": "4.3.0",
44
"scripts": {
55
"start": "expo start --dev-client",
66
"start:production": "expo start --no-dev --minify",
@@ -63,6 +63,7 @@
6363
"diff": "5.0.0",
6464
"expo": "~52.0.46",
6565
"expo-application": "~6.0.2",
66+
"expo-auth-session": "~6.0.3",
6667
"expo-av": "~15.0.2",
6768
"expo-build-properties": "~0.13.2",
6869
"expo-constants": "^17.0.8",

pnpm-lock.yaml

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import { Pressable } from 'react-native';
3+
import Animated from 'react-native-reanimated';
4+
5+
import { tailwind } from '@/theme';
6+
import { Icon } from '../common/icon/Icon';
7+
8+
type AuthButtonProps = {
9+
text: string;
10+
icon: React.ReactNode;
11+
handlePress?: () => void;
12+
disabled?: boolean;
13+
variant?: 'outline' | 'filled';
14+
style?: any;
15+
};
16+
17+
export const AuthButton = ({
18+
text,
19+
icon,
20+
handlePress,
21+
disabled = false,
22+
variant = 'outline',
23+
style,
24+
}: AuthButtonProps) => {
25+
const getButtonStyles = () => {
26+
const baseStyles = 'py-[11px] flex-row items-center justify-center rounded-[13px]';
27+
const variantStyles = variant === 'filled' ? 'bg-blue-800' : 'bg-gray-50';
28+
const disabledStyles = disabled ? 'opacity-50' : '';
29+
30+
return tailwind.style(baseStyles, variantStyles, disabledStyles);
31+
};
32+
33+
const getTextStyles = () => {
34+
const baseStyles = 'ml-2 text-base font-medium';
35+
const colorStyles = variant === 'filled' ? 'text-white' : 'text-gray-950';
36+
37+
return tailwind.style(baseStyles, colorStyles);
38+
};
39+
40+
return (
41+
<Pressable style={[getButtonStyles(), style]} onPress={handlePress} disabled={disabled}>
42+
<Icon size={16} icon={icon} />
43+
<Animated.Text style={getTextStyles()}>{text}</Animated.Text>
44+
</Pressable>
45+
);
46+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './Button';
22
export * from './IconButton';
3+
export * from './AuthButton';

src/components-next/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './sheet-components';
66
export * from './spinner';
77
export * from './action-tabs';
88
export * from './no-network';
9+
export * from './verification-code';
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { StyleSheet } from 'react-native';
2+
import type { SharedValue } from 'react-native-reanimated';
3+
import Animated, {
4+
Easing,
5+
FadeIn,
6+
FadeOut,
7+
FlipInXDown,
8+
FlipOutXDown,
9+
useAnimatedStyle,
10+
withTiming,
11+
} from 'react-native-reanimated';
12+
13+
export type StatusType = 'inProgress' | 'correct' | 'wrong';
14+
15+
export type AnimatedCodeNumberProps = {
16+
code?: string;
17+
highlighted: boolean;
18+
status: SharedValue<StatusType>;
19+
};
20+
21+
export const AnimatedCodeNumber: React.FC<AnimatedCodeNumberProps> = ({
22+
code,
23+
highlighted,
24+
status,
25+
}) => {
26+
const correctColor = 'hsl(151, 40.2%, 54.1%)'; // green-600
27+
const defaultColor = 'hsl(0, 0%, 89.5%)'; // gray-300
28+
29+
const rBoxStyle = useAnimatedStyle(() => {
30+
return {
31+
// Only show green border for correct status, default border for all other states
32+
borderColor: withTiming(status.value === 'correct' ? correctColor : defaultColor),
33+
};
34+
}, [correctColor, defaultColor]);
35+
36+
return (
37+
<Animated.View style={[styles.container, rBoxStyle]}>
38+
{code != null && (
39+
<Animated.View entering={FadeIn.duration(250)} exiting={FadeOut.duration(250)}>
40+
<Animated.Text
41+
entering={FlipInXDown.duration(500)
42+
// Go to this website and you'll see the curve I used:
43+
// https://cubic-bezier.com/#0,0.75,0.5,0.9
44+
// Basically, I want the animation to start slow, then accelerate at the end
45+
// Do we really need to use a curve? Every detail matters :)
46+
.easing(Easing.bezier(0, 0.75, 0.5, 0.9).factory())
47+
.build()}
48+
exiting={FlipOutXDown.duration(500)
49+
// https://cubic-bezier.com/#0.6,0.1,0.4,0.8
50+
// I want the animation to start fast, then decelerate at the end (opposite of the previous one)
51+
.easing(Easing.bezier(0.6, 0.1, 0.4, 0.8).factory())
52+
.build()}
53+
style={[styles.text, { color: 'hsl(0, 0%, 39.3%)' }]}>
54+
{code}
55+
</Animated.Text>
56+
</Animated.View>
57+
)}
58+
</Animated.View>
59+
);
60+
};
61+
62+
const styles = StyleSheet.create({
63+
container: {
64+
height: '90%',
65+
width: '80%',
66+
borderWidth: 2,
67+
borderRadius: 12,
68+
borderCurve: 'continuous',
69+
justifyContent: 'center',
70+
alignItems: 'center',
71+
},
72+
text: {
73+
fontSize: 40,
74+
fontFamily: 'Inter-500-24',
75+
},
76+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useCallback } from 'react';
2+
import {
3+
cancelAnimation,
4+
Easing,
5+
useAnimatedStyle,
6+
useSharedValue,
7+
withRepeat,
8+
withTiming,
9+
} from 'react-native-reanimated';
10+
11+
// Custom hook for creating an animated shaking effect
12+
const useAnimatedShake = () => {
13+
// Shared value to track the horizontal translation for shaking
14+
const shakeTranslateX = useSharedValue(0);
15+
16+
// Callback function to trigger the shake animation
17+
const shake = useCallback(() => {
18+
// Cancel any ongoing animations on shakeTranslateX
19+
cancelAnimation(shakeTranslateX);
20+
21+
// Reset the translation value to 0 before starting the new animation
22+
shakeTranslateX.value = 0;
23+
24+
// Apply a repeating animation to create a shaking effect
25+
shakeTranslateX.value = withRepeat(
26+
withTiming(10, {
27+
duration: 120,
28+
easing: Easing.bezier(0.35, 0.7, 0.5, 0.7),
29+
}),
30+
6, // Repeat the animation 6 times
31+
true, // Infinite loop (true indicates indefinite repetition)
32+
);
33+
}, [shakeTranslateX]); // Dependency array to ensure proper re-rendering
34+
35+
// Define the animated style based on the current translation value
36+
const rShakeStyle = useAnimatedStyle(() => {
37+
return {
38+
transform: [{ translateX: shakeTranslateX.value }],
39+
};
40+
}, []);
41+
42+
// Return the shake callback function and the animated style for external use
43+
return { shake, rShakeStyle };
44+
};
45+
46+
// Export the custom hook for use in other components
47+
export { useAnimatedShake };

0 commit comments

Comments
 (0)