Swipe to dismiss notifications (#4606)
* Swipe to dismiss notifications * Release notes * Update VRT * Dummy commit * Make notification swipe a bit snappier --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -63,6 +63,7 @@
|
|||||||
"react-simple-pull-to-refresh": "^1.3.3",
|
"react-simple-pull-to-refresh": "^1.3.3",
|
||||||
"react-spring": "^9.7.3",
|
"react-spring": "^9.7.3",
|
||||||
"react-stately": "^3.33.0",
|
"react-stately": "^3.33.0",
|
||||||
|
"react-swipeable": "^7.0.2",
|
||||||
"react-virtualized-auto-sizer": "^1.0.21",
|
"react-virtualized-auto-sizer": "^1.0.21",
|
||||||
"recharts": "^2.10.4",
|
"recharts": "^2.10.4",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import React, {
|
|||||||
type CSSProperties,
|
type CSSProperties,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { animated, useSpring } from 'react-spring';
|
||||||
|
import { useSwipeable } from 'react-swipeable';
|
||||||
|
|
||||||
import { Button, ButtonWithLoading } from '@actual-app/components/button';
|
import { Button, ButtonWithLoading } from '@actual-app/components/button';
|
||||||
import { useResponsive } from '@actual-app/components/hooks/useResponsive';
|
import { useResponsive } from '@actual-app/components/hooks/useResponsive';
|
||||||
@@ -132,17 +134,51 @@ function Notification({
|
|||||||
? { minHeight: styles.mobileMinHeight }
|
? { minHeight: styles.mobileMinHeight }
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
|
const [isSwiped, setIsSwiped] = useState(false);
|
||||||
|
const [spring, api] = useSpring(() => ({ x: 0, opacity: 1 }));
|
||||||
|
|
||||||
|
const swipeHandlers = useSwipeable({
|
||||||
|
onSwiping: ({ deltaX }) => {
|
||||||
|
if (!isSwiped) {
|
||||||
|
api.start({ x: deltaX });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSwiped: ({ velocity, deltaX }) => {
|
||||||
|
// Distance to trigger deletion
|
||||||
|
const threshold = 100;
|
||||||
|
const direction = deltaX > 0 ? 1 : -1;
|
||||||
|
|
||||||
|
if (Math.abs(deltaX) > threshold || velocity > 0.5) {
|
||||||
|
// Animate out & remove item after animation
|
||||||
|
api.start({
|
||||||
|
x: direction * 1000,
|
||||||
|
opacity: 0,
|
||||||
|
onRest: onRemove,
|
||||||
|
});
|
||||||
|
setIsSwiped(true);
|
||||||
|
} else {
|
||||||
|
// Reset position if not swiped far enough
|
||||||
|
api.start({ x: 0 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trackMouse: true,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<animated.div
|
||||||
role="alert"
|
role="alert"
|
||||||
style={{
|
style={{
|
||||||
|
...spring,
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
color: positive
|
color: positive
|
||||||
? theme.noticeText
|
? theme.noticeText
|
||||||
: error
|
: error
|
||||||
? theme.errorTextDark
|
? theme.errorTextDark
|
||||||
: theme.warningTextDark,
|
: theme.warningTextDark,
|
||||||
|
// Prevents scrolling conflicts
|
||||||
|
touchAction: 'none',
|
||||||
}}
|
}}
|
||||||
|
{...swipeHandlers}
|
||||||
>
|
>
|
||||||
<Stack
|
<Stack
|
||||||
align="center"
|
align="center"
|
||||||
@@ -237,10 +273,10 @@ function Notification({
|
|||||||
<Button
|
<Button
|
||||||
variant="bare"
|
variant="bare"
|
||||||
aria-label={t('Close')}
|
aria-label={t('Close')}
|
||||||
style={{ flexShrink: 0, color: 'currentColor' }}
|
style={{ padding: 10, color: 'currentColor' }}
|
||||||
onPress={onRemove}
|
onPress={onRemove}
|
||||||
>
|
>
|
||||||
<SvgDelete style={{ width: 9, height: 9, color: 'currentColor' }} />
|
<SvgDelete style={{ width: 10, height: 10 }} />
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
{overlayLoading && (
|
{overlayLoading && (
|
||||||
@@ -261,7 +297,7 @@ function Notification({
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</animated.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
upcoming-release-notes/4606.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Enhancements
|
||||||
|
authors: [joel-jeremy]
|
||||||
|
---
|
||||||
|
|
||||||
|
Swipe left/right to dismiss notifications.
|
||||||
10
yarn.lock
@@ -198,6 +198,7 @@ __metadata:
|
|||||||
react-simple-pull-to-refresh: "npm:^1.3.3"
|
react-simple-pull-to-refresh: "npm:^1.3.3"
|
||||||
react-spring: "npm:^9.7.3"
|
react-spring: "npm:^9.7.3"
|
||||||
react-stately: "npm:^3.33.0"
|
react-stately: "npm:^3.33.0"
|
||||||
|
react-swipeable: "npm:^7.0.2"
|
||||||
react-virtualized-auto-sizer: "npm:^1.0.21"
|
react-virtualized-auto-sizer: "npm:^1.0.21"
|
||||||
recharts: "npm:^2.10.4"
|
recharts: "npm:^2.10.4"
|
||||||
remark-gfm: "npm:^3.0.1"
|
remark-gfm: "npm:^3.0.1"
|
||||||
@@ -20087,6 +20088,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"react-swipeable@npm:^7.0.2":
|
||||||
|
version: 7.0.2
|
||||||
|
resolution: "react-swipeable@npm:7.0.2"
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
checksum: 10/c142190244865c321751b0ca5f14ac186cf8a5d49bd577b16b1d22ea4f673d1e85cd7ad1962736be4575df320a6081c2b2b36799236c777b1a3ffb8682f4993c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"react-transition-group@npm:2.9.0":
|
"react-transition-group@npm:2.9.0":
|
||||||
version: 2.9.0
|
version: 2.9.0
|
||||||
resolution: "react-transition-group@npm:2.9.0"
|
resolution: "react-transition-group@npm:2.9.0"
|
||||||
|
|||||||