Skip to content

Commit 881d6d8

Browse files
Merge branch 'develop' into fix.db-writer-lock-deletemessage
2 parents 6c447f8 + 52f5bff commit 881d6d8

16 files changed

Lines changed: 993 additions & 761 deletions

File tree

.github/scripts/__tests__/validate-test-map.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ describe('validate-test-map', () => {
5252
expect(stdout).toContain('::error file=app/nonexistent-global.txt::Stale global');
5353
});
5454

55+
// runAllWhenChanged entries may be globs — sniffler matches them as patterns,
56+
// so an existsSync check would wrongly condemn every glob as stale.
57+
it('accepts a runAllWhenChanged glob that matches at least one file', () => {
58+
const { stdout } = runValidator('stale-global');
59+
expect(stdout).not.toContain('file=patches/**');
60+
});
61+
62+
it('flags a runAllWhenChanged glob that matches nothing on disk', () => {
63+
const { status, stdout } = runValidator('stale-global');
64+
expect(status).toBe(1);
65+
expect(stdout).toContain('::error file=native/**::Stale global');
66+
});
67+
5568
it('flags a decoupled gap: a saga file anchored in neither a dependsOn glob nor runAllWhenChanged', () => {
5669
const { status, stdout } = runValidator('decoupled-gap');
5770
expect(status).toBe(1);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"tests": {
3-
"runAllWhenChanged": ["app/nonexistent-global.txt"]
3+
"runAllWhenChanged": ["app/nonexistent-global.txt", "patches/**", "native/**"]
44
}
55
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
placeholder

.github/scripts/validate-test-map.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,13 @@ for (const dir of uncovered) {
7373
warnCount++;
7474
}
7575

76-
// Stale runAllWhenChanged paths: missing on disk.
76+
// Stale runAllWhenChanged paths: missing on disk. Entries may be globs (sniffler
77+
// matches them as patterns), so a glob is fresh when it matches at least one file.
7778
const runAll = config.tests?.runAllWhenChanged || [];
78-
const staleGlobals = runAll.filter(p => !fs.existsSync(path.join(ROOT, p)));
79+
const isGlob = p => /[*?[\]{}]/.test(p);
80+
const staleGlobals = runAll.filter(p =>
81+
isGlob(p) ? fg.sync(p, { cwd: ROOT, dot: true }).length === 0 : !fs.existsSync(path.join(ROOT, p))
82+
);
7983
for (const p of staleGlobals) {
8084
ann('error', p, `Stale global: "${p}" in runAllWhenChanged does not exist — will silently never fire.`);
8185
errorCount++;

.github/workflows/build-pr.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ jobs:
7777
if: ${{ github.repository == 'RocketChat/Rocket.Chat.ReactNative' }}
7878
permissions:
7979
contents: read
80+
pull-requests: read
8081
runs-on: ubuntu-latest
8182
outputs:
8283
shards: ${{ steps.select.outputs.shards }}
@@ -94,10 +95,26 @@ jobs:
9495
id: full
9596
run: bash .github/scripts/assert-maestro-shards.sh
9697

98+
# Read labels live rather than from the event payload: the payload is frozen
99+
# at push time (a label added afterwards, or a re-run, would never see it),
100+
# and `contains()` over an array is an exact element match — it would miss
101+
# the decorated label name ("💎 release").
102+
- name: Detect release label
103+
id: label
104+
env:
105+
GH_TOKEN: ${{ github.token }}
106+
PR_NUMBER: ${{ github.event.pull_request.number }}
107+
run: |
108+
if gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name' | grep -qi 'release'; then
109+
echo "release=true" >>"$GITHUB_OUTPUT"
110+
else
111+
echo "release=false" >>"$GITHUB_OUTPUT"
112+
fi
113+
97114
- name: Select impacted shards
98115
id: select
99116
env:
100-
IS_RELEASE_LANE: ${{ github.event.pull_request.base.ref == 'master' || contains(github.event.pull_request.labels.*.name, 'release') }}
117+
IS_RELEASE_LANE: ${{ github.event.pull_request.base.ref == 'master' || steps.label.outputs.release == 'true' }}
101118
BASE_REF: ${{ github.event.pull_request.base.ref }}
102119
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
103120
FULL_SHARDS: ${{ steps.full.outputs.shards }}

.sniffler/config.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@
88
"runAllWhenChanged": [
99
"pnpm-lock.yaml",
1010
"package.json",
11+
"index.js",
12+
"app.json",
13+
"babel.config.js",
14+
"metro.config.js",
15+
"react-native.config.js",
16+
"patches/**",
17+
"android/**",
18+
"ios/**",
19+
".maestro/**",
20+
".sniffler/**",
21+
".github/workflows/**",
22+
".github/scripts/**",
1123
"app/index.tsx",
1224
"app/lib/notifications/push.ts",
1325
"app/lib/notifications/index.ts",

app/containers/ServerItem/SwipeableDeleteItem/Actions.tsx

Lines changed: 77 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -19,123 +19,118 @@ import I18n from '../../../i18n';
1919
export interface IDeleteActionProps {
2020
transX: SharedValue<number>;
2121
width: number;
22-
rowHeight: number;
2322
actionWidth: number;
2423
longSwipe: number;
2524
onDeletePress(): void;
2625
testID?: string;
2726
}
2827

29-
const SERVER_ITEM_PADDING_VERTICAL = 12;
28+
export const DeleteAction = memo(({ transX, width, actionWidth, longSwipe, onDeletePress, testID }: IDeleteActionProps) => {
29+
const { colors } = useTheme();
3030

31-
export const DeleteAction = memo(
32-
({ transX, width, rowHeight, actionWidth, longSwipe, onDeletePress, testID }: IDeleteActionProps) => {
33-
const { colors } = useTheme();
31+
const translateXDelete = useSharedValue(0);
3432

35-
const translateXDelete = useSharedValue(0);
33+
const triggerDeleteAnimation = (toValue: number) => {
34+
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
35+
translateXDelete.value = withSpring(toValue, { overshootClamping: true, mass: 0.7 });
36+
};
3637

37-
const triggerDeleteAnimation = (toValue: number) => {
38-
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
39-
translateXDelete.value = withSpring(toValue, { overshootClamping: true, mass: 0.7 });
40-
};
41-
42-
useAnimatedReaction(
43-
() => transX.value,
44-
(currentTransX, previousTransX) => {
45-
if (I18n.isRTL) {
46-
if (previousTransX && currentTransX > longSwipe && previousTransX <= longSwipe) {
47-
scheduleOnRN(triggerDeleteAnimation, actionWidth);
48-
} else if (previousTransX && currentTransX <= longSwipe && previousTransX > longSwipe) {
49-
scheduleOnRN(triggerDeleteAnimation, 0);
50-
}
51-
} else if (previousTransX && currentTransX < -longSwipe && previousTransX >= -longSwipe) {
52-
scheduleOnRN(triggerDeleteAnimation, -actionWidth);
53-
} else if (previousTransX && currentTransX >= -longSwipe && previousTransX < -longSwipe) {
38+
useAnimatedReaction(
39+
() => transX.value,
40+
(currentTransX, previousTransX) => {
41+
if (I18n.isRTL) {
42+
if (previousTransX && currentTransX > longSwipe && previousTransX <= longSwipe) {
43+
scheduleOnRN(triggerDeleteAnimation, actionWidth);
44+
} else if (previousTransX && currentTransX <= longSwipe && previousTransX > longSwipe) {
5445
scheduleOnRN(triggerDeleteAnimation, 0);
5546
}
47+
} else if (previousTransX && currentTransX < -longSwipe && previousTransX >= -longSwipe) {
48+
scheduleOnRN(triggerDeleteAnimation, -actionWidth);
49+
} else if (previousTransX && currentTransX >= -longSwipe && previousTransX < -longSwipe) {
50+
scheduleOnRN(triggerDeleteAnimation, 0);
5651
}
57-
);
52+
}
53+
);
5854

59-
const animatedDeleteButtonStyles = useAnimatedStyle(() => {
60-
if (I18n.isRTL) {
61-
// RTL: delete button appears from the left when swiping right
62-
if (transX.value > longSwipe && transX.value >= 2 * actionWidth) {
63-
const parallaxSwipe = interpolate(
64-
transX.value,
65-
[2 * actionWidth, longSwipe],
66-
[-actionWidth, -actionWidth - 0.1 * transX.value]
67-
);
68-
return {
69-
transform: [{ translateX: parallaxSwipe - translateXDelete.value }],
70-
left: 0,
71-
right: undefined
72-
};
73-
}
74-
return {
75-
transform: [{ translateX: transX.value - actionWidth - translateXDelete.value }],
76-
left: 0,
77-
right: undefined
78-
};
79-
}
80-
// LTR: delete button appears from the right when swiping left
81-
if (transX.value < -longSwipe && transX.value <= -2 * actionWidth) {
55+
const animatedDeleteButtonStyles = useAnimatedStyle(() => {
56+
if (I18n.isRTL) {
57+
// RTL: delete button appears from the left when swiping right
58+
if (transX.value > longSwipe && transX.value >= 2 * actionWidth) {
8259
const parallaxSwipe = interpolate(
8360
transX.value,
84-
[-2 * actionWidth, -longSwipe],
85-
[actionWidth, actionWidth + 0.1 * transX.value]
61+
[2 * actionWidth, longSwipe],
62+
[-actionWidth, -actionWidth - 0.1 * transX.value]
8663
);
8764
return {
88-
transform: [{ translateX: parallaxSwipe + translateXDelete.value }],
89-
right: 0,
90-
left: undefined
65+
transform: [{ translateX: parallaxSwipe - translateXDelete.value }],
66+
left: 0,
67+
right: undefined
9168
};
9269
}
9370
return {
94-
transform: [{ translateX: transX.value + actionWidth + translateXDelete.value }],
71+
transform: [{ translateX: transX.value - actionWidth - translateXDelete.value }],
72+
left: 0,
73+
right: undefined
74+
};
75+
}
76+
// LTR: delete button appears from the right when swiping left
77+
if (transX.value < -longSwipe && transX.value <= -2 * actionWidth) {
78+
const parallaxSwipe = interpolate(
79+
transX.value,
80+
[-2 * actionWidth, -longSwipe],
81+
[actionWidth, actionWidth + 0.1 * transX.value]
82+
);
83+
return {
84+
transform: [{ translateX: parallaxSwipe + translateXDelete.value }],
9585
right: 0,
9686
left: undefined
9787
};
98-
});
99-
const viewHeight = { height: rowHeight + SERVER_ITEM_PADDING_VERTICAL };
100-
101-
return (
102-
<View
103-
style={[styles.actionsLeftContainer, viewHeight, { backgroundColor: colors.buttonBackgroundDangerDefault }]}
104-
pointerEvents='box-none'>
105-
<Animated.View
106-
style={[
107-
styles.actionRightButtonContainer,
108-
{
109-
width
110-
},
111-
viewHeight,
112-
animatedDeleteButtonStyles
113-
]}>
114-
<RectButton
115-
accessible
116-
accessibilityLabel={I18n.t('Delete')}
117-
testID={testID}
118-
style={[styles.actionButton, { backgroundColor: colors.buttonBackgroundDangerDefault }]}
119-
onPress={onDeletePress}>
120-
<CustomIcon size={24} name='delete' color={colors.fontWhite} />
121-
</RectButton>
122-
</Animated.View>
123-
</View>
124-
);
125-
}
126-
);
88+
}
89+
return {
90+
transform: [{ translateX: transX.value + actionWidth + translateXDelete.value }],
91+
right: 0,
92+
left: undefined
93+
};
94+
});
95+
return (
96+
<View
97+
style={[styles.actionsLeftContainer, { backgroundColor: colors.buttonBackgroundDangerDefault }]}
98+
pointerEvents='box-none'>
99+
<Animated.View
100+
style={[
101+
styles.actionRightButtonContainer,
102+
{
103+
width
104+
},
105+
animatedDeleteButtonStyles
106+
]}>
107+
<RectButton
108+
accessible
109+
accessibilityLabel={I18n.t('Delete')}
110+
testID={testID}
111+
style={[styles.actionButton, { backgroundColor: colors.buttonBackgroundDangerDefault }]}
112+
onPress={onDeletePress}>
113+
<CustomIcon size={24} name='delete' color={colors.fontWhite} />
114+
</RectButton>
115+
</Animated.View>
116+
</View>
117+
);
118+
});
127119

128120
const styles = StyleSheet.create({
129121
actionsLeftContainer: {
130122
flexDirection: 'row',
131123
position: 'absolute',
124+
top: 0,
125+
bottom: 0,
132126
left: 0,
133127
right: 0
134128
},
135129
actionRightButtonContainer: {
136130
position: 'absolute',
137131
justifyContent: 'center',
138132
top: 0,
133+
bottom: 0,
139134
alignItems: 'flex-end'
140135
},
141136
actionButton: {

app/containers/ServerItem/SwipeableDeleteItem/Touchable.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ export interface ISwipeableDeleteTouchableProps {
1818
children: ReactElement;
1919
testID: string;
2020
width: number;
21-
rowHeight: number;
2221
actionWidth: number;
2322
longSwipe: number;
2423
smallSwipe: number;
@@ -33,7 +32,6 @@ const SwipeableDeleteTouchable = ({
3332
width,
3433
children,
3534
testID,
36-
rowHeight,
3735
actionWidth,
3836
longSwipe,
3937
smallSwipe,
@@ -188,7 +186,6 @@ const SwipeableDeleteTouchable = ({
188186
<DeleteAction
189187
width={width}
190188
transX={transX}
191-
rowHeight={rowHeight}
192189
actionWidth={actionWidth}
193190
longSwipe={longSwipe}
194191
onDeletePress={handleDeletePress}

app/containers/ServerItem/Touchable.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { type AccessibilityRole } from 'react-native';
33

44
import SwipeableDeleteTouchable from './SwipeableDeleteItem/Touchable';
55
import Touch from '../Touch';
6-
import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE, ROW_HEIGHT } from './styles';
6+
import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE } from './styles';
77
import { useTheme } from '../../theme';
88

99
export interface IServerItemTouchableProps {
@@ -34,7 +34,6 @@ const Touchable = ({
3434
<SwipeableDeleteTouchable
3535
width={width}
3636
testID={testID}
37-
rowHeight={ROW_HEIGHT}
3837
actionWidth={ACTION_WIDTH}
3938
longSwipe={LONG_SWIPE}
4039
smallSwipe={SMALL_SWIPE}

0 commit comments

Comments
 (0)