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-spring": "^9.7.3",
|
||||
"react-stately": "^3.33.0",
|
||||
"react-swipeable": "^7.0.2",
|
||||
"react-virtualized-auto-sizer": "^1.0.21",
|
||||
"recharts": "^2.10.4",
|
||||
"remark-gfm": "^3.0.1",
|
||||
|
||||
@@ -7,6 +7,8 @@ import React, {
|
||||
type CSSProperties,
|
||||
} from 'react';
|
||||
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 { useResponsive } from '@actual-app/components/hooks/useResponsive';
|
||||
@@ -132,17 +134,51 @@ function Notification({
|
||||
? { 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 (
|
||||
<View
|
||||
<animated.div
|
||||
role="alert"
|
||||
style={{
|
||||
...spring,
|
||||
marginTop: 10,
|
||||
color: positive
|
||||
? theme.noticeText
|
||||
: error
|
||||
? theme.errorTextDark
|
||||
: theme.warningTextDark,
|
||||
// Prevents scrolling conflicts
|
||||
touchAction: 'none',
|
||||
}}
|
||||
{...swipeHandlers}
|
||||
>
|
||||
<Stack
|
||||
align="center"
|
||||
@@ -237,10 +273,10 @@ function Notification({
|
||||
<Button
|
||||
variant="bare"
|
||||
aria-label={t('Close')}
|
||||
style={{ flexShrink: 0, color: 'currentColor' }}
|
||||
style={{ padding: 10, color: 'currentColor' }}
|
||||
onPress={onRemove}
|
||||
>
|
||||
<SvgDelete style={{ width: 9, height: 9, color: 'currentColor' }} />
|
||||
<SvgDelete style={{ width: 10, height: 10 }} />
|
||||
</Button>
|
||||
</Stack>
|
||||
{overlayLoading && (
|
||||
@@ -261,7 +297,7 @@ function Notification({
|
||||
/>
|
||||
</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-spring: "npm:^9.7.3"
|
||||
react-stately: "npm:^3.33.0"
|
||||
react-swipeable: "npm:^7.0.2"
|
||||
react-virtualized-auto-sizer: "npm:^1.0.21"
|
||||
recharts: "npm:^2.10.4"
|
||||
remark-gfm: "npm:^3.0.1"
|
||||
@@ -20087,6 +20088,15 @@ __metadata:
|
||||
languageName: node
|
||||
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":
|
||||
version: 2.9.0
|
||||
resolution: "react-transition-group@npm:2.9.0"
|
||||
|
||||